diff --git a/.vs/Subsurface_Solution/v14/.suo b/.vs/Subsurface_Solution/v14/.suo index 188fc21d5..f87552f79 100644 Binary files a/.vs/Subsurface_Solution/v14/.suo and b/.vs/Subsurface_Solution/v14/.suo differ diff --git a/Subsurface/Barotrauma.csproj b/Subsurface/Barotrauma.csproj index 7840f615f..223362a38 100644 --- a/Subsurface/Barotrauma.csproj +++ b/Subsurface/Barotrauma.csproj @@ -1,52 +1,52 @@ - - - - Debug - x86 - 8.0.30703 - 2.0 - {008C0F83-E914-4966-9135-EA885059EDD8} - WinExe - Properties - Barotrauma - Barotrauma - 512 - false - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 0.1.0.%2a - false - true - v4.5 - - - - x86 - true - full - false - bin\Windows\Debug\ - DEBUG;TRACE;WINDOWS - prompt - 4 - false - - - x86 - pdbonly - true - bin\Windows\Release\ - TRACE;WINDOWS - + + + + Debug + x86 + 8.0.30703 + 2.0 + {008C0F83-E914-4966-9135-EA885059EDD8} + WinExe + Properties + Barotrauma + Barotrauma + 512 + false + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 0.1.0.%2a + false + true + v4.5 + + + + x86 + true + full + false + bin\Windows\Debug\ + DEBUG;TRACE;WINDOWS + prompt + 4 + false + + + x86 + pdbonly + true + bin\Windows\Release\ + TRACE;WINDOWS + @@ -1455,5 +1455,5 @@ - --> + --> \ No newline at end of file diff --git a/Subsurface/Source/GameSession/GameSession.cs b/Subsurface/Source/GameSession/GameSession.cs index beb6e15eb..31be54638 100644 --- a/Subsurface/Source/GameSession/GameSession.cs +++ b/Subsurface/Source/GameSession/GameSession.cs @@ -244,7 +244,7 @@ namespace Barotrauma missionButton.UserData = InfoFrameTab.Mission; missionButton.OnClicked = SelectInfoFrameTab; - if (GameMain.Server != null) + if (GameMain.Server != null) { var manageButton = new GUIButton(new Rectangle(200, -30, 130, 20), "Manage players", GUI.Style, infoFrame); manageButton.UserData = InfoFrameTab.ManagePlayers; @@ -269,7 +269,7 @@ namespace Barotrauma break; case InfoFrameTab.Mission: CreateMissionInfo(infoFrame); - break; + break; case InfoFrameTab.ManagePlayers: GameMain.Server.ManagePlayersFrame(infoFrame); break; diff --git a/Subsurface/Source/Networking/BanList.cs b/Subsurface/Source/Networking/BanList.cs index f9960dbd1..0b80fb717 100644 --- a/Subsurface/Source/Networking/BanList.cs +++ b/Subsurface/Source/Networking/BanList.cs @@ -12,6 +12,20 @@ namespace Barotrauma.Networking public string Name; public string IP; + public bool CompareTo(string ipCompare) + { + int rangeBanIndex = IP.IndexOf(".x"); + if (rangeBanIndex<=-1) + { + return ipCompare == IP; + } + else + { + if (ipCompare.Length < rangeBanIndex) return false; + return ipCompare.Substring(0, rangeBanIndex) == IP.Substring(0, rangeBanIndex); + } + } + public BannedPlayer(string name, string ip) { this.Name = name; @@ -74,7 +88,7 @@ namespace Barotrauma.Networking public bool IsBanned(string IP) { - return bannedPlayers.Any(bp => bp.IP == IP); + return bannedPlayers.Any(bp => bp.CompareTo(IP)); } public GUIComponent CreateBanFrame(GUIComponent parent) @@ -94,6 +108,12 @@ namespace Barotrauma.Networking var removeButton = new GUIButton(new Rectangle(0, 0, 100, 20), "Remove", Alignment.Right | Alignment.CenterY, GUI.Style, textBlock); removeButton.UserData = bannedPlayer; removeButton.OnClicked = RemoveBan; + if (bannedPlayer.IP.IndexOf(".x") <= -1) + { + var rangeBanButton = new GUIButton(new Rectangle(-100, 0, 100, 20), "Ban range", Alignment.Right | Alignment.CenterY, GUI.Style, textBlock); + rangeBanButton.UserData = bannedPlayer; + rangeBanButton.OnClicked = RangeBan; + } } return banFrame; @@ -120,6 +140,46 @@ namespace Barotrauma.Networking return true; } + public string ToRange(string ip) + { + for (int i = ip.Length - 1; i > 0; i--) + { + if (ip[i] == '.') + { + ip = ip.Substring(0, i) + ".x"; + break; + } + } + return ip; + } + + private bool RangeBan(GUIButton button, object obj) + { + BannedPlayer banned = obj as BannedPlayer; + if (banned == null) return false; + + banned.IP = ToRange(banned.IP); + + BannedPlayer bp; + while ((bp = bannedPlayers.Find(x => banned.CompareTo(x.IP)))!=null) + { + //remove all specific bans that are now covered by the rangeban + bannedPlayers.Remove(bp); + } + + bannedPlayers.Add(banned); + + Save(); + + if (banFrame != null) + { + banFrame.Parent.RemoveChild(banFrame); + CreateBanFrame(banFrame.Parent); + } + + return true; + } + private bool CloseFrame(GUIButton button, object obj) { banFrame = null; diff --git a/Subsurface/Source/Networking/GameClient.cs b/Subsurface/Source/Networking/GameClient.cs index 22faf005a..a415b93f1 100644 --- a/Subsurface/Source/Networking/GameClient.cs +++ b/Subsurface/Source/Networking/GameClient.cs @@ -1025,7 +1025,7 @@ namespace Barotrauma.Networking client.SendMessage(msg, NetDeliveryMethod.ReliableUnordered); } - public override void KickPlayer(string kickedName, bool ban) + public override void KickPlayer(string kickedName, bool ban, bool range = false) { if (!permissions.HasFlag(ClientPermissions.Kick) && !ban) return; if (!permissions.HasFlag(ClientPermissions.Ban) && ban) return; diff --git a/Subsurface/Source/Networking/GameServer.cs b/Subsurface/Source/Networking/GameServer.cs index 8e4898810..6cfa964d1 100644 --- a/Subsurface/Source/Networking/GameServer.cs +++ b/Subsurface/Source/Networking/GameServer.cs @@ -114,6 +114,12 @@ namespace Barotrauma.Networking settingsButton.OnClicked = ToggleSettingsFrame; settingsButton.UserData = "settingsButton"; + whitelist = new WhiteList(); + + GUIButton whitelistButton = new GUIButton(new Rectangle(GameMain.GraphicsWidth - 170 - 170 - 170 - 170, 20, 150, 20), "Whitelist", Alignment.TopLeft, GUI.Style, inGameHUD); + whitelistButton.OnClicked = ToggleWhiteListFrame; + whitelistButton.UserData = "whitelistButton"; + banList = new BanList(); LoadSettings(); @@ -282,6 +288,7 @@ namespace Barotrauma.Networking { if (ShowNetStats) netStats.Update(deltaTime); if (settingsFrame != null) settingsFrame.Update(deltaTime); + if (whitelist.WhiteListFrame != null) whitelist.WhiteListFrame.Update(deltaTime); if (!started) return; @@ -477,6 +484,11 @@ namespace Barotrauma.Networking } break; case NetIncomingMessageType.Data: + if (banList.IsBanned(inc.SenderEndPoint.Address.ToString())) + { + inc.SenderConnection.Disconnect("You have been banned from the server"); + return; + } byte packetType = inc.ReadByte(); @@ -1172,7 +1184,7 @@ namespace Barotrauma.Networking //if (GameMain.GameSession!=null) GameMain.GameSession.CrewManager.CreateCrewFrame(crew); } - public override void KickPlayer(string playerName, bool ban) + public override void KickPlayer(string playerName, bool ban, bool range = false) { playerName = playerName.ToLowerInvariant(); @@ -1180,17 +1192,19 @@ namespace Barotrauma.Networking c.name.ToLowerInvariant() == playerName || (c.Character != null && c.Character.Name.ToLowerInvariant() == playerName)); - KickClient(client, ban); + KickClient(client, ban, range); } - public void KickClient(Client client, bool ban = false) + public void KickClient(Client client, bool ban = false, bool range = false) { if (client == null) return; if (ban) { DisconnectClient(client, client.name + " has been banned from the server", "You have been banned from the server"); - banList.BanPlayer(client.name, client.Connection.RemoteEndPoint.Address.ToString()); + string ip = client.Connection.RemoteEndPoint.Address.ToString(); + if (range) { ip = banList.ToRange(ip); } + banList.BanPlayer(client.name, ip); } else { @@ -1291,6 +1305,10 @@ namespace Barotrauma.Networking log.LogFrame.Update(0.016f); log.LogFrame.Draw(spriteBatch); } + else if (whitelist.WhiteListFrame != null) + { + whitelist.WhiteListFrame.Draw(spriteBatch); + } if (!ShowNetStats) return; @@ -1431,6 +1449,10 @@ namespace Barotrauma.Networking banButton.UserData = character.Name; banButton.OnClicked += GameMain.NetLobbyScreen.BanPlayer; + var rangebanButton = new GUIButton(new Rectangle(0, -25, 100, 20), "Ban range", Alignment.BottomRight, GUI.Style, characterFrame); + rangebanButton.UserData = character.Name; + rangebanButton.OnClicked += GameMain.NetLobbyScreen.BanPlayerRange; + var kickButton = new GUIButton(new Rectangle(0, 0, 100, 20), "Kick", Alignment.BottomLeft, GUI.Style, characterFrame); kickButton.UserData = character.Name; kickButton.OnClicked += GameMain.NetLobbyScreen.KickPlayer; diff --git a/Subsurface/Source/Networking/GameServerLogin.cs b/Subsurface/Source/Networking/GameServerLogin.cs index ff617de88..762281a01 100644 --- a/Subsurface/Source/Networking/GameServerLogin.cs +++ b/Subsurface/Source/Networking/GameServerLogin.cs @@ -45,7 +45,7 @@ namespace Barotrauma.Networking inc.SenderConnection.Deny("Connection error - already joined"); return; } - + int nonce = CryptoRandom.Instance.Next(); var msg = server.CreateMessage(); msg.Write(nonce); @@ -57,6 +57,7 @@ namespace Barotrauma.Networking private void CheckAuthentication(NetIncomingMessage inc) { + string whitelistPw = ""; var unauthenticatedClient = unauthenticatedClients.Find(uc => uc.Connection == inc.SenderConnection); if (unauthenticatedClient != null) { @@ -69,10 +70,26 @@ namespace Barotrauma.Networking inc.Decrypt(algo); string rdPw = inc.ReadString(); - if (rdPw != saltedPw) + if (!whitelist.enabled) { - inc.SenderConnection.Disconnect("Wrong password!"); - return; + if (rdPw != saltedPw) + { + inc.SenderConnection.Disconnect("Wrong password!"); + return; + } + } + else + { + WhiteListedPlayer wlp = whitelist.WhiteListedPlayers.Find(x => x.GetHashedPassword(unauthenticatedClient.Nonce) == saltedPw); + if (wlp==null) + { + inc.SenderConnection.Disconnect("Wrong password or name!"); + return; + } + else + { + whitelistPw = wlp.Password; + } } } else diff --git a/Subsurface/Source/Networking/GameServerSettings.cs b/Subsurface/Source/Networking/GameServerSettings.cs index fe6161671..0da9f9202 100644 --- a/Subsurface/Source/Networking/GameServerSettings.cs +++ b/Subsurface/Source/Networking/GameServerSettings.cs @@ -55,6 +55,7 @@ namespace Barotrauma.Networking private bool registeredToMaster; + private WhiteList whitelist; private BanList banList; private string password; @@ -257,7 +258,7 @@ namespace Barotrauma.Networking private void CreateSettingsFrame() { - settingsFrame = new GUIFrame(new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), Color.Black * 0.5f); + settingsFrame = new GUIFrame(new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), Color.Black * 0.5f); GUIFrame innerFrame = new GUIFrame(new Rectangle(0, 0, 400, 430), null, Alignment.Center, GUI.Style, settingsFrame); innerFrame.Padding = new Vector4(20.0f, 20.0f, 20.0f, 20.0f); @@ -647,6 +648,20 @@ namespace Barotrauma.Networking return false; } + public bool ToggleWhiteListFrame(GUIButton button, object obj) + { + if (whitelist.WhiteListFrame == null) + { + whitelist.CreateWhiteListFrame(); + } + else + { + whitelist.CloseFrame(); + } + + return false; + } + public void ManagePlayersFrame(GUIFrame infoFrame) { GUIListBox cList = new GUIListBox(new Rectangle(0, 0, 0, 300), Color.White * 0.7f, GUI.Style, infoFrame); @@ -668,10 +683,14 @@ namespace Barotrauma.Networking Alignment.Left, Alignment.Left, null, frame); - var banButton = new GUIButton(new Rectangle(-120, 0, 100, 20), "Ban", Alignment.Right | Alignment.CenterY, GUI.Style, frame); + var banButton = new GUIButton(new Rectangle(-110, 0, 100, 20), "Ban", Alignment.Right | Alignment.CenterY, GUI.Style, frame); banButton.UserData = c.name; banButton.OnClicked += GameMain.NetLobbyScreen.BanPlayer; + var rangebanButton = new GUIButton(new Rectangle(-220, 0, 100, 20), "Ban range", Alignment.Right | Alignment.CenterY, GUI.Style, frame); + rangebanButton.UserData = c.name; + rangebanButton.OnClicked += GameMain.NetLobbyScreen.BanPlayerRange; + var kickButton = new GUIButton(new Rectangle(0, 0, 100, 20), "Kick", Alignment.Right | Alignment.CenterY, GUI.Style, frame); kickButton.UserData = c.name; kickButton.OnClicked += GameMain.NetLobbyScreen.KickPlayer; diff --git a/Subsurface/Source/Networking/NetworkMember.cs b/Subsurface/Source/Networking/NetworkMember.cs index d18587249..c75ee1094 100644 --- a/Subsurface/Source/Networking/NetworkMember.cs +++ b/Subsurface/Source/Networking/NetworkMember.cs @@ -336,7 +336,7 @@ namespace Barotrauma.Networking public virtual void SendChatMessage(string message, ChatMessageType? type = null) { } - public virtual void KickPlayer(string kickedName, bool ban) { } + public virtual void KickPlayer(string kickedName, bool ban, bool range = false) { } public virtual void Update(float deltaTime) { diff --git a/Subsurface/Source/Networking/WhiteList.cs b/Subsurface/Source/Networking/WhiteList.cs new file mode 100644 index 000000000..ad12adf56 --- /dev/null +++ b/Subsurface/Source/Networking/WhiteList.cs @@ -0,0 +1,155 @@ +using Microsoft.Xna.Framework; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace Barotrauma.Networking +{ + class WhiteListedPlayer + { + public string Name; + public string Password; + public string IP; + + public WhiteListedPlayer(string name,string password,string ip) + { + Name = name; + Password = password; + IP = ip; + } + + public string GetHashedPassword(int nonce) + { + string saltedPw = Password; + saltedPw = saltedPw + Convert.ToString(nonce); + saltedPw = Encoding.UTF8.GetString(Lidgren.Network.NetUtility.ComputeSHAHash(Encoding.UTF8.GetBytes(saltedPw))); + return saltedPw; + } + } + + class WhiteList + { + private List whitelistedPlayers; + public List WhiteListedPlayers + { + get { return whitelistedPlayers; } + } + + private GUIComponent whitelistFrame; + private GUIComponent innerlistFrame; + + private GUITextBox nameBox; + private GUITextBox ipBox; + private GUITextBox pwBox; + + public bool enabled; + + public GUIComponent WhiteListFrame + { + get { return whitelistFrame; } + } + + public WhiteList() + { + enabled = false; + whitelistedPlayers = new List(); + } + + public bool IsWhiteListed(string name, string password, string ip) + { + if (!enabled) return true; + WhiteListedPlayer wlp = whitelistedPlayers.Find(p => p.Name == name); + if (wlp == null) return false; + if (wlp.Password != password && !string.IsNullOrWhiteSpace(wlp.Password)) return false; + if (wlp.IP != ip && !string.IsNullOrWhiteSpace(wlp.IP)) return false; + return true; + } + + public GUIComponent CreateWhiteListFrame() + { + whitelistFrame = new GUIFrame(new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), Color.Black * 0.5f); + + GUIFrame innerFrame = new GUIFrame(new Rectangle(0, 0, 500, 460), null, Alignment.Center, GUI.Style, whitelistFrame); + innerFrame.Padding = new Vector4(20.0f, 50.0f, 20.0f, 130.0f); + + var closeButton = new GUIButton(new Rectangle(0, 115, 100, 20), "Close", Alignment.BottomRight, GUI.Style, innerFrame); + closeButton.OnClicked = GameMain.Server.ToggleWhiteListFrame; + + new GUITextBlock(new Rectangle(0, -35, 200, 20), "Whitelist", GUI.Style, innerFrame, GUI.LargeFont); + var enabledTick = new GUITickBox(new Rectangle(200, -30, 20, 20), "Enabled", Alignment.Left, innerFrame); + enabledTick.Selected = enabled; + enabledTick.OnSelected = (GUITickBox box) => + { + enabled = !enabled; + return true; + }; + + new GUITextBlock(new Rectangle(0, 35, 90, 25), "Name:", GUI.Style, Alignment.BottomLeft, Alignment.TopLeft, innerFrame, false, GUI.Font); + nameBox = new GUITextBox(new Rectangle(100, 30, 170, 25), Alignment.BottomLeft, GUI.Style, innerFrame); + nameBox.Font = GUI.Font; + + new GUITextBlock(new Rectangle(0, 65, 90, 25), "Password:", GUI.Style, Alignment.BottomLeft, Alignment.TopLeft, innerFrame, false, GUI.Font); + pwBox = new GUITextBox(new Rectangle(100, 60, 170, 25), Alignment.BottomLeft, GUI.Style, innerFrame); + pwBox.Font = GUI.Font; + + new GUITextBlock(new Rectangle(0, 95, 90, 25), "IP Address:", GUI.Style, Alignment.BottomLeft, Alignment.TopLeft, innerFrame, false, GUI.Font); + ipBox = new GUITextBox(new Rectangle(100, 90, 170, 25), Alignment.BottomLeft, GUI.Style, innerFrame); + ipBox.Font = GUI.Font; + + var addnewButton = new GUIButton(new Rectangle(300, 55, 150, 20), "Add to whitelist", Alignment.BottomLeft, GUI.Style, innerFrame); + addnewButton.OnClicked = AddToWhiteList; + + innerlistFrame = new GUIListBox(new Rectangle(0, 0, 0, 0), GUI.Style, innerFrame); + + foreach (WhiteListedPlayer wlp in whitelistedPlayers) + { + string blockText = wlp.Name; + if (!string.IsNullOrWhiteSpace(wlp.IP)) blockText += " (" + wlp.IP + ")"; + GUITextBlock textBlock = new GUITextBlock( + new Rectangle(0, 0, 0, 25), + blockText, + GUI.Style, + Alignment.Left, Alignment.Left, innerlistFrame); + textBlock.Padding = new Vector4(10.0f, 10.0f, 0.0f, 0.0f); + textBlock.UserData = wlp; + + var removeButton = new GUIButton(new Rectangle(0, 0, 100, 20), "Remove", Alignment.Right | Alignment.CenterY, GUI.Style, textBlock); + removeButton.UserData = wlp; + removeButton.OnClicked = RemoveFromWhiteList; + } + + return whitelistFrame; + } + + private bool RemoveFromWhiteList(GUIButton button, object obj) + { + WhiteListedPlayer wlp = obj as WhiteListedPlayer; + if (wlp == null) return false; + + DebugConsole.Log("Removing " + wlp.Name + " from whitelist"); + GameServer.Log("Removing " + wlp.Name + " from whitelist", null); + + whitelistedPlayers.Remove(wlp); + CloseFrame(); CreateWhiteListFrame(); + + return true; + } + + private bool AddToWhiteList(GUIButton button, object obj) + { + if (string.IsNullOrWhiteSpace(nameBox.Text) || whitelistedPlayers.Find(x => x.Name.ToLower() == nameBox.Text.ToLower()) != null) return false; + whitelistedPlayers.Add(new WhiteListedPlayer(nameBox.Text,pwBox.Text,ipBox.Text)); + CloseFrame(); CreateWhiteListFrame(); + return true; + } + + public bool CloseFrame(GUIButton button=null, object obj=null) + { + whitelistFrame = null; + + return true; + } + } +} diff --git a/Subsurface/Source/Screens/NetLobbyScreen.cs b/Subsurface/Source/Screens/NetLobbyScreen.cs index 309306e04..8c9fb70a5 100644 --- a/Subsurface/Source/Screens/NetLobbyScreen.cs +++ b/Subsurface/Source/Screens/NetLobbyScreen.cs @@ -396,8 +396,12 @@ namespace Barotrauma GUIButton settingsButton = new GUIButton(new Rectangle(-100, 0, 80, 30), "Settings", Alignment.BottomRight, GUI.Style, infoFrame); settingsButton.OnClicked = GameMain.Server.ToggleSettingsFrame; - settingsButton.UserData = "settingsButton"; - + settingsButton.UserData = "settingsButton"; + + GUIButton whitelistButton = new GUIButton(new Rectangle(-200, 0, 80, 30), "Whitelist", Alignment.BottomRight, GUI.Style, infoFrame); + whitelistButton.OnClicked = GameMain.Server.ToggleWhiteListFrame; + whitelistButton.UserData = "whitelistButton"; + if (subList.Selected == null) subList.Select(Math.Max(0, prevSelectedSub)); if (shuttleList.Selected == null) { @@ -771,7 +775,7 @@ namespace Barotrauma playerFrame = new GUIFrame(new Rectangle(0, 0, 0, 0), Color.Black * 0.3f); - var playerFrameInner = new GUIFrame(new Rectangle(0, 0, 300, 150), null, Alignment.Center, GUI.Style, playerFrame); + var playerFrameInner = new GUIFrame(new Rectangle(0, 0, 300, 250), null, Alignment.Center, GUI.Style, playerFrame); playerFrameInner.Padding = new Vector4(20.0f, 20.0f, 20.0f, 20.0f); new GUITextBlock(new Rectangle(0,0,200,20), component.UserData.ToString(), @@ -832,7 +836,7 @@ namespace Barotrauma if (GameMain.Server != null || GameMain.Client.HasPermission(ClientPermissions.Kick)) { - var kickButton = new GUIButton(new Rectangle(0, -30, 100, 20), "Kick", Alignment.BottomLeft, GUI.Style, playerFrameInner); + var kickButton = new GUIButton(new Rectangle(0, -50, 100, 20), "Kick", Alignment.BottomLeft, GUI.Style, playerFrameInner); kickButton.UserData = obj; kickButton.OnClicked += KickPlayer; kickButton.OnClicked += ClosePlayerFrame; @@ -844,6 +848,11 @@ namespace Barotrauma banButton.UserData = obj; banButton.OnClicked += BanPlayer; banButton.OnClicked += ClosePlayerFrame; + + var rangebanButton = new GUIButton(new Rectangle(0, -25, 100, 20), "Ban range", Alignment.BottomLeft, GUI.Style, playerFrameInner); + rangebanButton.UserData = obj; + rangebanButton.OnClicked += BanPlayerRange; + rangebanButton.OnClicked += ClosePlayerFrame; } var closeButton = new GUIButton(new Rectangle(0, 0, 100, 20), "Close", Alignment.BottomRight, GUI.Style, playerFrameInner); @@ -886,8 +895,24 @@ namespace Barotrauma else if (GameMain.Client != null && GameMain.Client.HasPermission(ClientPermissions.Ban)) { GameMain.Client.KickPlayer(userData.ToString(), true); - } + } + + return false; + } + public bool BanPlayerRange(GUIButton button, object userData) + { + if (userData == null) return false; + + if (GameMain.Server != null) + { + GameMain.Server.KickPlayer(userData.ToString(), true, true); + } + else if (GameMain.Client != null && GameMain.Client.HasPermission(ClientPermissions.Ban)) + { + GameMain.Client.KickPlayer(userData.ToString(), true, true); + } + return false; }