/// Intended use is for users with shared connections. This should not be used as an alternative to <see cref="Datacenter"/>.
/// </remarks>
IP = 1 << 1,
+
+ /// <summary>
+ /// Ban is an IP range that is only applied for first time joins.
+ /// </summary>
+ /// <remarks>
+ /// Intended for use with residential IP ranges that are often used maliciously.
+ /// </remarks>
+ BlacklistedRange = 1 << 2,
// @formatter:on
}
return record == null ? null : MakePlayerRecord(record);
}
+ protected async Task<bool> PlayerRecordExists(DbGuard db, NetUserId userId)
+ {
+ return await db.DbContext.Player.AnyAsync(p => p.UserId == userId);
+ }
+
[return: NotNullIfNotNull(nameof(player))]
protected PlayerRecord? MakePlayerRecord(Player? player)
{
await using var db = await GetDbImpl();
var exempt = await GetBanExemptionCore(db, userId);
- var query = MakeBanLookupQuery(address, userId, hwId, db, includeUnbanned: false, exempt)
+ var newPlayer = userId == null || !await PlayerRecordExists(db, userId.Value);
+ var query = MakeBanLookupQuery(address, userId, hwId, db, includeUnbanned: false, exempt, newPlayer)
.OrderByDescending(b => b.BanTime);
var ban = await query.FirstOrDefaultAsync();
await using var db = await GetDbImpl();
var exempt = await GetBanExemptionCore(db, userId);
- var query = MakeBanLookupQuery(address, userId, hwId, db, includeUnbanned, exempt);
+ var newPlayer = !await db.PgDbContext.Player.AnyAsync(p => p.UserId == userId);
+ var query = MakeBanLookupQuery(address, userId, hwId, db, includeUnbanned, exempt, newPlayer);
var queryBans = await query.ToArrayAsync();
var bans = new List<ServerBanDef>(queryBans.Length);
ImmutableArray<byte>? hwId,
DbGuardImpl db,
bool includeUnbanned,
- ServerBanExemptFlags? exemptFlags)
+ ServerBanExemptFlags? exemptFlags,
+ bool newPlayer)
{
DebugTools.Assert(!(address == null && userId == null && hwId == null));
{
var newQ = db.PgDbContext.Ban
.Include(p => p.Unban)
- .Where(b => b.Address != null && EF.Functions.ContainsOrEqual(b.Address.Value, address));
+ .Where(b => b.Address != null
+ && EF.Functions.ContainsOrEqual(b.Address.Value, address)
+ && !(b.ExemptFlags.HasFlag(ServerBanExemptFlags.BlacklistedRange) && !newPlayer));
query = query == null ? newQ : query.Union(newQ);
}
if (exemptFlags is { } exempt)
{
+ if (exempt != ServerBanExemptFlags.None)
+ exempt |= ServerBanExemptFlags.BlacklistedRange; // Any kind of exemption should bypass BlacklistedRange
+
query = query.Where(b => (b.ExemptFlags & exempt) == 0);
}
var exempt = await GetBanExemptionCore(db, userId);
+ var newPlayer = userId == null || !await PlayerRecordExists(db, userId.Value);
+
// SQLite can't do the net masking stuff we need to match IP address ranges.
// So just pull down the whole list into memory.
var bans = await GetAllBans(db.SqliteDbContext, includeUnbanned: false, exempt);
- return bans.FirstOrDefault(b => BanMatches(b, address, userId, hwId, exempt)) is { } foundBan
+ return bans.FirstOrDefault(b => BanMatches(b, address, userId, hwId, exempt, newPlayer)) is { } foundBan
? ConvertBan(foundBan)
: null;
}
var exempt = await GetBanExemptionCore(db, userId);
+ var newPlayer = !await db.SqliteDbContext.Player.AnyAsync(p => p.UserId == userId);
+
// SQLite can't do the net masking stuff we need to match IP address ranges.
// So just pull down the whole list into memory.
var queryBans = await GetAllBans(db.SqliteDbContext, includeUnbanned, exempt);
return queryBans
- .Where(b => BanMatches(b, address, userId, hwId, exempt))
+ .Where(b => BanMatches(b, address, userId, hwId, exempt, newPlayer))
.Select(ConvertBan)
.ToList()!;
}
IPAddress? address,
NetUserId? userId,
ImmutableArray<byte>? hwId,
- ServerBanExemptFlags? exemptFlags)
+ ServerBanExemptFlags? exemptFlags,
+ bool newPlayer)
{
+ // Any flag to bypass BlacklistedRange bans.
+ var exemptFromBlacklistedRange = exemptFlags != null && exemptFlags.Value != ServerBanExemptFlags.None;
+
if (!exemptFlags.GetValueOrDefault(ServerBanExemptFlags.None).HasFlag(ServerBanExemptFlags.IP)
- && address != null && ban.Address is not null && address.IsInSubnet(ban.Address.ToTuple().Value))
+ && address != null
+ && ban.Address is not null
+ && address.IsInSubnet(ban.Address.ToTuple().Value)
+ && (!ban.ExemptFlags.HasFlag(ServerBanExemptFlags.BlacklistedRange) ||
+ newPlayer && !exemptFromBlacklistedRange))
{
return true;
}