From df3aae5d498e101c7789edd0d5d26812317cadbe Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Wed, 9 Aug 2023 16:23:41 +0200 Subject: [PATCH] feat: a big draft --- .dockerignore | 25 +++ Continuity.sln | 22 +-- Continuity.sln.DotSettings | 2 + Server.Packets/IPacket.cs | 7 - Server.Packets/PacketForwardingService.cs | 40 ---- Server.Packets/PacketId.cs | 6 - Server.Packets/PacketIdAttribute.cs | 12 -- Server.Packets/Server.Packets.csproj | 13 -- Server.Tests/PacketTests.cs | 19 -- Server.Tests/Server.Tests.csproj | 29 --- Server.Tests/Usings.cs | 2 - Server/AesProvider.cs | 46 ----- Server/AesSession.cs | 216 ++++++++++++++++++++++ Server/AuthorizationServer.cs | 55 ------ Server/AuthorizationServerSession.cs | 44 ----- Server/Dockerfile | 18 ++ Server/IPacketHandler.cs | 6 + Server/LoginHandler.cs | 12 ++ Server/OperationCode.cs | 6 + Server/Packet.cs | 24 +++ Server/PacketConsumer.cs | 18 ++ Server/PacketDistributorService.cs | 74 ++++++++ Server/PacketHandler.cs | 11 ++ Server/Program.cs | 41 ++-- Server/Server.csproj | 26 +-- Server/WonderkingAuthServer.cs | 70 +++++++ Server/WonderkingSession.cs | 21 +++ Server/appsettings.json | 8 - qodana.yaml | 29 +++ 29 files changed, 570 insertions(+), 332 deletions(-) create mode 100644 .dockerignore create mode 100644 Continuity.sln.DotSettings delete mode 100644 Server.Packets/IPacket.cs delete mode 100644 Server.Packets/PacketForwardingService.cs delete mode 100644 Server.Packets/PacketId.cs delete mode 100644 Server.Packets/PacketIdAttribute.cs delete mode 100644 Server.Packets/Server.Packets.csproj delete mode 100644 Server.Tests/PacketTests.cs delete mode 100644 Server.Tests/Server.Tests.csproj delete mode 100644 Server.Tests/Usings.cs delete mode 100644 Server/AesProvider.cs create mode 100644 Server/AesSession.cs delete mode 100644 Server/AuthorizationServer.cs delete mode 100644 Server/AuthorizationServerSession.cs create mode 100644 Server/Dockerfile create mode 100644 Server/IPacketHandler.cs create mode 100644 Server/LoginHandler.cs create mode 100644 Server/OperationCode.cs create mode 100644 Server/Packet.cs create mode 100644 Server/PacketConsumer.cs create mode 100644 Server/PacketDistributorService.cs create mode 100644 Server/PacketHandler.cs create mode 100644 Server/WonderkingAuthServer.cs create mode 100644 Server/WonderkingSession.cs delete mode 100644 Server/appsettings.json create mode 100644 qodana.yaml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..cd967fc --- /dev/null +++ b/.dockerignore @@ -0,0 +1,25 @@ +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/.idea +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md \ No newline at end of file diff --git a/Continuity.sln b/Continuity.sln index ddcf71a..0ceae62 100644 --- a/Continuity.sln +++ b/Continuity.sln @@ -1,10 +1,6 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Server", "Server\Server.csproj", "{DDE63D00-4CC2-4397-B409-410D9F272F0F}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Server.Tests", "Server.Tests\Server.Tests.csproj", "{5D09881E-E059-46F5-9064-66F192258C0C}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Server.Packets", "Server.Packets\Server.Packets.csproj", "{C0769799-62D3-4AEE-9335-9011D97C1DE2}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Server", "Server\Server.csproj", "{7EDA8B31-3E03-4CA3-87D1-CFEB05C277D6}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -12,17 +8,9 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {DDE63D00-4CC2-4397-B409-410D9F272F0F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DDE63D00-4CC2-4397-B409-410D9F272F0F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DDE63D00-4CC2-4397-B409-410D9F272F0F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DDE63D00-4CC2-4397-B409-410D9F272F0F}.Release|Any CPU.Build.0 = Release|Any CPU - {5D09881E-E059-46F5-9064-66F192258C0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5D09881E-E059-46F5-9064-66F192258C0C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5D09881E-E059-46F5-9064-66F192258C0C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5D09881E-E059-46F5-9064-66F192258C0C}.Release|Any CPU.Build.0 = Release|Any CPU - {C0769799-62D3-4AEE-9335-9011D97C1DE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C0769799-62D3-4AEE-9335-9011D97C1DE2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C0769799-62D3-4AEE-9335-9011D97C1DE2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C0769799-62D3-4AEE-9335-9011D97C1DE2}.Release|Any CPU.Build.0 = Release|Any CPU + {7EDA8B31-3E03-4CA3-87D1-CFEB05C277D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7EDA8B31-3E03-4CA3-87D1-CFEB05C277D6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7EDA8B31-3E03-4CA3-87D1-CFEB05C277D6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7EDA8B31-3E03-4CA3-87D1-CFEB05C277D6}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/Continuity.sln.DotSettings b/Continuity.sln.DotSettings new file mode 100644 index 0000000..bc40c83 --- /dev/null +++ b/Continuity.sln.DotSettings @@ -0,0 +1,2 @@ + + True \ No newline at end of file diff --git a/Server.Packets/IPacket.cs b/Server.Packets/IPacket.cs deleted file mode 100644 index e424a16..0000000 --- a/Server.Packets/IPacket.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Server.Packets; - -public interface IPacket -{ - Span Serialize(); - void Deserialize(Span bytes); -} \ No newline at end of file diff --git a/Server.Packets/PacketForwardingService.cs b/Server.Packets/PacketForwardingService.cs deleted file mode 100644 index 2049ba7..0000000 --- a/Server.Packets/PacketForwardingService.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System.Collections.Immutable; -using System.Reflection; - -namespace Server.Packets; - -public class PacketForwardingService -{ - private ImmutableDictionary _packets = null!; - - public PacketForwardingService() - { - InitializePackets(typeof(IPacket).Assembly); - } - - private void InitializePackets(Assembly assembly) - { - // Get all types that implement IPacket and have a PacketIdAttribute - _packets = assembly.GetTypes() - .Select(x => (attr: x.GetCustomAttribute(), ctor: x.GetConstructor(Type.EmptyTypes))) - .Where(x => x.GetType().IsAssignableTo(typeof(IPacket)) && x.GetType().IsClass) - .Where(x => x.attr != null && x.ctor != null) - .ToImmutableDictionary(x => x.attr!.PacketId, x => x.ctor)!; - } - - public IPacket Dispatch(Span bytes) - { - // Get constructor according to packet id - IPacket packet = _packets[GetPacketId(bytes)].Invoke(Array.Empty()) as IPacket ?? - throw new InvalidOperationException(); - // Slice off packet id - packet.Deserialize(bytes.Slice(2)); - return packet; - } - - private PacketId GetPacketId(Span bytes) - { - var id = (PacketId)BitConverter.ToUInt16(new[] { bytes[0], bytes[1] }); - return id; - } -} \ No newline at end of file diff --git a/Server.Packets/PacketId.cs b/Server.Packets/PacketId.cs deleted file mode 100644 index b6263a0..0000000 --- a/Server.Packets/PacketId.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Server.Packets; - -public enum PacketId : ushort -{ - -} \ No newline at end of file diff --git a/Server.Packets/PacketIdAttribute.cs b/Server.Packets/PacketIdAttribute.cs deleted file mode 100644 index 37c8eb5..0000000 --- a/Server.Packets/PacketIdAttribute.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Server.Packets; -[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] -[JetBrains.Annotations.BaseTypeRequiredAttribute(typeof(IPacket))] -public class PacketIdAttribute : Attribute -{ - public PacketIdAttribute(PacketId packetId) - { - PacketId = packetId; - } - - public PacketId PacketId { get; private set; } -} \ No newline at end of file diff --git a/Server.Packets/Server.Packets.csproj b/Server.Packets/Server.Packets.csproj deleted file mode 100644 index 552c270..0000000 --- a/Server.Packets/Server.Packets.csproj +++ /dev/null @@ -1,13 +0,0 @@ - - - - net7.0 - enable - enable - - - - - - - diff --git a/Server.Tests/PacketTests.cs b/Server.Tests/PacketTests.cs deleted file mode 100644 index a9d3def..0000000 --- a/Server.Tests/PacketTests.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Reflection; - -namespace Server.Tests; - -[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute] -public class PacketTests -{ - [Fact] - public void TestPacketInheritingIPacket() - { - var allTypes = AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes()) - .Where(x => x.IsAssignableTo(typeof(Packets.IPacket)) && x.IsClass); - if (!allTypes.Any()) - true.Should().BeTrue("There have been no types found which can be checked"); - else - allTypes.All(t => t.GetCustomAttributes().Any()) - .Should().BeTrue("All types inheriting from IPacket should have the PacketIdAttribute attribute."); - } -} \ No newline at end of file diff --git a/Server.Tests/Server.Tests.csproj b/Server.Tests/Server.Tests.csproj deleted file mode 100644 index f7ab799..0000000 --- a/Server.Tests/Server.Tests.csproj +++ /dev/null @@ -1,29 +0,0 @@ - - - - net7.0 - enable - enable - - false - - - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - - - diff --git a/Server.Tests/Usings.cs b/Server.Tests/Usings.cs deleted file mode 100644 index 7fef4b0..0000000 --- a/Server.Tests/Usings.cs +++ /dev/null @@ -1,2 +0,0 @@ -global using Xunit; -global using FluentAssertions; \ No newline at end of file diff --git a/Server/AesProvider.cs b/Server/AesProvider.cs deleted file mode 100644 index 717ac24..0000000 --- a/Server/AesProvider.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.Security.Cryptography; -using Microsoft.Extensions.Logging; - -namespace Server; - -public class AesProvider -{ - private readonly Aes _aes; - private readonly ICryptoTransform _encryptor; - private readonly ICryptoTransform _decryptor; - private readonly ILogger _logger; - - public AesProvider(ILoggerFactory loggerFactory, Span key, Span iv) - { - _logger = loggerFactory.CreateLogger(nameof(AesProvider)); - _aes = Aes.Create(); - _aes.Key = key.ToArray(); - _aes.IV = iv.ToArray(); - _encryptor = _aes.CreateEncryptor(); - _decryptor = _aes.CreateDecryptor(); - } - - public Span Encrypt(Span input) - { - using MemoryStream memoryStream = new MemoryStream(); - using CryptoStream cryptoStream = new CryptoStream(memoryStream, _encryptor, CryptoStreamMode.Write); - cryptoStream.Write(input); - cryptoStream.Flush(); - - Span bytes = memoryStream.ToArray(); - - return bytes; - } - - public Span Decrypt(Span input) - { - using MemoryStream memoryStream = new MemoryStream(input.ToArray()); - using CryptoStream cryptoStream = new CryptoStream(memoryStream, _decryptor, CryptoStreamMode.Read); - Span bytes = new byte[input.Length]; - var i = cryptoStream.Read(bytes); - if (i > 0) - _logger.LogWarning("CryptoStream hasn't been read till the end. ({length}", i); - - return bytes; - } -} \ No newline at end of file diff --git a/Server/AesSession.cs b/Server/AesSession.cs new file mode 100644 index 0000000..3b40a28 --- /dev/null +++ b/Server/AesSession.cs @@ -0,0 +1,216 @@ +using System.Buffers.Binary; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; +using System.Runtime.Serialization.Formatters.Binary; +using System.Security.Cryptography; +using System.Text; +using System.Text.Json.Serialization; +using MassTransit.Mediator; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using NetCoreServer; +using Buffer = NetCoreServer.Buffer; + +namespace Server; + +public abstract class AesSession : TcpSession +{ + private readonly ILogger _logger; + private readonly IMediator _mediator; + + private static readonly byte[] Key = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7 } + .Reverse().ToArray(); + + private static readonly byte[] IV = new byte[] + { 0xfe, 220, 0xba, 0x98, 0x76, 0x54, 50, 0x10, 15, 30, 0x2d, 60, 0x4b, 90, 0x69, 120 }.Reverse().ToArray(); + + private readonly ICryptoTransform _encryptor; + private readonly ICryptoTransform _decryptor; + + protected AesSession(TcpServer + server, ILogger logger, IMediator mediator) : base(server) + { + _logger = logger; + _mediator = mediator; + var aes = Aes.Create(); + aes.Key = Key; + aes.IV = IV; + aes.Padding = PaddingMode.None; + aes.Mode = CipherMode.ECB; + + _decryptor = aes.CreateDecryptor(aes.Key, aes.IV); + _encryptor = aes.CreateEncryptor(aes.Key, aes.IV); + } + + public override long Send(byte[] buffer) + { + byte[] encryptedBuffer; + using (var ms = new MemoryStream()) + using (var cs = new CryptoStream(ms, _encryptor, CryptoStreamMode.Write)) + { + cs.Write(buffer); + cs.FlushFinalBlock(); + encryptedBuffer = ms.ToArray(); + } + + return base.Send(encryptedBuffer); + } + + public override long Send(byte[] buffer, long offset, long size) + { + byte[] encryptedBuffer; + using (var ms = new MemoryStream()) + using (var cs = new CryptoStream(ms, _encryptor, CryptoStreamMode.Write)) + { + cs.Write(buffer, (int)offset, (int)size); + cs.FlushFinalBlock(); + encryptedBuffer = ms.ToArray(); + } + + return base.Send(encryptedBuffer, 0, encryptedBuffer.Length); + } + + public override long Send(ReadOnlySpan buffer) + { + Span encryptedBuffer; + using (var ms = new MemoryStream()) + using (var cs = new CryptoStream(ms, _encryptor, CryptoStreamMode.Write)) + { + cs.Write(buffer); + cs.FlushFinalBlock(); + encryptedBuffer = ms.ToArray(); + } + + return base.Send(encryptedBuffer); + } + + public override bool SendAsync(byte[] buffer) + { + byte[] encryptedBuffer; + using (var ms = new MemoryStream()) + using (var cs = new CryptoStream(ms, _encryptor, CryptoStreamMode.Write)) + { + cs.Write(buffer); + cs.FlushFinalBlock(); + encryptedBuffer = ms.ToArray(); + } + + return base.SendAsync(encryptedBuffer); + } + + public override bool SendAsync(byte[] buffer, long offset, long size) + { + byte[] encryptedBuffer; + using (var ms = new MemoryStream()) + using (var cs = new CryptoStream(ms, _encryptor, CryptoStreamMode.Write)) + { + cs.Write(buffer, (int)offset, (int)size); + cs.FlushFinalBlock(); + encryptedBuffer = ms.ToArray(); + } + + return base.SendAsync(encryptedBuffer, 0, encryptedBuffer.Length); + } + + public override bool SendAsync(ReadOnlySpan buffer) + { + Span encryptedBuffer; + using (var ms = new MemoryStream()) + using (var cs = new CryptoStream(ms, _encryptor, CryptoStreamMode.Write)) + { + cs.Write(buffer); + cs.FlushFinalBlock(); + encryptedBuffer = ms.ToArray(); + } + + return base.SendAsync(encryptedBuffer); + } + + public override long Receive(byte[] buffer) + { + Span decryptedBuffer; + using (var ms = new MemoryStream()) + using (var cs = new CryptoStream(ms, _decryptor, CryptoStreamMode.Write)) + { + cs.Write(buffer); + cs.FlushFinalBlock(); + decryptedBuffer = ms.ToArray(); + } + + return base.Receive(decryptedBuffer.ToArray()); + } + + public override long Receive(byte[] buffer, long offset, long size) + { + byte[] decryptedBuffer; + using (var ms = new MemoryStream()) + using (var cs = new CryptoStream(ms, _decryptor, CryptoStreamMode.Write)) + { + cs.Write(buffer, (int)offset, (int)size); + cs.FlushFinalBlock(); + decryptedBuffer = ms.ToArray(); + } + + return base.Receive(decryptedBuffer, 0, decryptedBuffer.Length); + } + + protected override void OnReceived(byte[] buffer, long offset, long size) + { + try + { + Console.WriteLine($"Length: {size} & offset: {offset}"); + //Console.WriteLine(BitConverter.ToString(buffer.ToArray()).Replace("-", string.Empty)); + Span decryptedBuffer = new byte[size]; + + + // xor every value after the first 8 bytes + // then apply AES decryption + var dataBuffer = Decrypt(new ArraySegment(buffer, 8, (int)size - 8).ToArray()); + /*using (var ms = new MemoryStream(dataBuffer)) + using (var cs = new CryptoStream(ms, _decryptor, CryptoStreamMode.Read)) + { + cs.Read(decryptedBuffer); + }*/ + + + Console.WriteLine("Length " + BitConverter.ToUInt16(buffer, 0)); + var opCode = BitConverter.ToUInt16(buffer.ToArray(), 2); + Console.WriteLine("Packet Op Code: " + opCode); + Console.WriteLine("Some Value: " + buffer[4]); + /* + * 20s = 5 + * 15s = 4 + * 10s = 3 + * + * client alive time * 5s => uptime + */ + var clientAliveTime = BitConverter.ToUInt16(buffer.ToArray(), 5); + Console.WriteLine("Client Alive time: " + clientAliveTime); + Console.WriteLine("Might be a flag:" + buffer[7]); + + Console.WriteLine("username: " + Encoding.ASCII.GetString(dataBuffer.ToArray(), 0, 20)); + Console.WriteLine("password: " + Encoding.ASCII.GetString(dataBuffer.ToArray(), 20, 32)); + Console.WriteLine("Full buffer: " + Encoding.ASCII.GetString(dataBuffer.ToArray())); + + Packet packet = new Packet((OperationCode)opCode, dataBuffer, clientAliveTime, buffer[0], buffer[3], + Id); + Parallel.Invoke(() => _mediator.Send(packet)); + base.OnReceived(decryptedBuffer.ToArray(), offset, decryptedBuffer.Length); + } + catch (CryptographicException ex) + { + _logger.LogError("An error has occured while decrypting: {ErrorMessage}", ex.Message); + _logger.LogError("Default buffer message: {Message}", Encoding.ASCII.GetString(buffer)); + } + } + + private byte[] Decrypt(byte[] buffer) + { + for (int i = 0; i < buffer.Length; ++i) + { + buffer[i] = (byte)(buffer[i] ^ i ^ (3 * (0xFE - i))); + } + + return buffer; + } +} \ No newline at end of file diff --git a/Server/AuthorizationServer.cs b/Server/AuthorizationServer.cs deleted file mode 100644 index eccb0ff..0000000 --- a/Server/AuthorizationServer.cs +++ /dev/null @@ -1,55 +0,0 @@ -using Microsoft.Extensions.Logging; -using ReInject.Implementation.Attributes; - -namespace Server; - -using Microsoft.Extensions.Configuration; - -public class AuthorizationServer : NetCoreServer.TcpServer -{ - private readonly AesProvider _aesProvider; - private readonly ILogger _logger; - - public AuthorizationServer(IConfiguration configuration, ILoggerFactory loggerFactory, AesProvider aesProvider) : base( - configuration["auth:address"]!, - configuration.GetValue("auth:port")) - { - _aesProvider = aesProvider; - _logger = loggerFactory.CreateLogger(nameof(AuthorizationServer)); - } - - protected override NetCoreServer.TcpSession CreateSession() - { - var session = new AuthorizationServerSession(this, _logger, _aesProvider); - return session; - } - - protected override void OnStarting() - { - base.OnStarting(); - _logger.LogInformation("is starting"); - } - - protected override void OnStarted() - { - base.OnStarted(); - _logger.LogInformation("is started"); - } - - protected override void OnStopping() - { - base.OnStopping(); - _logger.LogInformation("is stopping"); - } - - protected override void OnStopped() - { - base.OnStopped(); - _logger.LogInformation("is stopped"); - } - - protected override void OnError(System.Net.Sockets.SocketError error) - { - _logger.LogError("Authorization server caught an error with code {error}", error); - } -} \ No newline at end of file diff --git a/Server/AuthorizationServerSession.cs b/Server/AuthorizationServerSession.cs deleted file mode 100644 index 950dbb8..0000000 --- a/Server/AuthorizationServerSession.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System.Net.Sockets; -using Microsoft.Extensions.Logging; -using NetCoreServer; - -namespace Server; - -public class AuthorizationServerSession : TcpSession -{ - private readonly ILogger _logger; - private readonly AesProvider _aesProvider; - - public AuthorizationServerSession(AuthorizationServer server, ILogger logger, AesProvider aesProvider) : base(server) - { - _logger = logger; - _aesProvider = aesProvider; - } - - public override long Send(byte[] buffer, long offset, long size) - { - var bytes = _aesProvider.Encrypt(buffer); - return bytes.Length; - } - - public override long Receive(byte[] buffer, long offset, long size) - { - var bytes = _aesProvider.Decrypt(buffer); - return bytes.Length; - } - - protected override void OnConnected() - { - _logger.LogInformation("Session with Id {Id} is connected.", Id); - } - - protected override void OnDisconnected() - { - _logger.LogInformation("Session with Id {Id} is disconnected.", Id); - } - - protected override void OnError(SocketError error) - { - _logger.LogError("Authorization session caught an error with code {error}", error); - } -} \ No newline at end of file diff --git a/Server/Dockerfile b/Server/Dockerfile new file mode 100644 index 0000000..189051f --- /dev/null +++ b/Server/Dockerfile @@ -0,0 +1,18 @@ +FROM mcr.microsoft.com/dotnet/runtime:7.0 AS base +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build +WORKDIR /src +COPY ["Serer/Serer.csproj", "Serer/"] +RUN dotnet restore "Serer/Serer.csproj" +COPY . . +WORKDIR "/src/Serer" +RUN dotnet build "Serer.csproj" -c Release -o /app/build + +FROM build AS publish +RUN dotnet publish "Serer.csproj" -c Release -o /app/publish /p:UseAppHost=false + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "Serer.dll"] diff --git a/Server/IPacketHandler.cs b/Server/IPacketHandler.cs new file mode 100644 index 0000000..6068aa3 --- /dev/null +++ b/Server/IPacketHandler.cs @@ -0,0 +1,6 @@ +namespace Server; + +public interface IPacketHandler +{ + public void Process(Packet packet); +} \ No newline at end of file diff --git a/Server/LoginHandler.cs b/Server/LoginHandler.cs new file mode 100644 index 0000000..a6f5465 --- /dev/null +++ b/Server/LoginHandler.cs @@ -0,0 +1,12 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Server; + +[PacketHandler(OperationCode.LoginInfo)] +public class LoginHandler : IPacketHandler +{ + public void Process(Packet packet) + { + Console.WriteLine("Login being processed."); + } +} \ No newline at end of file diff --git a/Server/OperationCode.cs b/Server/OperationCode.cs new file mode 100644 index 0000000..e4c2ae4 --- /dev/null +++ b/Server/OperationCode.cs @@ -0,0 +1,6 @@ +namespace Server; + +public enum OperationCode : ushort +{ + LoginInfo = 11 +} \ No newline at end of file diff --git a/Server/Packet.cs b/Server/Packet.cs new file mode 100644 index 0000000..c3795e9 --- /dev/null +++ b/Server/Packet.cs @@ -0,0 +1,24 @@ +using MassTransit; + +namespace Server; + +[MessageUrn("packets")] +public class Packet +{ + public readonly OperationCode OperationCode; + public readonly byte[] MessageBody; + public readonly TimeSpan ClientAliveTime; + public readonly byte UnknownValue; + public readonly byte UnknownValue2; + public readonly Guid SessionId; + + public Packet(OperationCode operationCode, byte[] messageBody, uint aliveTime, byte unknownValue2, byte unknownValue, Guid sessionId) + { + MessageBody = messageBody; + UnknownValue2 = unknownValue2; + UnknownValue = unknownValue; + SessionId = sessionId; + OperationCode = operationCode; + ClientAliveTime = TimeSpan.FromSeconds(5 * aliveTime); + } +} \ No newline at end of file diff --git a/Server/PacketConsumer.cs b/Server/PacketConsumer.cs new file mode 100644 index 0000000..7def403 --- /dev/null +++ b/Server/PacketConsumer.cs @@ -0,0 +1,18 @@ +using MassTransit; + +namespace Server; + +public class PacketConsumer : IConsumer +{ + private readonly PacketDistributorService _distributorService; + + public PacketConsumer(PacketDistributorService distributorService) + { + _distributorService = distributorService; + } + + public Task Consume(ConsumeContext context) + { + return _distributorService.AddPacket(context.Message); + } +} \ No newline at end of file diff --git a/Server/PacketDistributorService.cs b/Server/PacketDistributorService.cs new file mode 100644 index 0000000..efafac9 --- /dev/null +++ b/Server/PacketDistributorService.cs @@ -0,0 +1,74 @@ +using System.Collections.Concurrent; +using System.Reflection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace Server; + +public class PacketDistributorService : IHostedService +{ + private readonly ConcurrentQueue _concurrentQueue; + private readonly ILogger _logger; + private readonly CancellationTokenSource _cancellationTokenSource = new(); + private readonly Dictionary _actions; + + public PacketDistributorService(ILogger logger) + { + _concurrentQueue = new ConcurrentQueue(); + _logger = logger; + _actions = new Dictionary(); + var packetHandlers = Assembly.GetExecutingAssembly().GetTypes().Where(t => + t.GetCustomAttributes().Any(attribute => attribute.GetType() == typeof(PacketHandler)) && + t.GetInterfaces().Contains(typeof(IPacketHandler))); + foreach (var type in packetHandlers) + { + PacketHandler packetHandler = type.GetCustomAttribute()!; + _actions.Add(packetHandler.Code, + type.GetConstructor( Type.EmptyTypes) + .Invoke(null) as IPacketHandler); + _logger.LogInformation( + "Handler for ID \"{PacketHandlerCode}\" has been added with name \"{PacketHandlerName}\"", + packetHandler.Code, type.Name); + } + } + + public Task StartAsync(CancellationToken cancellationToken) + { + return Task.CompletedTask; + } + + public Task AddPacket(Packet packet) + { + _logger.LogInformation("Packet with ID: {MessageOperationCode} has been received", + packet.OperationCode); + _concurrentQueue.Enqueue(packet); + return Task.Run(DequeueAndProcessAsync); + } + + private async Task DequeueAndProcessAsync() + { + while (!_cancellationTokenSource.IsCancellationRequested) + { + if (_concurrentQueue.TryDequeue(out var item)) + { + Parallel.Invoke(() => + { + var tempId = Guid.NewGuid(); + _logger.LogInformation("[{TempId}] Packet with ID: {MessageOperationCode} is being dequeued",tempId, item.OperationCode); + var packetHandler = _actions[item.OperationCode]; + packetHandler.Process(item); + _logger.LogInformation("[{TempId}] Packet with ID: {MessageOperationCode} has finished",tempId, item.OperationCode); + }); + } + else + { + await Task.Delay(100); // Delay to prevent busy-waiting, can be adjusted based on needs + } + } + } + + public Task StopAsync(CancellationToken cancellationToken) + { + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Server/PacketHandler.cs b/Server/PacketHandler.cs new file mode 100644 index 0000000..5d8dedd --- /dev/null +++ b/Server/PacketHandler.cs @@ -0,0 +1,11 @@ +namespace Server; + +public class PacketHandler : Attribute +{ + public readonly OperationCode Code; + + public PacketHandler(OperationCode code) + { + Code = code; + } +} \ No newline at end of file diff --git a/Server/Program.cs b/Server/Program.cs index 0974b20..b42a324 100644 --- a/Server/Program.cs +++ b/Server/Program.cs @@ -1,28 +1,27 @@ -using Microsoft.Extensions.Configuration; +using System.Net; +using System.Reflection; +using MassTransit; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -using ReInject; using Server; -var services = Injector.GetContainer(); +Console.WriteLine(BitConverter.IsLittleEndian); -var configuration = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build(); - -services.Register(DependencyStrategy.AtomicInstance, true, configuration); - -ILoggerFactory loggerFactory = LoggerFactory.Create(builder => +var builder = Host.CreateApplicationBuilder(); +builder.Services.AddLogging(); +builder.Services.AddSingleton(); +builder.Services.AddHostedService(provider => + provider.GetService() ?? throw new InvalidOperationException()); +builder.Services.AddMassTransit(x => { - builder.AddSimpleConsole(options => - { - options.ColorBehavior = Microsoft.Extensions.Logging.Console.LoggerColorBehavior.Enabled; - options.TimestampFormat = "[HH:mm:ss]"; - options.IncludeScopes = true; - }); - builder.AddFile("Logs/log-{0:yyyy}-{0:MM}-{0:dd}.txt", - opts => opts.FormatLogFileName = fileName => String.Format(fileName, DateTime.UtcNow)); + x.UsingInMemory((context, configurator) => { configurator.ConfigureEndpoints(context); }); + x.AddMediator(cfg => { cfg.AddConsumers(Assembly.GetExecutingAssembly()); }); }); +builder.Services.AddHostedService(provider => new WonderkingAuthServer(IPAddress.Any, 10001, + provider.GetService>() ?? throw new InvalidOperationException(), + provider.GetService() ?? throw new InvalidOperationException(), + provider.GetService() ?? throw new InvalidOperationException())); -services.Register(DependencyStrategy.AtomicInstance, true, new AesProvider(loggerFactory, null, null)); -services.Register(DependencyStrategy.AtomicInstance, true, loggerFactory); - -var authServer = services.GetInstance(); -authServer.Start(); \ No newline at end of file +using IHost host = builder.Build(); +await host.RunAsync(); \ No newline at end of file diff --git a/Server/Server.csproj b/Server/Server.csproj index 623ede9..b1ca7ed 100644 --- a/Server/Server.csproj +++ b/Server/Server.csproj @@ -5,27 +5,27 @@ net7.0 enable enable + Linux + Server - + + .dockerignore + - - - - - + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + - - - - - - Always - diff --git a/Server/WonderkingAuthServer.cs b/Server/WonderkingAuthServer.cs new file mode 100644 index 0000000..2044e7a --- /dev/null +++ b/Server/WonderkingAuthServer.cs @@ -0,0 +1,70 @@ +using System.Net; +using System.Net.Sockets; +using MassTransit; +using MassTransit.Mediator; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using NetCoreServer; + +namespace Server; + +public class WonderkingAuthServer : TcpServer, IHostedService +{ + private readonly ILogger _logger; + private readonly IServiceProvider _serviceProvider; + private readonly ILoggerFactory _loggerFactory; + + public WonderkingAuthServer(IPAddress address, int port, ILogger logger, + IServiceProvider serviceProvider, ILoggerFactory loggerFactory) : base(address, port) + { + _logger = logger; + _serviceProvider = serviceProvider; + _loggerFactory = loggerFactory; + } + + protected override TcpSession CreateSession() => new WonderkingSession(this, + _serviceProvider.GetService() ?? throw new InvalidOperationException(), + _loggerFactory.CreateLogger(nameof(WonderkingSession))); + + protected override void OnStarting() + { + _logger.LogInformation("Starting"); + base.OnStarting(); + } + + protected override void OnStarted() + { + _logger.LogInformation("Started"); + base.OnStarted(); + } + + protected override void OnStopping() + { + _logger.LogInformation("Stopping"); + base.OnStopping(); + } + + protected override void OnStopped() + { + _logger.LogInformation("Stopped"); + base.OnStopped(); + } + + protected override void OnError(SocketError error) + { + _logger.LogError("An error has occured {Error}", error); + } + + public Task StartAsync(CancellationToken cancellationToken) + { + Start(); + return Task.CompletedTask; + } + + public Task StopAsync(CancellationToken cancellationToken) + { + Stop(); + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Server/WonderkingSession.cs b/Server/WonderkingSession.cs new file mode 100644 index 0000000..f302e46 --- /dev/null +++ b/Server/WonderkingSession.cs @@ -0,0 +1,21 @@ +using MassTransit; +using MassTransit.Mediator; +using Microsoft.Extensions.Logging; +using NetCoreServer; + +namespace Server; + +public class WonderkingSession : AesSession +{ + private readonly IMediator _mediator; + + public WonderkingSession(TcpServer server, IMediator mediator, ILogger logger) : base(server, logger, mediator) + { + _mediator = mediator; + } + + protected override void OnReceived(byte[] buffer, long offset, long size) + { + base.OnReceived(buffer, offset, size); + } +} \ No newline at end of file diff --git a/Server/appsettings.json b/Server/appsettings.json deleted file mode 100644 index 64d4da3..0000000 --- a/Server/appsettings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "auth": { - "port": 10001, - "address": "127.0.0.1" - }, - "logging":{ - } -} \ No newline at end of file diff --git a/qodana.yaml b/qodana.yaml new file mode 100644 index 0000000..b9fb98b --- /dev/null +++ b/qodana.yaml @@ -0,0 +1,29 @@ +#-------------------------------------------------------------------------------# +# Qodana analysis is configured by qodana.yaml file # +# https://www.jetbrains.com/help/qodana/qodana-yaml.html # +#-------------------------------------------------------------------------------# +version: "1.0" + +#Specify inspection profile for code analysis +profile: + name: qodana.starter + +#Enable inspections +#include: +# - name: + +#Disable inspections +#exclude: +# - name: +# paths: +# - + +#Execute shell command before Qodana execution (Applied in CI/CD pipeline) +#bootstrap: sh ./prepare-qodana.sh + +#Install IDE plugins before Qodana execution (Applied in CI/CD pipeline) +#plugins: +# - id: #(plugin id can be found at https://plugins.jetbrains.com) + +#Specify Qodana linter for analysis (Applied in CI/CD pipeline) +linter: jetbrains/qodana-dotnet:latest