diff --git a/Content.Server/Administration/Commands/ImportNetsetCommand.cs b/Content.Server/Administration/Commands/ImportNetsetCommand.cs new file mode 100644 index 0000000000..fa60346f89 --- /dev/null +++ b/Content.Server/Administration/Commands/ImportNetsetCommand.cs @@ -0,0 +1,116 @@ +using System.Net; +using Content.Server.Database; +using Content.Shared.Administration; +using Robust.Shared.Console; +using Robust.Shared.ContentPack; +using Robust.Shared.Network; +using Robust.Shared.Utility; + +namespace Content.Server.Administration.Commands; + +[AdminCommand(AdminFlags.Host)] +public sealed class ImportNetsetCommand : IConsoleCommand +{ + [Dependency] private readonly IResourceManager _resourceManager = default!; + [Dependency] private readonly IServerDbManager _serverDbManager = default!; + + public static NetUserId AutomationBanningUser = new(new Guid("a17aa146-807c-48bb-8573-65e3cb05119d")); + + public string Command => "importnetset"; + public string Description => "Import a .netset file into the ban database."; + public string Help => "importnetset \"\""; + public async void Execute(IConsoleShell shell, string argStr, string[] args) + { + if (args.Length != 2) + { + shell.WriteError(Loc.GetString("shell-wrong-arguments-number")); + return; + } + + var stream = _resourceManager.ContentFileReadText(new ResourcePath(args[0]).ToRootedPath()); + + var bansToAdd = new List(); + var line = await stream.ReadLineAsync(); + while (line != null) + { + if (line.StartsWith("#")) // That's a comment, skip it. + { + line = await stream.ReadLineAsync(); + continue; + } + + if (bansToAdd.Count % 100 == 0) + shell.WriteLine($"Got {bansToAdd.Count} bans so far..."); + + var ipMaskSplit = line.Split("/"); + + if (ipMaskSplit.Length > 2) + { + shell.WriteError($"Failed to import address {line}"); + return; + } + + if (!IPAddress.TryParse(ipMaskSplit[0], out var ip)) + { + shell.WriteError($"Failed to import address {line}"); + return; + } + + var cidr = 32; + if (ipMaskSplit.Length == 2) + { + if ( !int.TryParse(ipMaskSplit[1], out cidr)) + { + shell.WriteError($"Failed to import address {line}"); + return; + } + } + + var possibleCollisions = await _serverDbManager.GetServerBansAsync(ip, null, null, false); + var collides = false; + foreach (var ban in possibleCollisions) + { + if (ban.UserId != null || ban.ExpirationTime != null || ban.HWId == null) + continue; + + if (ban.Address == null) // no clue how we'd get here but best be safe. + continue; + + if (ban.Address.Value.cidrMask > cidr) + continue; + + collides = true; + shell.WriteError($"The imported address {ip}/{cidr} collides with {ban.Address.Value.address}/{ban.Address.Value.cidrMask}, ban {ban.Id}. Add it manually."); + } + + if (collides) + { + line = await stream.ReadLineAsync(); + continue; // not gonna add a ban that collides. + } + + var def = new ServerBanDef( + null, + null, + (ip, cidr), + null, + DateTimeOffset.UnixEpoch, + null, + args[1], + AutomationBanningUser, + null); + + bansToAdd.Add(def); + + line = await stream.ReadLineAsync(); + } + + shell.WriteLine($"Importing {bansToAdd.Count} bans"); + + foreach (var ban in bansToAdd) + { + await _serverDbManager.AddServerBanAsync(ban); + } + shell.WriteLine("Import complete!"); + } +}