123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200 |
- using System.Formats.Asn1;
- using System.IO;
- using System.Reflection;
- using System.Security.Cryptography.X509Certificates;
- using InABox.Clients;
- using InABox.Core;
- using InABox.IPC;
- using InABox.Logging;
- using InABox.Rpc;
- using PRSServer;
- namespace PRSServices;
- public interface IEngine
- {
- string ServiceName { get; set; }
- string Version { get; set; }
- void Run();
- void Stop();
- void Configure(Server settings);
- PortStatus[] PortStatusList();
- }
- public abstract class Engine<TProperties> : IEngine where TProperties : ServerProperties
- {
-
- private RpcServerPipeTransport _enginemanager;
-
- protected RpcClientPipeTransport? Transport;
- public TProperties Properties { get; private set; }
- public abstract void Run();
- public abstract void Stop();
- public virtual PortStatus[] PortStatusList()
- {
- return new PortStatus[] { };
- }
- public string ServiceName { get; set; }
- public string Version { get; set; }
- protected string AppDataFolder { get; set; }
- public virtual void Configure(Server server)
- {
- Properties = server.Properties as TProperties;
- AppDataFolder = GetPath(server.Key);
- MainLogger.AddLogger(new LogFileLogger(AppDataFolder));
- MainLogger.AddLogger(new NamedPipeLogger(server.Key));
-
- _enginemanager = new RpcServerPipeTransport($"{ServiceName}M");
- _enginemanager.AddHandler<IEngine, PortStatusCommand, PortStatusParameters, PortStatusResult>(new PortStatusHandler(this));
- _enginemanager.AfterMessage += (transport, args) => MainLogger.Send(LogType.Information,"",$"Engine Manager Message: {args.Message?.Command}", Guid.Empty);
- _enginemanager.Start();
- }
- private bool _connecting = false;
- protected void CheckConnection()
- {
- if (_connecting)
- {
- return;
- }
- _connecting = true;
- // Wait for server connection
- while (_connecting)
- {
- Logger.Send(LogType.Information, "", "Connecting to Database server...");
- if (Client.Ping())
- _connecting = false;
- else
- {
- Logger.Send(LogType.Error, "", "Database server unavailable. Trying again in 30 seconds...");
- Task.Delay(30_000).Wait();
- }
- }
-
- ClientFactory.SetBypass();
- return;
- }
- /// <summary>
- /// Initialise the client to the database server, and set up reconnection loop.
- /// </summary>
- protected void InitialiseConnection(string serverKey, Platform clientPlatform)
- {
- Transport = new RpcClientPipeTransport(DatabaseServerProperties.GetPipeName(serverKey, true));
- ClientFactory.SetClientType(typeof(RpcClient<>), clientPlatform, Version, Transport);
- Transport.OnClose += Transport_OnClose;
- CheckConnection();
- }
- private void Transport_OnClose(IRpcTransport transport, RpcTransportCloseArgs e)
- {
- // Try to reconnect when lost connection.
- Logger.Send(LogType.Error, "", "Database server connection lost.");
- CheckConnection();
- }
- public static string GetPath(string key)
- {
- if (Assembly.GetEntryAssembly() != null)
- return Path.Combine(
- Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
- Path.GetFileNameWithoutExtension(Assembly.GetEntryAssembly().Location),
- key
- );
- return Path.Combine(
- Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
- Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().Location),
- key
- );
- }
-
- public static X509Certificate2? LoadCertificate(string filename)
- {
- if (!String.IsNullOrWhiteSpace(filename))
- {
- Logger.Send(LogType.Information, "", $"Certificate FileName is {filename}");
- if (File.Exists(filename))
- {
- Logger.Send(LogType.Information, "", "Certificate found; verifying HTTPS Certificate");
- try
- {
- var certificate = new X509Certificate2(filename);
- if (certificate.NotAfter > DateTime.Now)
- {
-
- var names = GetDnsNames(certificate);
- Logger.Send(LogType.Information, "", $"Certificate valid for {string.Join(',', names)}");
- return certificate;
- }
- else
- {
- Logger.Send(LogType.Error, "", "HTTPS Certificate has expired, using HTTP instead");
- }
- }
- catch (Exception)
- {
- Logger.Send(LogType.Error, "", "Error validating HTTPS Certificate, using HTTP instead");
- }
- }
- else
- Logger.Send(LogType.Error, "", "Certificate File does not exist!");
- }
- return null;
- }
- public static IEnumerable<string> GetDnsNames(X509Certificate2 certificate)
- {
- yield return certificate.GetNameInfo(X509NameType.DnsName, false);
- foreach (var name in DnsAlternateNames(certificate))
- yield return name;
- }
- public static IEnumerable<string> DnsAlternateNames(X509Certificate2 certificate)
- {
- // Adapted from https://stackoverflow.com/questions/16698307/how-do-you-parse-the-subject-alternate-names-from-an-x509certificate2/59382929#59382929
- // OID for SubjectAlternativeName X509 extension.
- const string SAN_OID = "2.5.29.17";
- var extension = certificate.Extensions[SAN_OID];
- if (extension is null) yield break;
- // Tag value "2" is defined by:
- //
- // dNSName [2] IA5String,
- //
- // in: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.6
- var dnsNameTag = new Asn1Tag(TagClass.ContextSpecific, tagValue: 2, isConstructed: false);
- var asnReader = new AsnReader(extension.RawData, AsnEncodingRules.BER);
- var sequenceReader = asnReader.ReadSequence(Asn1Tag.Sequence);
- while (sequenceReader.HasData)
- {
- var tag = sequenceReader.PeekTag();
- if(tag != dnsNameTag)
- {
- sequenceReader.ReadEncodedValue();
- continue;
- }
- var dnsName = sequenceReader.ReadCharacterString(UniversalTagNumber.IA5String, dnsNameTag);
- yield return dnsName;
- }
- }
- }
|