Engine.cs 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. using System.Formats.Asn1;
  2. using System.IO;
  3. using System.Reflection;
  4. using System.Security.Cryptography.X509Certificates;
  5. using InABox.Core;
  6. using InABox.IPC;
  7. using InABox.Logging;
  8. using InABox.Rpc;
  9. using PRSServer;
  10. namespace PRSServices;
  11. public interface IEngine
  12. {
  13. string ServiceName { get; set; }
  14. string Version { get; set; }
  15. void Run();
  16. void Stop();
  17. void Configure(Server settings);
  18. PortStatus[] PortStatusList();
  19. }
  20. public abstract class Engine<TProperties> : IEngine where TProperties : ServerProperties
  21. {
  22. private RpcServerPipeTransport _enginemanager;
  23. public TProperties Properties { get; private set; }
  24. public abstract void Run();
  25. public abstract void Stop();
  26. public virtual PortStatus[] PortStatusList()
  27. {
  28. return new PortStatus[] { };
  29. }
  30. public string ServiceName { get; set; }
  31. public string Version { get; set; }
  32. protected string AppDataFolder { get; set; }
  33. public virtual void Configure(Server server)
  34. {
  35. Properties = server.Properties as TProperties;
  36. AppDataFolder = GetPath(server.Key);
  37. MainLogger.AddLogger(new LogFileLogger(AppDataFolder));
  38. MainLogger.AddLogger(new NamedPipeLogger(server.Key));
  39. _enginemanager = new RpcServerPipeTransport($"{ServiceName}M");
  40. _enginemanager.AddHandler<IEngine, PortStatusCommand, PortStatusParameters, PortStatusResult>(new PortStatusHandler(this));
  41. _enginemanager.AfterMessage += (transport, args) => MainLogger.Send(LogType.Information,"",$"Engine Manager Message: {args.Message?.Command}");
  42. _enginemanager.Start();
  43. }
  44. public static string GetPath(string key)
  45. {
  46. if (Assembly.GetEntryAssembly() != null)
  47. return Path.Combine(
  48. Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
  49. Path.GetFileNameWithoutExtension(Assembly.GetEntryAssembly().Location),
  50. key
  51. );
  52. return Path.Combine(
  53. Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
  54. Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().Location),
  55. key
  56. );
  57. }
  58. public static X509Certificate2? LoadCertificate(string filename)
  59. {
  60. if (!String.IsNullOrWhiteSpace(filename))
  61. {
  62. Logger.Send(LogType.Information, "", "Certificate FileName is {0}", filename);
  63. if (File.Exists(filename))
  64. {
  65. Logger.Send(LogType.Information, "", "Certificate found; verifying HTTPS Certificate");
  66. try
  67. {
  68. var certificate = new X509Certificate2(filename);
  69. if (certificate.NotAfter > DateTime.Now)
  70. {
  71. var names = GetDnsNames(certificate);
  72. Logger.Send(LogType.Information, "", $"Certificate valid for {string.Join(',', names)}");
  73. return certificate;
  74. }
  75. else
  76. {
  77. Logger.Send(LogType.Error, "", "HTTPS Certificate has expired, using HTTP instead");
  78. }
  79. }
  80. catch (Exception)
  81. {
  82. Logger.Send(LogType.Error, "", "Error validating HTTPS Certificate, using HTTP instead");
  83. }
  84. }
  85. else
  86. Logger.Send(LogType.Error, "", "Certificate File does not exist!");
  87. }
  88. return null;
  89. }
  90. public static IEnumerable<string> GetDnsNames(X509Certificate2 certificate)
  91. {
  92. yield return certificate.GetNameInfo(X509NameType.DnsName, false);
  93. foreach (var name in DnsAlternateNames(certificate))
  94. yield return name;
  95. }
  96. public static IEnumerable<string> DnsAlternateNames(X509Certificate2 certificate)
  97. {
  98. // Adapted from https://stackoverflow.com/questions/16698307/how-do-you-parse-the-subject-alternate-names-from-an-x509certificate2/59382929#59382929
  99. // OID for SubjectAlternativeName X509 extension.
  100. const string SAN_OID = "2.5.29.17";
  101. var extension = certificate.Extensions[SAN_OID];
  102. if (extension is null) yield break;
  103. // Tag value "2" is defined by:
  104. //
  105. // dNSName [2] IA5String,
  106. //
  107. // in: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.6
  108. var dnsNameTag = new Asn1Tag(TagClass.ContextSpecific, tagValue: 2, isConstructed: false);
  109. var asnReader = new AsnReader(extension.RawData, AsnEncodingRules.BER);
  110. var sequenceReader = asnReader.ReadSequence(Asn1Tag.Sequence);
  111. while (sequenceReader.HasData)
  112. {
  113. var tag = sequenceReader.PeekTag();
  114. if(tag != dnsNameTag)
  115. {
  116. sequenceReader.ReadEncodedValue();
  117. continue;
  118. }
  119. var dnsName = sequenceReader.ReadCharacterString(UniversalTagNumber.IA5String, dnsNameTag);
  120. yield return dnsName;
  121. }
  122. }
  123. }