//****************************************************************************************************** // WindowsServiceClient.cs - Gbtc // // Copyright © 2012, Grid Protection Alliance. All Rights Reserved. // // Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See // the NOTICE file distributed with this work for additional information regarding copyright ownership. // The GPA licenses this file to you under the MIT License (MIT), the "License"; you may // not use this file except in compliance with the License. You may obtain a copy of the License at: // // http://www.opensource.org/licenses/MIT // // Unless agreed to in writing, the subject software distributed under the License is distributed on an // "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the // License for the specific language governing permissions and limitations. // // Code Modification History: // ---------------------------------------------------------------------------------------------------- // 05/16/2011 - Mehulbhai P Thakkar // Generated original version of source code. // 12/20/2012 - Starlynn Danyelle Gilliam // Modified Header. // //****************************************************************************************************** using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Security; using System.Security; using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; using GSF.Communication; using GSF.Net.Security; using GSF.ServiceProcess; namespace GSF.TimeSeries.UI { /// /// Class to connect and communicate with windows service. /// public class WindowsServiceClient : IDisposable { #region [ Members ] // Fields private ClientBase m_remotingClient; private ClientHelper m_clientHelper; private string m_cachedStatus; private readonly int m_statusBufferSize; private bool m_authenticated; private bool m_disposed; #endregion #region [ Constructors ] /// /// Creates an instance of . /// /// Connection information such as server ip address and port where windows service is running. public WindowsServiceClient(string connectionString) { // Initialize status cache members. Dictionary settings = connectionString.ParseKeyValuePairs(); SerializationFormat serializationFormat; string setting; if (settings.TryGetValue("statusBufferSize", out setting) && !string.IsNullOrWhiteSpace(setting)) m_statusBufferSize = int.Parse(setting); else m_statusBufferSize = 8192; m_cachedStatus = string.Empty; // Initialize remoting client socket. if (!settings.TryGetValue("enabledSslProtocols", out setting) || !setting.Equals("None", StringComparison.OrdinalIgnoreCase)) m_remotingClient = InitializeTlsClient(connectionString); else m_remotingClient = InitializeTcpClient(connectionString); // Parse out desired message serialization format, if specified if (!settings.TryGetValue("serializationFormat", out setting) || !Enum.TryParse(setting, true, out serializationFormat)) serializationFormat = SerializationFormat.Binary; // Initialize windows service client. m_clientHelper = new ClientHelper(); m_clientHelper.SerializationFormat = serializationFormat; m_clientHelper.RemotingClient = m_remotingClient; m_clientHelper.ReceivedServiceUpdate += ClientHelper_ReceivedServiceUpdate; m_clientHelper.AuthenticationSuccess += ClientHelper_AuthenticationSuccess; m_clientHelper.AuthenticationFailure += ClientHelper_AuthenticationFailure; } private void ClientHelper_AuthenticationSuccess(object sender, EventArgs e) { m_authenticated = true; } private void ClientHelper_AuthenticationFailure(object sender, System.ComponentModel.CancelEventArgs e) { m_authenticated = false; } /// /// Releases the unmanaged resources before the object is reclaimed by . /// ~WindowsServiceClient() { Dispose(false); } #endregion #region [ Properties ] /// /// Gets reference to ClientHelper object. /// public ClientHelper Helper { get { return m_clientHelper; } } /// /// Gets chached status information to display upon successful connection to windows service. /// public string CachedStatus { get { return m_cachedStatus; } } /// /// Gets or sets authenticated state for service client. /// public bool Authenticated { get { return m_authenticated; } set { m_authenticated = value; } } #endregion #region [ Methods ] /// /// Releases all the resources used by the object. /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// /// Releases the unmanaged resources used by the object and optionally releases the managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { if (!m_disposed) { try { if (disposing) { if ((object)m_clientHelper != null) { m_clientHelper.ReceivedServiceUpdate -= ClientHelper_ReceivedServiceUpdate; m_clientHelper.AuthenticationSuccess -= ClientHelper_AuthenticationSuccess; m_clientHelper.AuthenticationFailure -= ClientHelper_AuthenticationFailure; m_clientHelper.Dispose(); } if ((object)m_remotingClient != null) { m_remotingClient.MaxConnectionAttempts = 0; if (m_remotingClient.CurrentState == ClientState.Connected) m_remotingClient.Disconnect(); m_remotingClient.Dispose(); } } } finally { m_disposed = true; // Prevent duplicate dispose. } } } private TcpClient InitializeTcpClient(string connectionString) { Dictionary settings; string setting; TcpClient remotingClient; // Initialize remoting client socket. remotingClient = new TcpClient(); remotingClient.ConnectionString = connectionString; remotingClient.PayloadAware = true; remotingClient.IgnoreInvalidCredentials = true; remotingClient.MaxConnectionAttempts = -1; // Parse connection string into key-value pairs settings = connectionString.ParseKeyValuePairs(); // See if user wants to connect to remote service using integrated security if (settings.TryGetValue("integratedSecurity", out setting) && !string.IsNullOrWhiteSpace(setting)) remotingClient.IntegratedSecurity = setting.ParseBoolean(); return remotingClient; } private TlsClient InitializeTlsClient(string connectionString) { Dictionary settings; string setting; SimplePolicyChecker certificateChecker; TlsClient remotingClient; SslProtocols enabledSslProtocols; SslPolicyErrors validPolicyErrors; X509ChainStatusFlags validChainFlags; // Initialize remoting client socket. certificateChecker = new SimplePolicyChecker(); remotingClient = new TlsClient(); remotingClient.ConnectionString = connectionString; remotingClient.PayloadAware = true; remotingClient.IgnoreInvalidCredentials = true; remotingClient.MaxConnectionAttempts = -1; remotingClient.RemoteCertificateValidationCallback = RemoteCertificateValidationCallback; remotingClient.CertificateChecker = certificateChecker; // Parse connection string into key-value pairs settings = connectionString.ParseKeyValuePairs(); // See if user wants to connect to remote service using integrated security if (settings.TryGetValue("integratedSecurity", out setting) && !string.IsNullOrWhiteSpace(setting)) remotingClient.IntegratedSecurity = setting.ParseBoolean(); // See if the user has explicitly defined the set of enabled SslProtocols try { if (settings.TryGetValue("enabledSslProtocols", out setting) && Enum.TryParse(setting, true, out enabledSslProtocols)) remotingClient.EnabledSslProtocols = enabledSslProtocols; else remotingClient.EnabledSslProtocols = SslProtocols.Tls12; } catch (SecurityException) { // Security exception can occur when user forces use of older TLS protocol through configuration but event log warning entry cannot be written } // See if the user has explicitly defined valid policy errors or valid chain flags if (settings.TryGetValue("validPolicyErrors", out setting) && Enum.TryParse(setting, true, out validPolicyErrors)) certificateChecker.ValidPolicyErrors = validPolicyErrors; else certificateChecker.ValidPolicyErrors = SslPolicyErrors.RemoteCertificateChainErrors; if (settings.TryGetValue("validChainFlags", out setting) && Enum.TryParse(setting, true, out validChainFlags)) certificateChecker.ValidChainFlags = validChainFlags; else certificateChecker.ValidChainFlags = X509ChainStatusFlags.UntrustedRoot; // See if the user has explicitly defined whether to execute revocation checks on server certificates if (settings.TryGetValue("checkCertificateRevocation", out setting) && !string.IsNullOrWhiteSpace(setting)) remotingClient.CheckCertificateRevocation = setting.ParseBoolean(); return remotingClient; } private bool RemoteCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { TlsClient remotingClient; IPEndPoint remoteEndPoint; IPHostEntry localhost; remotingClient = m_remotingClient as TlsClient; if ((object)remotingClient != null) { remoteEndPoint = remotingClient.Client.RemoteEndPoint as IPEndPoint; if ((object)remoteEndPoint != null) { // Create an exception and do not check policy for localhost localhost = Dns.GetHostEntry("localhost"); if (localhost.AddressList.Any(address => address.Equals(remoteEndPoint.Address))) return true; } // Not connected to localhost, so use the certificate checker return remotingClient.CertificateChecker.ValidateRemoteCertificate(sender, certificate, chain, sslPolicyErrors); } return false; } /// /// Handles ReceivedServiceUpdate event of ClientHelper. /// /// Source of the event. /// Event arguments. private void ClientHelper_ReceivedServiceUpdate(object sender, EventArgs e) { m_cachedStatus = (m_cachedStatus + e.Argument2).TruncateLeft(m_statusBufferSize); } #endregion } }