From e2ea5c6ce00eee6e2f5580c423b6a2bf038eff9d Mon Sep 17 00:00:00 2001 From: RaidMax Date: Sun, 11 Jul 2021 17:26:30 -0500 Subject: [PATCH] support hostnames for server config --- .../Factories/RConConnectionFactory.cs | 15 ++++------- Application/IW4MServer.cs | 13 +++++++++- Integrations/Cod/CodRConConnection.cs | 6 ++--- .../Source/Interfaces/IRConClientFactory.cs | 5 ++-- Integrations/Source/RConClientFactory.cs | 7 +++--- Integrations/Source/SourceRConConnection.cs | 25 +++++++++---------- .../Configuration/ServerConfiguration.cs | 8 ++---- .../ServerConfigurationValidator.cs | 4 --- .../Interfaces/IRConConnectionFactory.cs | 9 ++++--- SharedLibraryCore/Server.cs | 11 ++++++-- .../ViewComponents/ServerListViewComponent.cs | 6 ++--- .../Views/Client/Statistics/Advanced.cshtml | 2 +- 12 files changed, 59 insertions(+), 52 deletions(-) diff --git a/Application/Factories/RConConnectionFactory.cs b/Application/Factories/RConConnectionFactory.cs index 399820814..14cb1e790 100644 --- a/Application/Factories/RConConnectionFactory.cs +++ b/Application/Factories/RConConnectionFactory.cs @@ -1,4 +1,5 @@ using System; +using System.Net; using SharedLibraryCore.Interfaces; using System.Text; using Integrations.Cod; @@ -26,21 +27,15 @@ namespace IW4MAdmin.Application.Factories _serviceProvider = serviceProvider; } - /// - /// creates a new rcon connection instance - /// - /// ip address of the server - /// port of the server - /// rcon password of the server - /// - public IRConConnection CreateConnection(string ipAddress, int port, string password, string rconEngine) + /// + public IRConConnection CreateConnection(IPEndPoint ipEndpoint, string password, string rconEngine) { return rconEngine switch { - "COD" => new CodRConConnection(ipAddress, port, password, + "COD" => new CodRConConnection(ipEndpoint, password, _serviceProvider.GetRequiredService>(), GameEncoding), "Source" => new SourceRConConnection(_serviceProvider.GetRequiredService>(), - _serviceProvider.GetRequiredService(), ipAddress, port, password), + _serviceProvider.GetRequiredService(), ipEndpoint, password), _ => throw new ArgumentException($"No supported RCon engine available for '{rconEngine}'") }; } diff --git a/Application/IW4MServer.cs b/Application/IW4MServer.cs index ed4a128b6..6a9f500ac 100644 --- a/Application/IW4MServer.cs +++ b/Application/IW4MServer.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Net; using System.Runtime.InteropServices; using System.Text.RegularExpressions; using System.Threading; @@ -1059,6 +1060,16 @@ namespace IW4MAdmin public async Task Initialize() { + try + { + ResolvedIpEndPoint = new IPEndPoint((await Dns.GetHostAddressesAsync(IP)).First(), Port); + } + catch (Exception ex) + { + ServerLogger.LogWarning(ex, "Could not resolve hostname or IP for RCon connection {IP}:{Port}", IP, Port); + ResolvedIpEndPoint = new IPEndPoint(IPAddress.Parse(IP), Port); + } + RconParser = Manager.AdditionalRConParsers .FirstOrDefault(_parser => _parser.Version == ServerConfig.RConParserVersion); @@ -1068,7 +1079,7 @@ namespace IW4MAdmin RconParser ??= Manager.AdditionalRConParsers[0]; EventParser ??= Manager.AdditionalEventParsers[0]; - RemoteConnection = RConConnectionFactory.CreateConnection(IP, Port, Password, RconParser.RConEngine); + RemoteConnection = RConConnectionFactory.CreateConnection(ResolvedIpEndPoint, Password, RconParser.RConEngine); RemoteConnection.SetConfiguration(RconParser); var version = await this.GetMappedDvarValueOrDefaultAsync("version"); diff --git a/Integrations/Cod/CodRConConnection.cs b/Integrations/Cod/CodRConConnection.cs index 5192b551a..15eb1ad07 100644 --- a/Integrations/Cod/CodRConConnection.cs +++ b/Integrations/Cod/CodRConConnection.cs @@ -32,12 +32,12 @@ namespace Integrations.Cod private readonly ILogger _log; private readonly Encoding _gameEncoding; - public CodRConConnection(string ipAddress, int port, string password, ILogger log, Encoding gameEncoding) + public CodRConConnection(IPEndPoint ipEndpoint, string password, ILogger log, Encoding gameEncoding) { - Endpoint = new IPEndPoint(IPAddress.Parse(ipAddress), port); - _gameEncoding = gameEncoding; RConPassword = password; + _gameEncoding = gameEncoding; _log = log; + Endpoint = ipEndpoint; } public void SetConfiguration(IRConParser parser) diff --git a/Integrations/Source/Interfaces/IRConClientFactory.cs b/Integrations/Source/Interfaces/IRConClientFactory.cs index 92b565046..4d33a9ccc 100644 --- a/Integrations/Source/Interfaces/IRConClientFactory.cs +++ b/Integrations/Source/Interfaces/IRConClientFactory.cs @@ -1,9 +1,10 @@ -using RconSharp; +using System.Net; +using RconSharp; namespace Integrations.Source.Interfaces { public interface IRConClientFactory { - RconClient CreateClient(string hostname, int port); + RconClient CreateClient(IPEndPoint ipEndPoint); } } \ No newline at end of file diff --git a/Integrations/Source/RConClientFactory.cs b/Integrations/Source/RConClientFactory.cs index 35e5347b6..7aa69b532 100644 --- a/Integrations/Source/RConClientFactory.cs +++ b/Integrations/Source/RConClientFactory.cs @@ -1,13 +1,14 @@ -using Integrations.Source.Interfaces; +using System.Net; +using Integrations.Source.Interfaces; using RconSharp; namespace Integrations.Source { public class RConClientFactory : IRConClientFactory { - public RconClient CreateClient(string hostname, int port) + public RconClient CreateClient(IPEndPoint ipEndPoint) { - return RconClient.Create(hostname, port); + return RconClient.Create(ipEndPoint.Address.ToString(), ipEndPoint.Port); } } } \ No newline at end of file diff --git a/Integrations/Source/SourceRConConnection.cs b/Integrations/Source/SourceRConConnection.cs index c8f3661ae..5334a4b0c 100644 --- a/Integrations/Source/SourceRConConnection.cs +++ b/Integrations/Source/SourceRConConnection.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Net; using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; @@ -20,8 +21,7 @@ namespace Integrations.Source { private readonly ILogger _logger; private readonly string _password; - private readonly string _hostname; - private readonly int _port; + private readonly IPEndPoint _ipEndPoint; private readonly IRConClientFactory _rconClientFactory; private readonly SemaphoreSlim _activeQuery; @@ -34,13 +34,12 @@ namespace Integrations.Source private bool _needNewSocket = true; public SourceRConConnection(ILogger logger, IRConClientFactory rconClientFactory, - string hostname, int port, string password) + IPEndPoint ipEndPoint, string password) { _rconClientFactory = rconClientFactory; _password = password; - _hostname = hostname; - _port = port; _logger = logger; + _ipEndPoint = ipEndPoint; _activeQuery = new SemaphoreSlim(1, 1); } @@ -67,12 +66,12 @@ namespace Integrations.Source // ignored } - _rconClient = _rconClientFactory.CreateClient(_hostname, _port); + _rconClient = _rconClientFactory.CreateClient(_ipEndPoint); _authenticated = false; _needNewSocket = false; } - using (LogContext.PushProperty("Server", $"{_hostname}:{_port}")) + using (LogContext.PushProperty("Server", _ipEndPoint.ToString())) { _logger.LogDebug("Connecting to RCon socket"); } @@ -90,7 +89,7 @@ namespace Integrations.Source parameters = parameters.ReplaceUnfriendlyCharacters(); parameters = parameters.StripColors(); - using (LogContext.PushProperty("Server", $"{_hostname}:{_port}")) + using (LogContext.PushProperty("Server", _ipEndPoint.ToString())) { _logger.LogDebug("Sending query {Type} with parameters \"{Parameters}\"", type, parameters); } @@ -98,7 +97,7 @@ namespace Integrations.Source var response = await _rconClient.ExecuteCommandAsync(parameters, multiPacket) .WithTimeout(ConnectionTimeout); - using (LogContext.PushProperty("Server", $"{_hostname}:{_port}")) + using (LogContext.PushProperty("Server", $"{_ipEndPoint}")) { _logger.LogDebug("Received RCon response {Response}", response); } @@ -115,7 +114,7 @@ namespace Integrations.Source catch (SocketException ex) { - using (LogContext.PushProperty("Server", $"{_hostname}:{_port}")) + using (LogContext.PushProperty("Server", _ipEndPoint.ToString())) { _logger.LogError(ex, "Socket exception encountered while attempting to communicate with server"); } @@ -128,7 +127,7 @@ namespace Integrations.Source catch (Exception ex) when (ex.GetType() != typeof(NetworkException) && ex.GetType() != typeof(ServerException)) { - using (LogContext.PushProperty("Server", $"{_hostname}:{_port}")) + using (LogContext.PushProperty("Server", _ipEndPoint.ToString())) { _logger.LogError(ex, "Could not execute RCon query {Parameters}", parameters); } @@ -160,7 +159,7 @@ namespace Integrations.Source { if (!_authenticated) { - using (LogContext.PushProperty("Server", $"{_hostname}:{_port}")) + using (LogContext.PushProperty("Server", _ipEndPoint.ToString())) { _logger.LogDebug("Authenticating to RCon socket"); } @@ -170,7 +169,7 @@ namespace Integrations.Source if (!_authenticated) { - using (LogContext.PushProperty("Server", $"{_hostname}:{_port}")) + using (LogContext.PushProperty("Server", _ipEndPoint.ToString())) { _logger.LogError("Could not login to server"); } diff --git a/SharedLibraryCore/Configuration/ServerConfiguration.cs b/SharedLibraryCore/Configuration/ServerConfiguration.cs index 90b4f3957..bc1e1ff01 100644 --- a/SharedLibraryCore/Configuration/ServerConfiguration.cs +++ b/SharedLibraryCore/Configuration/ServerConfiguration.cs @@ -87,12 +87,8 @@ namespace SharedLibraryCore.Configuration while (string.IsNullOrEmpty(IPAddress)) { - string input = Utilities.PromptString(loc["SETUP_SERVER_IP"]); - - if (System.Net.IPAddress.TryParse(input, out System.Net.IPAddress ip)) - { - IPAddress = input; - } + var input = Utilities.PromptString(loc["SETUP_SERVER_IP"]); + IPAddress = input; } Port = Utilities.PromptInt(loc["SETUP_SERVER_PORT"], null, 1, ushort.MaxValue); diff --git a/SharedLibraryCore/Configuration/Validation/ServerConfigurationValidator.cs b/SharedLibraryCore/Configuration/Validation/ServerConfigurationValidator.cs index 5be315964..decc448d6 100644 --- a/SharedLibraryCore/Configuration/Validation/ServerConfigurationValidator.cs +++ b/SharedLibraryCore/Configuration/Validation/ServerConfigurationValidator.cs @@ -10,10 +10,6 @@ namespace SharedLibraryCore.Configuration.Validation { public ServerConfigurationValidator() { - RuleFor(_server => _server.IPAddress) - .NotEmpty() - .Must(_address => IPAddress.TryParse(_address, out _)); - RuleFor(_server => _server.Port) .InclusiveBetween(1, ushort.MaxValue); diff --git a/SharedLibraryCore/Interfaces/IRConConnectionFactory.cs b/SharedLibraryCore/Interfaces/IRConConnectionFactory.cs index 025a5a5a7..3179417ae 100644 --- a/SharedLibraryCore/Interfaces/IRConConnectionFactory.cs +++ b/SharedLibraryCore/Interfaces/IRConConnectionFactory.cs @@ -1,4 +1,6 @@ -namespace SharedLibraryCore.Interfaces +using System.Net; + +namespace SharedLibraryCore.Interfaces { /// /// defines the capabilities of an RCon connection factory @@ -8,11 +10,10 @@ /// /// creates an rcon connection instance /// - /// ip address of the server - /// port of the server + /// ip address and port of the server /// password of the server /// engine to create the rcon connection to /// instance of rcon connection - IRConConnection CreateConnection(string ipAddress, int port, string password, string rconEngine); + IRConConnection CreateConnection(IPEndPoint ipEndpoint, string password, string rconEngine); } } diff --git a/SharedLibraryCore/Server.cs b/SharedLibraryCore/Server.cs index 02907d5ec..fba519eaa 100644 --- a/SharedLibraryCore/Server.cs +++ b/SharedLibraryCore/Server.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; @@ -58,7 +59,9 @@ namespace SharedLibraryCore InitializeAutoMessages(); } - public long EndPoint => Convert.ToInt64($"{IP.Replace(".", "")}{Port}"); + public long EndPoint => IPAddress.TryParse(IP, out _) + ? Convert.ToInt64($"{IP.Replace(".", "")}{Port}") + : $"{IP.Replace(".", "")}{Port}".GetStableHashCode(); /// /// Returns list of all current players @@ -303,7 +306,7 @@ namespace SharedLibraryCore { get { - return Clients.Where(p => p != null/* && !p.IsBot*/).Count(); + return Clients.Count(p => p != null && !p.IsBot); } } public int MaxClients { get; protected set; } @@ -320,7 +323,11 @@ namespace SharedLibraryCore public SemaphoreSlim EventProcessing { get; private set; } // Internal + /// + /// this is actually the hostname now + /// public string IP { get; protected set; } + public IPEndPoint ResolvedIpEndPoint { get; protected set; } public string Version { get; protected set; } public bool IsInitialized { get; set; } protected readonly ILogger ServerLogger; diff --git a/WebfrontCore/ViewComponents/ServerListViewComponent.cs b/WebfrontCore/ViewComponents/ServerListViewComponent.cs index 1c2af9ba2..6ebd71d54 100644 --- a/WebfrontCore/ViewComponents/ServerListViewComponent.cs +++ b/WebfrontCore/ViewComponents/ServerListViewComponent.cs @@ -12,7 +12,7 @@ namespace WebfrontCore.ViewComponents { public IViewComponentResult Invoke(Game? game) { - var servers = Program.Manager.GetServers().Where(_server => !game.HasValue ? true : _server.GameName == game); + var servers = Program.Manager.GetServers().Where(_server => !game.HasValue || _server.GameName == game); var serverInfo = servers.Select(s => new ServerInfo() { @@ -36,8 +36,8 @@ namespace WebfrontCore.ViewComponents }).ToList(), ChatHistory = s.ChatHistory.ToList(), Online = !s.Throttled, - IPAddress = $"{(IPAddress.Parse(s.IP).IsInternal() ? Program.Manager.ExternalIPAddress : s.IP)}:{s.Port}", - ConnectProtocolUrl = s.EventParser.URLProtocolFormat.FormatExt(IPAddress.Parse(s.IP).IsInternal() ? Program.Manager.ExternalIPAddress : s.IP, s.Port) + IPAddress = $"{(s.ResolvedIpEndPoint.Address.IsInternal() ? Program.Manager.ExternalIPAddress : s.IP)}:{s.Port}", + ConnectProtocolUrl = s.EventParser.URLProtocolFormat.FormatExt(s.ResolvedIpEndPoint.Address.IsInternal() ? Program.Manager.ExternalIPAddress : s.IP, s.Port) }).ToList(); return View("_List", serverInfo); } diff --git a/WebfrontCore/Views/Client/Statistics/Advanced.cshtml b/WebfrontCore/Views/Client/Statistics/Advanced.cshtml index b0751db96..7114804a9 100644 --- a/WebfrontCore/Views/Client/Statistics/Advanced.cshtml +++ b/WebfrontCore/Views/Client/Statistics/Advanced.cshtml @@ -139,7 +139,7 @@ : null; var headShots = allPerServer.Any() - ? allPerServer.Where(hit => hit.MeansOfDeath?.Name == headshotKey || hit.MeansOfDeath?.Name == headshotKey2).Sum(hit => hit.HitCount) + ? allPerServer.Where(hit => hit.MeansOfDeath?.Name == headshotKey || hit.HitLocation?.Name == headshotKey2).Sum(hit => hit.HitCount) : (int?) null; // want to default to -- in ui instead of 0 var meleeKills = allPerServer.Any()