using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Reflection; using System.Security.Cryptography.X509Certificates; using System.ServiceModel; using System.ServiceModel.Channels; using System.ServiceModel.Discovery; using System.ServiceModel.Security; using System.Threading; using System.Threading.Tasks; using System.Xml; using ArchestrAServices.Common; namespace Asb.Base.V2; internal class ClientManagement : IClientManagement { public ConnectContext CreateConnectContext(EndpointAddress serviceEndpointAddress, Binding binding, string application, string user, CancellationToken cancellationToken) where T : class, IAuthenticateAsb { ConnectContext connectContext = new ConnectContext { Success = false, ErrorMessage = string.Empty, ServiceChannelFactory = null, ServiceClient = null, ServiceChannel = null, ConnectionId = Guid.Empty, ConnectionUser = user, ConnectionApplication = application }; ServiceTrace.LogVerbose("Try connecting with endpoint {0}", serviceEndpointAddress.Uri.AbsoluteUri); connectContext.ServiceChannelFactory = new ChannelFactory(binding, serviceEndpointAddress); if (binding is NetTcpBinding netTcpBinding && netTcpBinding.Security.Mode == SecurityMode.Transport && connectContext.ServiceChannelFactory.Credentials != null) { connectContext.ServiceChannelFactory.Credentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.ChainTrust; connectContext.ServiceChannelFactory.Credentials.ServiceCertificate.Authentication.RevocationMode = X509RevocationMode.NoCheck; } InjectCustomBehavior(connectContext.ServiceChannelFactory); if (!OpenOrAbort(connectContext.ServiceChannelFactory, cancellationToken)) { ServiceTrace.LogVerbose("BaseV2Client CreateConnectContext is cancelled when create channel for {0}", serviceEndpointAddress.Uri.AbsoluteUri); return connectContext; } connectContext.ServiceClient = connectContext.ServiceChannelFactory.CreateChannel(); if (connectContext.ServiceClient == null) { ServiceTrace.LogWarning("BaseV2Client not able to create a client for the endpoint {0}", serviceEndpointAddress.Uri.AbsoluteUri); return null; } connectContext.CastClientToChannel(); if (connectContext.ServiceChannel == null) { ServiceTrace.LogWarning("BaseV2Client not able to create a channel for the endpoint {0}", serviceEndpointAddress.Uri.AbsoluteUri); return null; } if (!OpenOrAbort(connectContext.ServiceChannel, cancellationToken)) { ServiceTrace.LogVerbose("BaseV2Client CreateConnectContext is cancelled when open channel for {0}", serviceEndpointAddress.Uri.AbsoluteUri); } return connectContext; } public virtual void InjectCustomBehavior(ChannelFactory channelFactory) where T : class, IAuthenticateAsb { } public bool EstablishSecureSession(ConnectContext context, string solutionName, ClientAccess access) where T : class, IAuthenticateAsb { if (context?.ServiceChannel == null) { return false; } if (context.ServiceChannel.State != CommunicationState.Faulted) { ServiceTrace.LogVerbose("Channel with endpoint {0} is open, establishing secure session using solution {1}", context.ServiceChannel.RemoteAddress.Uri.AbsoluteUri, solutionName); return context.EstablishSecureSession(solutionName, access); } ServiceTrace.LogVerbose("Secure connection with endpoint {0} is NOT established using solution {1}", context.ServiceChannel.RemoteAddress.Uri.AbsoluteUri, solutionName); context.ServiceChannel.Abort(); context.Dispose(); if (string.IsNullOrEmpty(context.ErrorMessage)) { context.ErrorMessage = "BaseV2Client could not connect with service: EstablishSecureSession failed"; } ServiceTrace.LogWarning(context.ErrorMessage); return false; } public void DisconnectSecureSession(ConnectContext context) where T : class, IAuthenticateAsb { context?.DisconnectSecureSession(); } public IEnumerable DiscoverEndpointsForContract(XmlQualifiedName contract, string scopeRule) { if (contract == null) { throw new ArgumentNullException("contract"); } ServiceTrace.LogCsv("DiscoverEndpointsForContract entry [Contract scopeRule]", contract, string.IsNullOrEmpty(scopeRule) ? "" : scopeRule); ServiceTrace.LogResume("DiscoverEndpointsForContract entry, contract {0}, scopeRule {1}", contract, string.IsNullOrEmpty(scopeRule) ? "" : scopeRule); FindResponse findResponse; try { Uri uri = RegistryHandler.MakeLDSProbeEndpointAddress(Environment.MachineName); ServiceTrace.LogVerbose("DiscoverEndpointsForContract({0}) generated LDS endpoint for localhost: {1}", contract, uri.AbsoluteUri); FindCriteria findCriteria = new FindCriteria(); findCriteria.ContractTypeNames.Add(contract); List list = new List(); if (!string.IsNullOrEmpty(scopeRule)) { list.Add(scopeRule); } Collection collection = SvcUtilities.CreateFindScopes(string.Empty, string.Empty, string.Empty, list); ServiceTrace.LogVerbose("DiscoverEndpointsForContract({0}) generated {1} scope Uris:", contract, collection.Count); findCriteria.Scopes.Clear(); foreach (Uri item in collection) { ServiceTrace.LogVerbose(" {0}", item.AbsoluteUri); findCriteria.Scopes.Add(item); } using DiscoveryClient discoveryClient = new DiscoveryClient(new DiscoveryEndpoint(SvcUtilities.GetBinding(uri.AbsoluteUri), new EndpointAddress(uri))); findResponse = discoveryClient.Find(findCriteria); ServiceTrace.LogVerbose("DiscoverEndpointsForContract({0}) found {1} endpoints", contract, findResponse.Endpoints.Count); } catch (TargetInvocationException ex) { ServiceTrace.LogError("DiscoverEndpointsForContract({0}, {1}) TargetInvocationException: {2}", contract, scopeRule, ex.Message); if (ex.InnerException != null) { ServiceTrace.LogError(" {0}", ex.InnerException.Message); } findResponse = null; } catch (ObjectDisposedException ex2) { ServiceTrace.LogError("DiscoverEndpointsForContract({0}, {1}) ObjectDisposedException: {2}", contract, scopeRule, ex2.Message); if (ex2.InnerException != null) { ServiceTrace.LogError(" {0}", ex2.InnerException.Message); } findResponse = null; } catch (Exception ex3) { ServiceTrace.LogError("DiscoverEndpointsForContract({0}, {1}) Exception: {2}", contract, scopeRule, ex3.Message); if (ex3.InnerException != null) { ServiceTrace.LogError(" {0}", ex3.InnerException.Message); } findResponse = null; } ServiceTrace.LogSuspend("DiscoverEndpointsForContract exit, contract {0}, scopeRule {1}", contract, string.IsNullOrEmpty(scopeRule) ? "" : scopeRule); ServiceTrace.LogCsv("DiscoverEndpointsForContract exit [Contract scopeRule]", contract, string.IsNullOrEmpty(scopeRule) ? "" : scopeRule); return findResponse?.Endpoints; } private static bool OpenOrAbort(ICommunicationObject communicationObject, CancellationToken cancellationToken) { Task task = Task.Factory.StartNew(communicationObject.Open, cancellationToken, TaskCreationOptions.None, TaskScheduler.Default); try { task.Wait(cancellationToken); } catch (OperationCanceledException) { communicationObject.Abort(); return false; } return true; } }