1
0
mirror of https://github.com/chylex/Minecraft-Phantom-Panel.git synced 2025-09-05 22:53:11 +02:00
Files

123 lines
3.7 KiB
C#

using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Cryptography.X509Certificates;
using Phantom.Utils.Logging;
using Serilog;
namespace Phantom.Utils.Rpc.New;
public sealed class RpcServer(string loggerName, EndPoint endPoint, RpcServerCertificate certificate, RpcServerHandshake handshake) {
private readonly ILogger logger = PhantomLogger.Create<RpcServer>(loggerName);
private readonly List<Client> clients = [];
public async Task<bool> Run(CancellationToken shutdownToken) {
SslServerAuthenticationOptions sslOptions = new () {
AllowRenegotiation = false,
AllowTlsResume = true,
CertificateRevocationCheckMode = X509RevocationMode.NoCheck,
ClientCertificateRequired = false,
EnabledSslProtocols = TlsSupport.SupportedProtocols,
EncryptionPolicy = EncryptionPolicy.RequireEncryption,
ServerCertificate = certificate.Certificate,
};
try {
using var serverSocket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
try {
serverSocket.Bind(endPoint);
serverSocket.Listen(5);
} catch (Exception e) {
logger.Error(e, "Could not bind to {EndPoint}.", endPoint);
return false;
}
try {
logger.Information("Server listening on {EndPoint}.", endPoint);
while (!shutdownToken.IsCancellationRequested) {
Socket clientSocket = await serverSocket.AcceptAsync(shutdownToken);
clients.Add(new Client(logger, clientSocket, sslOptions, handshake, shutdownToken));
clients.RemoveAll(static client => client.Task.IsCompleted);
}
} finally {
await Stop(serverSocket);
}
} catch (Exception e) {
logger.Error(e, "Server crashed with uncaught exception.");
return false;
}
return true;
}
private async Task Stop(Socket serverSocket) {
try {
serverSocket.Close();
} catch (Exception e) {
logger.Error(e, "Server socket failed to close.");
return;
}
logger.Information("Server socket closed, waiting for client sockets to close.");
try {
await Task.WhenAll(clients.Select(static client => client.Task));
} catch (Exception) {
// Ignore exceptions when shutting down.
}
logger.Information("Server stopped.");
}
private sealed class Client {
public Task Task { get; }
private readonly ILogger logger;
private readonly Socket socket;
private readonly SslServerAuthenticationOptions sslOptions;
private readonly RpcServerHandshake handshake;
private readonly CancellationToken shutdownToken;
public Client(ILogger logger, Socket socket, SslServerAuthenticationOptions sslOptions, RpcServerHandshake handshake, CancellationToken shutdownToken) {
this.logger = logger;
this.socket = socket;
this.sslOptions = sslOptions;
this.handshake = handshake;
this.shutdownToken = shutdownToken;
this.Task = Run();
}
private async Task Run() {
try {
await using var stream = new SslStream(new NetworkStream(socket, ownsSocket: false), leaveInnerStreamOpen: false);
try {
await stream.AuthenticateAsServerAsync(sslOptions, shutdownToken);
} catch (Exception e) {
logger.Error(e, "Could not establish a secure connection.");
return;
}
try {
await handshake.AcceptClient(socket.RemoteEndPoint?.ToString() ?? "<unknown address>", stream, shutdownToken);
} catch (EndOfStreamException) {
logger.Warning("Could not perform application handshake, connection lost.");
return;
} catch (Exception e) {
logger.Warning(e, "Could not perform application handshake.");
return;
}
byte[] buffer = new byte[1024];
int readBytes;
while ((readBytes = await stream.ReadAsync(buffer, shutdownToken)) > 0) {}
} finally {
socket.Close();
}
}
}
}