mirror of
https://github.com/chylex/Minecraft-Phantom-Panel.git
synced 2025-09-05 22:53:11 +02:00
123 lines
3.7 KiB
C#
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();
|
|
}
|
|
}
|
|
}
|
|
}
|