Engine.cs 5.0 KB

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