mirror of
https://github.com/chylex/Minecraft-Phantom-Panel.git
synced 2025-09-05 22:53:11 +02:00
101 lines
3.9 KiB
C#
101 lines
3.9 KiB
C#
using System.Net.Security;
|
|
using System.Net.Sockets;
|
|
using System.Security.Authentication;
|
|
using System.Security.Cryptography.X509Certificates;
|
|
using Phantom.Utils.Logging;
|
|
using Serilog;
|
|
|
|
namespace Phantom.Utils.Rpc.New;
|
|
|
|
public sealed class RpcClient<TClientToServerMessage, TServerToClientMessage>(string loggerName, string host, ushort port, string distinguishedName, RpcCertificateThumbprint certificateThumbprint, RpcClientHandshake handshake) {
|
|
private readonly ILogger logger = PhantomLogger.Create<RpcClient<TClientToServerMessage, TServerToClientMessage>>(loggerName);
|
|
|
|
private bool loggedCertificateValidationError = false;
|
|
|
|
private bool ValidateServerCertificate(object sender, X509Certificate? certificate, X509Chain? chain, SslPolicyErrors sslPolicyErrors) {
|
|
if (certificate == null || sslPolicyErrors.HasFlag(SslPolicyErrors.RemoteCertificateNotAvailable)) {
|
|
logger.Error("Could not establish a secure connection, server did not provide a certificate.");
|
|
}
|
|
else if (sslPolicyErrors.HasFlag(SslPolicyErrors.RemoteCertificateNameMismatch)) {
|
|
logger.Error("Could not establish a secure connection, server certificate has the wrong name: {Name}", certificate.Subject);
|
|
}
|
|
else if (!certificateThumbprint.Check(certificate)) {
|
|
logger.Error("Could not establish a secure connection, server certificate does not match.");
|
|
}
|
|
else if (TlsSupport.CheckAlgorithm((X509Certificate2) certificate) is {} error) {
|
|
logger.Error("Could not establish a secure connection, server certificate rejected because it uses {ActualAlgorithmName} instead of {ExpectedAlgorithmName}.", error.ActualAlgorithmName, error.ExpectedAlgorithmName);
|
|
}
|
|
else if ((sslPolicyErrors & ~SslPolicyErrors.RemoteCertificateChainErrors) != SslPolicyErrors.None) {
|
|
logger.Error("Could not establish a secure connection, server certificate validation failed.");
|
|
}
|
|
else {
|
|
return true;
|
|
}
|
|
|
|
loggedCertificateValidationError = true;
|
|
return false;
|
|
}
|
|
|
|
public async Task<RpcClientConnection<TClientToServerMessage>?> Connect(CancellationToken shutdownToken) {
|
|
SslClientAuthenticationOptions sslOptions = new () {
|
|
AllowRenegotiation = false,
|
|
AllowTlsResume = true,
|
|
CertificateRevocationCheckMode = X509RevocationMode.NoCheck,
|
|
EnabledSslProtocols = TlsSupport.SupportedProtocols,
|
|
EncryptionPolicy = EncryptionPolicy.RequireEncryption,
|
|
RemoteCertificateValidationCallback = ValidateServerCertificate,
|
|
TargetHost = distinguishedName,
|
|
};
|
|
|
|
try {
|
|
using var clientSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);
|
|
|
|
logger.Information("Connecting to {Host}:{Port}...", host, port);
|
|
|
|
try {
|
|
await clientSocket.ConnectAsync(host, port, shutdownToken);
|
|
} catch (Exception e) {
|
|
logger.Error(e, "Could not connect.");
|
|
return null;
|
|
}
|
|
|
|
await using var stream = new SslStream(new NetworkStream(clientSocket, ownsSocket: false), leaveInnerStreamOpen: false);
|
|
|
|
try {
|
|
loggedCertificateValidationError = false;
|
|
await stream.AuthenticateAsClientAsync(sslOptions, shutdownToken);
|
|
} catch (AuthenticationException e) {
|
|
if (!loggedCertificateValidationError) {
|
|
logger.Error(e, "Could not establish a secure connection.");
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
logger.Information("Established a secure connection.");
|
|
|
|
try {
|
|
await handshake.AcceptServer(stream, shutdownToken);
|
|
} catch (EndOfStreamException) {
|
|
logger.Warning("Could not perform application handshake, connection lost.");
|
|
return null;
|
|
} catch (Exception e) {
|
|
logger.Warning(e, "Could not perform application handshake.");
|
|
return null;
|
|
}
|
|
// await stream.WriteAsync(new byte[] { 1, 2, 3 }, shutdownToken);
|
|
|
|
byte[] buffer = new byte[1024];
|
|
int readBytes;
|
|
while ((readBytes = await stream.ReadAsync(buffer, shutdownToken)) > 0) {}
|
|
} catch (Exception e) {
|
|
logger.Error(e, "Client crashed with uncaught exception.");
|
|
return null;
|
|
} finally {
|
|
logger.Information("Client stopped.");
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|