support hostnames for server config

This commit is contained in:
RaidMax 2021-07-11 17:26:30 -05:00
parent 5ef00d6dae
commit e2ea5c6ce0
12 changed files with 59 additions and 52 deletions

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Net;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
using System.Text; using System.Text;
using Integrations.Cod; using Integrations.Cod;
@ -26,21 +27,15 @@ namespace IW4MAdmin.Application.Factories
_serviceProvider = serviceProvider; _serviceProvider = serviceProvider;
} }
/// <summary> /// <inheritdoc/>
/// creates a new rcon connection instance public IRConConnection CreateConnection(IPEndPoint ipEndpoint, string password, string rconEngine)
/// </summary>
/// <param name="ipAddress">ip address of the server</param>
/// <param name="port">port of the server</param>
/// <param name="password">rcon password of the server</param>
/// <returns></returns>
public IRConConnection CreateConnection(string ipAddress, int port, string password, string rconEngine)
{ {
return rconEngine switch return rconEngine switch
{ {
"COD" => new CodRConConnection(ipAddress, port, password, "COD" => new CodRConConnection(ipEndpoint, password,
_serviceProvider.GetRequiredService<ILogger<CodRConConnection>>(), GameEncoding), _serviceProvider.GetRequiredService<ILogger<CodRConConnection>>(), GameEncoding),
"Source" => new SourceRConConnection(_serviceProvider.GetRequiredService<ILogger<SourceRConConnection>>(), "Source" => new SourceRConConnection(_serviceProvider.GetRequiredService<ILogger<SourceRConConnection>>(),
_serviceProvider.GetRequiredService<IRConClientFactory>(), ipAddress, port, password), _serviceProvider.GetRequiredService<IRConClientFactory>(), ipEndpoint, password),
_ => throw new ArgumentException($"No supported RCon engine available for '{rconEngine}'") _ => throw new ArgumentException($"No supported RCon engine available for '{rconEngine}'")
}; };
} }

View File

@ -11,6 +11,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
@ -1059,6 +1060,16 @@ namespace IW4MAdmin
public async Task Initialize() 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 RconParser = Manager.AdditionalRConParsers
.FirstOrDefault(_parser => _parser.Version == ServerConfig.RConParserVersion); .FirstOrDefault(_parser => _parser.Version == ServerConfig.RConParserVersion);
@ -1068,7 +1079,7 @@ namespace IW4MAdmin
RconParser ??= Manager.AdditionalRConParsers[0]; RconParser ??= Manager.AdditionalRConParsers[0];
EventParser ??= Manager.AdditionalEventParsers[0]; EventParser ??= Manager.AdditionalEventParsers[0];
RemoteConnection = RConConnectionFactory.CreateConnection(IP, Port, Password, RconParser.RConEngine); RemoteConnection = RConConnectionFactory.CreateConnection(ResolvedIpEndPoint, Password, RconParser.RConEngine);
RemoteConnection.SetConfiguration(RconParser); RemoteConnection.SetConfiguration(RconParser);
var version = await this.GetMappedDvarValueOrDefaultAsync<string>("version"); var version = await this.GetMappedDvarValueOrDefaultAsync<string>("version");

View File

@ -32,12 +32,12 @@ namespace Integrations.Cod
private readonly ILogger _log; private readonly ILogger _log;
private readonly Encoding _gameEncoding; private readonly Encoding _gameEncoding;
public CodRConConnection(string ipAddress, int port, string password, ILogger<CodRConConnection> log, Encoding gameEncoding) public CodRConConnection(IPEndPoint ipEndpoint, string password, ILogger<CodRConConnection> log, Encoding gameEncoding)
{ {
Endpoint = new IPEndPoint(IPAddress.Parse(ipAddress), port);
_gameEncoding = gameEncoding;
RConPassword = password; RConPassword = password;
_gameEncoding = gameEncoding;
_log = log; _log = log;
Endpoint = ipEndpoint;
} }
public void SetConfiguration(IRConParser parser) public void SetConfiguration(IRConParser parser)

View File

@ -1,9 +1,10 @@
using RconSharp; using System.Net;
using RconSharp;
namespace Integrations.Source.Interfaces namespace Integrations.Source.Interfaces
{ {
public interface IRConClientFactory public interface IRConClientFactory
{ {
RconClient CreateClient(string hostname, int port); RconClient CreateClient(IPEndPoint ipEndPoint);
} }
} }

View File

