diff --git a/src/ScadaLink.DataConnectionLayer/Actors/DataConnectionActor.cs b/src/ScadaLink.DataConnectionLayer/Actors/DataConnectionActor.cs index b668a4d..02451d9 100644 --- a/src/ScadaLink.DataConnectionLayer/Actors/DataConnectionActor.cs +++ b/src/ScadaLink.DataConnectionLayer/Actors/DataConnectionActor.cs @@ -58,14 +58,18 @@ public class DataConnectionActor : UntypedActor, IWithStash, IWithTimers private int _totalSubscribed; private int _resolvedTags; + private readonly IDictionary _connectionDetails; + public DataConnectionActor( string connectionName, IDataConnection adapter, - DataConnectionOptions options) + DataConnectionOptions options, + IDictionary? connectionDetails = null) { _connectionName = connectionName; _adapter = adapter; _options = options; + _connectionDetails = connectionDetails ?? new Dictionary(); } protected override void PreStart() @@ -204,7 +208,7 @@ public class DataConnectionActor : UntypedActor, IWithStash, IWithTimers { _log.Debug("[{0}] Attempting connection...", _connectionName); var self = Self; - _adapter.ConnectAsync(new Dictionary()).ContinueWith(t => + _adapter.ConnectAsync(_connectionDetails).ContinueWith(t => { if (t.IsCompletedSuccessfully) return new ConnectResult(true, null); diff --git a/src/ScadaLink.DataConnectionLayer/Actors/DataConnectionManagerActor.cs b/src/ScadaLink.DataConnectionLayer/Actors/DataConnectionManagerActor.cs index b1856ad..d7b03d8 100644 --- a/src/ScadaLink.DataConnectionLayer/Actors/DataConnectionManagerActor.cs +++ b/src/ScadaLink.DataConnectionLayer/Actors/DataConnectionManagerActor.cs @@ -44,9 +44,13 @@ public class DataConnectionManagerActor : ReceiveActor var adapter = _factory.Create(command.ProtocolType, command.ConnectionDetails); var props = Props.Create(() => new DataConnectionActor( - command.ConnectionName, adapter, _options)); + command.ConnectionName, adapter, _options, command.ConnectionDetails)); - var actorRef = Context.ActorOf(props, command.ConnectionName); + // Sanitize name for Akka actor path (replace spaces and invalid chars) + var actorName = new string(command.ConnectionName + .Select(c => char.IsLetterOrDigit(c) || "-_.*$+:@&=,!~';()".Contains(c) ? c : '-') + .ToArray()); + var actorRef = Context.ActorOf(props, actorName); _connectionActors[command.ConnectionName] = actorRef; _log.Info("Created DataConnectionActor for {0} (protocol={1})", diff --git a/src/ScadaLink.DataConnectionLayer/Adapters/RealOpcUaClient.cs b/src/ScadaLink.DataConnectionLayer/Adapters/RealOpcUaClient.cs index d891e2a..4673c6b 100644 --- a/src/ScadaLink.DataConnectionLayer/Adapters/RealOpcUaClient.cs +++ b/src/ScadaLink.DataConnectionLayer/Adapters/RealOpcUaClient.cs @@ -26,7 +26,10 @@ public class RealOpcUaClient : IOpcUaClient SecurityConfiguration = new SecurityConfiguration { AutoAcceptUntrustedCertificates = true, - ApplicationCertificate = new CertificateIdentifier() + ApplicationCertificate = new CertificateIdentifier(), + TrustedIssuerCertificates = new CertificateTrustList { StorePath = Path.Combine(Path.GetTempPath(), "ScadaLink", "pki", "issuers") }, + TrustedPeerCertificates = new CertificateTrustList { StorePath = Path.Combine(Path.GetTempPath(), "ScadaLink", "pki", "trusted") }, + RejectedCertificateStore = new CertificateTrustList { StorePath = Path.Combine(Path.GetTempPath(), "ScadaLink", "pki", "rejected") } }, ClientConfiguration = new ClientConfiguration { DefaultSessionTimeout = 60000 }, TransportQuotas = new TransportQuotas { OperationTimeout = 15000 } @@ -35,11 +38,26 @@ public class RealOpcUaClient : IOpcUaClient await appConfig.ValidateAsync(ApplicationType.Client); appConfig.CertificateValidator.CertificateValidation += (_, e) => e.Accept = true; - var endpoint = new EndpointDescription(endpointUrl) + // Discover endpoints from the server, pick the no-security one + EndpointDescription? endpoint; + try { - SecurityMode = MessageSecurityMode.None, - SecurityPolicyUri = SecurityPolicies.None - }; +#pragma warning disable CS0618 + using var discoveryClient = DiscoveryClient.Create(new Uri(endpointUrl)); +#pragma warning restore CS0618 +#pragma warning disable CS0618 + var endpoints = discoveryClient.GetEndpoints(null); +#pragma warning restore CS0618 + endpoint = endpoints + .Where(e => e.SecurityMode == MessageSecurityMode.None) + .FirstOrDefault() ?? endpoints.FirstOrDefault(); + } + catch + { + // Fallback: construct endpoint description manually + endpoint = new EndpointDescription(endpointUrl); + } + var endpointConfig = EndpointConfiguration.Create(appConfig); var configuredEndpoint = new ConfiguredEndpoint(null, endpoint, endpointConfig);