@ -1,13 +1,14 @@
using Integrations.Source.Interfaces; using System.Net;
using Integrations.Source.Interfaces;
using RconSharp; using RconSharp;
namespace Integrations.Source namespace Integrations.Source
{ {
public class RConClientFactory : IRConClientFactory 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);
} }
} }
} }

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -20,8 +21,7 @@ namespace Integrations.Source
{ {
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly string _password; private readonly string _password;
private readonly string _hostname; private readonly IPEndPoint _ipEndPoint;
private readonly int _port;
private readonly IRConClientFactory _rconClientFactory; private readonly IRConClientFactory _rconClientFactory;
private readonly SemaphoreSlim _activeQuery; private readonly SemaphoreSlim _activeQuery;
@ -34,13 +34,12 @@ namespace Integrations.Source
private bool _needNewSocket = true; private bool _needNewSocket = true;
public SourceRConConnection(ILogger<SourceRConConnection> logger, IRConClientFactory rconClientFactory, public SourceRConConnection(ILogger<SourceRConConnection> logger, IRConClientFactory rconClientFactory,
string hostname, int port, string password) IPEndPoint ipEndPoint, string password)
{ {
_rconClientFactory = rconClientFactory; _rconClientFactory = rconClientFactory;
_password = password; _password = password;
_hostname = hostname;
_port = port;
_logger = logger; _logger = logger;
_ipEndPoint = ipEndPoint;
_activeQuery = new SemaphoreSlim(1, 1); _activeQuery = new SemaphoreSlim(1, 1);
} }
@ -67,12 +66,12 @@ namespace Integrations.Source
// ignored // ignored
} }
_rconClient = _rconClientFactory.CreateClient(_hostname, _port); _rconClient = _rconClientFactory.CreateClient(_ipEndPoint);
_authenticated = false; _authenticated = false;
_needNewSocket = false; _needNewSocket = false;
} }
using (LogContext.PushProperty("Server", $"{_hostname}:{_port}")) using (LogContext.PushProperty("Server", _ipEndPoint.ToString()))
{ {
_logger.LogDebug("Connecting to RCon socket"); _logger.LogDebug("Connecting to RCon socket");
} }
@ -90,7 +89,7 @@ namespace Integrations.Source
parameters = parameters.ReplaceUnfriendlyCharacters(); parameters = parameters.ReplaceUnfriendlyCharacters();
parameters = parameters.StripColors(); 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); _logger.LogDebug("Sending query {Type} with parameters \"{Parameters}\"", type, parameters);
} }
@ -98,7 +97,7 @@ namespace Integrations.Source
var response = await _rconClient.ExecuteCommandAsync(parameters, multiPacket) var response = await _rconClient.ExecuteCommandAsync(parameters, multiPacket)
.WithTimeout(ConnectionTimeout); .WithTimeout(ConnectionTimeout);
using (LogContext.PushProperty("Server", $"{_hostname}:{_port}")) using (LogContext.PushProperty("Server", $"{_ipEndPoint}"))
{ {
_logger.LogDebug("Received RCon response {Response}", response); _logger.LogDebug("Received RCon response {Response}", response);
} }
@ -115,7 +114,7 @@ namespace Integrations.Source
catch (SocketException ex) 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"); _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) && catch (Exception ex) when (ex.GetType() != typeof(NetworkException) &&
ex.GetType() != typeof(ServerException)) 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); _logger.LogError(ex, "Could not execute RCon query {Parameters}", parameters);
} }
@ -160,7 +159,7 @@ namespace Integrations.Source
{ {
if (!_authenticated) if (!_authenticated)
{ {
using (LogContext.PushProperty("Server", $"{_hostname}:{_port}")) using (LogContext.PushProperty("Server", _ipEndPoint.ToString()))
{ {
_logger.LogDebug("Authenticating to RCon socket"); _logger.LogDebug("Authenticating to RCon socket");
} }
@ -170,7 +169,7 @@ namespace Integrations.Source
if (!_authenticated) if (!_authenticated)
{ {
using (LogContext.PushProperty("Server", $"{_hostname}:{_port}")) using (LogContext.PushProperty("Server", _ipEndPoint.ToString()))
{ {
_logger.LogError("Could not login to server"); _logger.LogError("Could not login to server");
} }

View File

@ -87,13 +87,9 @@ namespace SharedLibraryCore.Configuration
while (string.IsNullOrEmpty(IPAddress)) while (string.IsNullOrEmpty(IPAddress))
{ {
string input = Utilities.PromptString(loc["SETUP_SERVER_IP"]); var input = Utilities.PromptString(loc["SETUP_SERVER_IP"]);
if (System.Net.IPAddress.TryParse(input, out System.Net.IPAddress ip))
{
IPAddress = input; IPAddress = input;
} }
}
Port = Utilities.PromptInt(loc["SETUP_SERVER_PORT"], null, 1, ushort.MaxValue); Port = Utilities.PromptInt(loc["SETUP_SERVER_PORT"], null, 1, ushort.MaxValue);
Password = Utilities.PromptString(loc["SETUP_SERVER_RCON"]); Password = Utilities.PromptString(loc["SETUP_SERVER_RCON"]);

View File

@ -10,10 +10,6 @@ namespace SharedLibraryCore.Configuration.Validation
{ {
public ServerConfigurationValidator() public ServerConfigurationValidator()
{ {
RuleFor(_server => _server.IPAddress)
.NotEmpty()
.Must(_address => IPAddress.TryParse(_address, out _));
RuleFor(_server => _server.Port) RuleFor(_server => _server.Port)
.InclusiveBetween(1, ushort.MaxValue); .InclusiveBetween(1, ushort.MaxValue);

View File

@ -1,4 +1,6 @@
namespace SharedLibraryCore.Interfaces using System.Net;
namespace SharedLibraryCore.Interfaces
{ {
/// <summary> /// <summary>
/// defines the capabilities of an RCon connection factory /// defines the capabilities of an RCon connection factory
@ -8,11 +10,10 @@
/// <summary> /// <summary>
/// creates an rcon connection instance /// creates an rcon connection instance
/// </summary> /// </summary>
/// <param name="ipAddress">ip address of the server</param> /// <param name="ipEndpoint">ip address and port of the server</param>
/// <param name="port">port of the server</param>
/// <param name="password"> password of the server</param> /// <param name="password"> password of the server</param>
/// <param name="rconEngine">engine to create the rcon connection to</param> /// <param name="rconEngine">engine to create the rcon connection to</param>
/// <returns>instance of rcon connection</returns> /// <returns>instance of rcon connection</returns>
IRConConnection CreateConnection(string ipAddress, int port, string password, string rconEngine); IRConConnection CreateConnection(IPEndPoint ipEndpoint, string password, string rconEngine);
} }
} }

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -58,7 +59,9 @@ namespace SharedLibraryCore
InitializeAutoMessages(); 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();
/// <summary> /// <summary>
/// Returns list of all current players /// Returns list of all current players
@ -303,7 +306,7 @@ namespace SharedLibraryCore
{ {
get get
{ {
return Clients.Where(p => p != null/* && !p.IsBot*/).Count(); return Clients.Count(p => p != null && !p.IsBot);
} }
} }
public int MaxClients { get; protected set; } public int MaxClients { get; protected set; }
@ -320,7 +323,11 @@ namespace SharedLibraryCore
public SemaphoreSlim EventProcessing { get; private set; } public SemaphoreSlim EventProcessing { get; private set; }
// Internal // Internal
/// <summary>
/// this is actually the hostname now
/// </summary>
public string IP { get; protected set; } public string IP { get; protected set; }
public IPEndPoint ResolvedIpEndPoint { get; protected set; }
public string Version { get; protected set; } public string Version { get; protected set; }
public bool IsInitialized { get; set; } public bool IsInitialized { get; set; }
protected readonly ILogger ServerLogger; protected readonly ILogger ServerLogger;

View File

@ -12,7 +12,7 @@ namespace WebfrontCore.ViewComponents
{ {
public IViewComponentResult Invoke(Game? game) 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() var serverInfo = servers.Select(s => new ServerInfo()
{ {
@ -36,8 +36,8 @@ namespace WebfrontCore.ViewComponents
}).ToList(), }).ToList(),
ChatHistory = s.ChatHistory.ToList(), ChatHistory = s.ChatHistory.ToList(),
Online = !s.Throttled, Online = !s.Throttled,
IPAddress = $"{(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(IPAddress.Parse(s.IP).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(); }).ToList();
return View("_List", serverInfo); return View("_List", serverInfo);
} }

View File

@ -139,7 +139,7 @@
: null; : null;
var headShots = allPerServer.Any() 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 : (int?) null; // want to default to -- in ui instead of 0
var meleeKills = allPerServer.Any() var meleeKills = allPerServer.Any()