From 62eedc19be4e6e79ac03cbaeebb0667011406e8a Mon Sep 17 00:00:00 2001 From: Martin Regen Date: Wed, 21 Oct 2020 00:03:37 +0200 Subject: [PATCH] Reduce core dependencies, move https binding to optional assembly. (#1157) * implement transport binding factory * split https transport into seperate nuget package * new Opc.Ua.Binding.Https assembly * Https binding is loaded on demand, if available * reduce core dependencies to kestrel and others fixes #600 --- .azurepipelines/signlistDebug.txt | 5 + .azurepipelines/signlistRelease.txt | 3 + .../ConsoleReferenceClient.csproj | 5 +- .../ConsoleReferenceClient/Program.cs | 2 +- .../NetCoreReferenceServer.csproj | 9 +- .../ReferenceClient/Reference Client.csproj | 4 + .../ReferenceServer/Reference Server.csproj | 5 + .../Opc.Ua.Client.ComplexTypes.csproj | 1 + Libraries/Opc.Ua.Client/Opc.Ua.Client.csproj | 4 - .../Opc.Ua.Client/Properties/AssemblyInfo.cs | 24 + Libraries/Opc.Ua.Server/Opc.Ua.Server.csproj | 8 - .../Opc.Ua.Server/Properties/AssemblyInfo.cs | 24 + .../Opc.Ua.Server/Server/StandardServer.cs | 621 +++++++++--------- .../Opc.Ua.Bindings.Https.csproj | 51 ++ .../Properties/AssemblyInfo.cs | 24 + .../Stack/Https/HttpsServiceHost.cs | 175 +++++ .../Stack/Https/HttpsTransportChannel.cs | 90 +-- .../Stack/Https/HttpsTransportListener.cs} | 35 +- .../Documentation/Opc.Ua.Bindings.cs | 2 +- Stack/Opc.Ua.Core/Opc.Ua.Core.csproj | 17 - .../Security/Constants/SecurityConstants.cs | 7 +- .../Opc.Ua.Core/Stack/Bindings/BaseBinding.cs | 4 +- .../Stack/Bindings/ITransportBindings.cs | 118 ++++ .../Stack/Bindings/TransportBindings.cs | 88 +++ .../Stack/Bindings/TransportBindingsBase.cs | 174 +++++ .../Bindings/TransportListenerBindings.cs | 96 --- .../Stack/Client/ReverseConnectHost.cs | 10 +- .../Opc.Ua.Core/Stack/Client/UaChannelBase.cs | 49 +- Stack/Opc.Ua.Core/Stack/Server/ServerBase.cs | 302 ++------- Stack/Opc.Ua.Core/Stack/Tcp/ChannelQuotas.cs | 2 - .../Opc.Ua.Core/Stack/Tcp/TcpMessageSocket.cs | 6 +- Stack/Opc.Ua.Core/Stack/Tcp/TcpServiceHost.cs | 142 ++++ ...TcpListener.cs => TcpTransportListener.cs} | 19 +- .../Stack/Transport/ITransportChannel.cs | 12 - .../Transport/TransportListenerSettings.cs | 1 - .../Types/Encoders/EncodeableFactory.cs | 2 +- Stack/Opc.Ua.Core/Types/Utils/Utils.cs | 44 +- .../Opc.Ua.Core.Tests.csproj | 1 + .../Opc.Ua.Gds.Tests/Opc.Ua.Gds.Tests.csproj | 1 + UA Core Library.sln | 6 + UA Reference.sln | 18 +- nuget/Opc.Ua.Symbols.nuspec | 1 + nuget/Opc.Ua.nuspec | 1 + 43 files changed, 1386 insertions(+), 827 deletions(-) create mode 100644 Libraries/Opc.Ua.Client/Properties/AssemblyInfo.cs create mode 100644 Libraries/Opc.Ua.Server/Properties/AssemblyInfo.cs create mode 100644 Stack/Opc.Ua.Bindings.Https/Opc.Ua.Bindings.Https.csproj create mode 100644 Stack/Opc.Ua.Bindings.Https/Properties/AssemblyInfo.cs create mode 100644 Stack/Opc.Ua.Bindings.Https/Stack/Https/HttpsServiceHost.cs rename Stack/{Opc.Ua.Core => Opc.Ua.Bindings.Https}/Stack/Https/HttpsTransportChannel.cs (85%) rename Stack/{Opc.Ua.Core/Stack/Https/HttpsListener.cs => Opc.Ua.Bindings.Https/Stack/Https/HttpsTransportListener.cs} (93%) create mode 100644 Stack/Opc.Ua.Core/Stack/Bindings/ITransportBindings.cs create mode 100644 Stack/Opc.Ua.Core/Stack/Bindings/TransportBindings.cs create mode 100644 Stack/Opc.Ua.Core/Stack/Bindings/TransportBindingsBase.cs delete mode 100644 Stack/Opc.Ua.Core/Stack/Bindings/TransportListenerBindings.cs create mode 100644 Stack/Opc.Ua.Core/Stack/Tcp/TcpServiceHost.cs rename Stack/Opc.Ua.Core/Stack/Tcp/{TcpListener.cs => TcpTransportListener.cs} (97%) diff --git a/.azurepipelines/signlistDebug.txt b/.azurepipelines/signlistDebug.txt index 6dc42d3c09..2c4f289024 100644 --- a/.azurepipelines/signlistDebug.txt +++ b/.azurepipelines/signlistDebug.txt @@ -1,6 +1,9 @@ Stack\Opc.Ua.Core\bin\Debug\netstandard2.1\Opc.Ua.Core.dll Stack\Opc.Ua.Core\bin\Debug\netstandard2.0\Opc.Ua.Core.dll Stack\Opc.Ua.Core\bin\Debug\net462\Opc.Ua.Core.dll +Stack\Opc.Ua.Bindings.Https\bin\Debug\netstandard2.1\Opc.Ua.Bindings.Https.dll +Stack\Opc.Ua.Bindings.Https\bin\Debug\netstandard2.0\Opc.Ua.Bindings.Https.dll +Stack\Opc.Ua.Bindings.Https\bin\Debug\net462\Opc.Ua.Bindings.Https.dll Libraries\Opc.Ua.Server\bin\Debug\netstandard2.1\Opc.Ua.Server.dll Libraries\Opc.Ua.Server\bin\Debug\netstandard2.0\Opc.Ua.Server.dll Libraries\Opc.Ua.Server\bin\Debug\net462\Opc.Ua.Server.dll @@ -19,3 +22,5 @@ Libraries\Opc.Ua.Gds.Client.Common\bin\Debug\net462\Opc.Ua.Gds.Client.Common.dll Libraries\Opc.Ua.Gds.Server.Common\bin\Debug\netstandard2.1\Opc.Ua.Gds.Server.Common.dll Libraries\Opc.Ua.Gds.Server.Common\bin\Debug\netstandard2.0\Opc.Ua.Gds.Server.Common.dll Libraries\Opc.Ua.Gds.Server.Common\bin\Debug\net462\Opc.Ua.Gds.Server.Common.dll + + diff --git a/.azurepipelines/signlistRelease.txt b/.azurepipelines/signlistRelease.txt index 63e5fa9fb4..ed5b2e403f 100644 --- a/.azurepipelines/signlistRelease.txt +++ b/.azurepipelines/signlistRelease.txt @@ -1,6 +1,9 @@ Stack\Opc.Ua.Core\bin\Release\netstandard2.1\Opc.Ua.Core.dll Stack\Opc.Ua.Core\bin\Release\netstandard2.0\Opc.Ua.Core.dll Stack\Opc.Ua.Core\bin\Release\net462\Opc.Ua.Core.dll +Stack\Opc.Ua.Bindings.Https\bin\Release\netstandard2.1\Opc.Ua.Bindings.Https.dll +Stack\Opc.Ua.Bindings.Https\bin\Release\netstandard2.0\Opc.Ua.Bindings.Https.dll +Stack\Opc.Ua.Bindings.Https\bin\Release\net462\Opc.Ua.Bindings.Https.dll Libraries\Opc.Ua.Server\bin\Release\netstandard2.1\Opc.Ua.Server.dll Libraries\Opc.Ua.Server\bin\Release\netstandard2.0\Opc.Ua.Server.dll Libraries\Opc.Ua.Server\bin\Release\net462\Opc.Ua.Server.dll diff --git a/Applications/ConsoleReferenceClient/ConsoleReferenceClient.csproj b/Applications/ConsoleReferenceClient/ConsoleReferenceClient.csproj index 52b459ed8a..cc6f323d01 100644 --- a/Applications/ConsoleReferenceClient/ConsoleReferenceClient.csproj +++ b/Applications/ConsoleReferenceClient/ConsoleReferenceClient.csproj @@ -6,12 +6,15 @@ Exe ConsoleReferenceClient OPC Foundation - OPC UA SDK .NET Console Reference Client Copyright © 2004-2020 OPC Foundation, Inc Quickstarts.ConsoleReferenceClient + + + + diff --git a/Applications/ConsoleReferenceClient/Program.cs b/Applications/ConsoleReferenceClient/Program.cs index d6e983961b..0f1d4d12e2 100644 --- a/Applications/ConsoleReferenceClient/Program.cs +++ b/Applications/ConsoleReferenceClient/Program.cs @@ -33,7 +33,7 @@ using Opc.Ua.Configuration; namespace Quickstarts.ConsoleReferenceClient { - public class Program + public static class Program { public static void Main(string[] args) { diff --git a/Applications/ConsoleReferenceServer/NetCoreReferenceServer.csproj b/Applications/ConsoleReferenceServer/NetCoreReferenceServer.csproj index 2fee8b2a85..8adc3ca952 100644 --- a/Applications/ConsoleReferenceServer/NetCoreReferenceServer.csproj +++ b/Applications/ConsoleReferenceServer/NetCoreReferenceServer.csproj @@ -6,7 +6,6 @@ Exe ConsoleReferenceServer OPC Foundation - OPC UA SDK .NET Core Reference Server Copyright © 2004-2020 OPC Foundation, Inc @@ -19,6 +18,10 @@ + + + + @@ -35,8 +38,4 @@ - - - - diff --git a/Applications/ReferenceClient/Reference Client.csproj b/Applications/ReferenceClient/Reference Client.csproj index 45b8b50b53..fc349d942a 100644 --- a/Applications/ReferenceClient/Reference Client.csproj +++ b/Applications/ReferenceClient/Reference Client.csproj @@ -120,6 +120,10 @@ + + {92d98d3d-2a7d-4f4b-8c72-1a98908d0221} + Opc.Ua.Bindings.Https + {4569aaa2-710b-4e8b-a4c6-f917777814da} Opc.Ua.Core diff --git a/Applications/ReferenceServer/Reference Server.csproj b/Applications/ReferenceServer/Reference Server.csproj index 55e4a00fd9..f8e175e6d4 100644 --- a/Applications/ReferenceServer/Reference Server.csproj +++ b/Applications/ReferenceServer/Reference Server.csproj @@ -67,6 +67,7 @@ + @@ -134,6 +135,10 @@ + + {92d98d3d-2a7d-4f4b-8c72-1a98908d0221} + Opc.Ua.Bindings.Https + {4569aaa2-710b-4e8b-a4c6-f917777814da} Opc.Ua.Core diff --git a/Libraries/Opc.Ua.Client.ComplexTypes/Opc.Ua.Client.ComplexTypes.csproj b/Libraries/Opc.Ua.Client.ComplexTypes/Opc.Ua.Client.ComplexTypes.csproj index 1741297670..b338cbc5a1 100644 --- a/Libraries/Opc.Ua.Client.ComplexTypes/Opc.Ua.Client.ComplexTypes.csproj +++ b/Libraries/Opc.Ua.Client.ComplexTypes/Opc.Ua.Client.ComplexTypes.csproj @@ -26,6 +26,7 @@ + diff --git a/Libraries/Opc.Ua.Client/Opc.Ua.Client.csproj b/Libraries/Opc.Ua.Client/Opc.Ua.Client.csproj index 62e7599d26..f893ce0cda 100644 --- a/Libraries/Opc.Ua.Client/Opc.Ua.Client.csproj +++ b/Libraries/Opc.Ua.Client/Opc.Ua.Client.csproj @@ -19,10 +19,6 @@ - - - - diff --git a/Libraries/Opc.Ua.Client/Properties/AssemblyInfo.cs b/Libraries/Opc.Ua.Client/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..dc0263bfa2 --- /dev/null +++ b/Libraries/Opc.Ua.Client/Properties/AssemblyInfo.cs @@ -0,0 +1,24 @@ +/* Copyright (c) 1996-2020 The OPC Foundation. All rights reserved. + The source code in this file is covered under a dual-license scenario: + - RCL: for OPC Foundation members in good-standing + - GPL V2: everybody else + RCL license terms accompanied with this source code. See http://opcfoundation.org/License/RCL/1.00/ + GNU General Public License as published by the Free Software Foundation; + version 2 of the License are accompanied with this source code. See http://opcfoundation.org/License/GPLv2 + This source code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +*/ +using System.Runtime.CompilerServices; + +#if SIGNASSEMBLY +[assembly: InternalsVisibleTo("Opc.Ua.Client.Tests, PublicKey = " + + // OPC Foundation Strong Name Public Key + "0024000004800000940000000602000000240000525341310004000001000100d987b12f068b35" + + "80429f3dde01397508880fc7e62621397618456ca1549aeacfbdb90c62adfe918f05ce3677b390" + + "f78357b8745cb6e1334655afce1a9527ac92fc829ff585ea79f007e52ba0f83ead627e3edda40b" + + "ec5ae574128fc9342cb57cb8285aa4e5b589c0ebef3be571b5c8f2ab1067f7c880e8f8882a73c8" + + "0a12a1ef")] +#else +[assembly: InternalsVisibleTo("Opc.Ua.Client.Tests")] +#endif diff --git a/Libraries/Opc.Ua.Server/Opc.Ua.Server.csproj b/Libraries/Opc.Ua.Server/Opc.Ua.Server.csproj index ce6241b356..ae097b7729 100644 --- a/Libraries/Opc.Ua.Server/Opc.Ua.Server.csproj +++ b/Libraries/Opc.Ua.Server/Opc.Ua.Server.csproj @@ -16,17 +16,9 @@ $(PackageId).Debug - - $(DefineConstants);NO_HTTPS - - - - - - diff --git a/Libraries/Opc.Ua.Server/Properties/AssemblyInfo.cs b/Libraries/Opc.Ua.Server/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..554126623d --- /dev/null +++ b/Libraries/Opc.Ua.Server/Properties/AssemblyInfo.cs @@ -0,0 +1,24 @@ +/* Copyright (c) 1996-2020 The OPC Foundation. All rights reserved. + The source code in this file is covered under a dual-license scenario: + - RCL: for OPC Foundation members in good-standing + - GPL V2: everybody else + RCL license terms accompanied with this source code. See http://opcfoundation.org/License/RCL/1.00/ + GNU General Public License as published by the Free Software Foundation; + version 2 of the License are accompanied with this source code. See http://opcfoundation.org/License/GPLv2 + This source code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +*/ +using System.Runtime.CompilerServices; + +#if SIGNASSEMBLY +[assembly: InternalsVisibleTo("Opc.Ua.Server.Tests, PublicKey = " + + // OPC Foundation Strong Name Public Key + "0024000004800000940000000602000000240000525341310004000001000100d987b12f068b35" + + "80429f3dde01397508880fc7e62621397618456ca1549aeacfbdb90c62adfe918f05ce3677b390" + + "f78357b8745cb6e1334655afce1a9527ac92fc829ff585ea79f007e52ba0f83ead627e3edda40b" + + "ec5ae574128fc9342cb57cb8285aa4e5b589c0ebef3be571b5c8f2ab1067f7c880e8f8882a73c8" + + "0a12a1ef")] +#else +[assembly: InternalsVisibleTo("Opc.Ua.Server.Tests")] +#endif diff --git a/Libraries/Opc.Ua.Server/Server/StandardServer.cs b/Libraries/Opc.Ua.Server/Server/StandardServer.cs index 486c3a7d23..ec992f9dd4 100644 --- a/Libraries/Opc.Ua.Server/Server/StandardServer.cs +++ b/Libraries/Opc.Ua.Server/Server/StandardServer.cs @@ -33,6 +33,7 @@ using System.IO; using System.Security.Cryptography.X509Certificates; using System.Threading; using System.Threading.Tasks; +using Opc.Ua.Bindings; namespace Opc.Ua.Server { @@ -51,7 +52,7 @@ namespace Opc.Ua.Server } #endregion - + #region IDisposable Members /// /// An overrideable version of the Dispose. @@ -59,7 +60,7 @@ namespace Opc.Ua.Server /// true to release both managed and unmanaged resources; false to release only unmanaged resources. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed", MessageId = "m_serverInternal"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed", MessageId = "m_registrationTimer"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed", MessageId = "m_configurationWatcher")] protected override void Dispose(bool disposing) - { + { if (disposing) { // halt any outstanding timer. @@ -68,14 +69,14 @@ namespace Opc.Ua.Server Utils.SilentDispose(m_registrationTimer); m_registrationTimer = null; } - + // close the watcher. if (m_configurationWatcher != null) { Utils.SilentDispose(m_configurationWatcher); m_configurationWatcher = null; } - + // close the server. if (m_serverInternal != null) { @@ -101,16 +102,16 @@ namespace Opc.Ua.Server /// Returns a object /// public override ResponseHeader FindServers( - RequestHeader requestHeader, - string endpointUrl, - StringCollection localeIds, - StringCollection serverUris, + RequestHeader requestHeader, + string endpointUrl, + StringCollection localeIds, + StringCollection serverUris, out ApplicationDescriptionCollection servers) { servers = new ApplicationDescriptionCollection(); - + ValidateRequest(requestHeader); - + lock (m_lock) { // parse the url provided by the client. @@ -131,7 +132,7 @@ namespace Opc.Ua.Server } // build list of unique servers. - Dictionary uniqueServers = new Dictionary(); + Dictionary uniqueServers = new Dictionary(); foreach (EndpointDescription description in GetEndpoints()) { @@ -173,7 +174,7 @@ namespace Opc.Ua.Server servers.Add(application); } } - + return CreateResponse(requestHeader, StatusCodes.Good); } @@ -189,14 +190,14 @@ namespace Opc.Ua.Server /// Returns a object /// public override ResponseHeader GetEndpoints( - RequestHeader requestHeader, - string endpointUrl, - StringCollection localeIds, - StringCollection profileUris, + RequestHeader requestHeader, + string endpointUrl, + StringCollection localeIds, + StringCollection profileUris, out EndpointDescriptionCollection endpoints) - { + { endpoints = null; - + ValidateRequest(requestHeader); lock (m_lock) @@ -286,25 +287,25 @@ namespace Opc.Ua.Server /// Returns a object /// public override ResponseHeader CreateSession( - RequestHeader requestHeader, - ApplicationDescription clientDescription, - string serverUri, - string endpointUrl, - string sessionName, - byte[] clientNonce, - byte[] clientCertificate, - double requestedSessionTimeout, - uint maxResponseMessageSize, - out NodeId sessionId, - out NodeId authenticationToken, - out double revisedSessionTimeout, - out byte[] serverNonce, - out byte[] serverCertificate, - out EndpointDescriptionCollection serverEndpoints, + RequestHeader requestHeader, + ApplicationDescription clientDescription, + string serverUri, + string endpointUrl, + string sessionName, + byte[] clientNonce, + byte[] clientCertificate, + double requestedSessionTimeout, + uint maxResponseMessageSize, + out NodeId sessionId, + out NodeId authenticationToken, + out double revisedSessionTimeout, + out byte[] serverNonce, + out byte[] serverCertificate, + out EndpointDescriptionCollection serverEndpoints, out SignedSoftwareCertificateCollection serverSoftwareCertificates, - out SignatureData serverSignature, - out uint maxRequestMessageSize) - { + out SignatureData serverSignature, + out uint maxRequestMessageSize) + { sessionId = 0; revisedSessionTimeout = 0; serverNonce = null; @@ -314,7 +315,7 @@ namespace Opc.Ua.Server maxRequestMessageSize = (uint)MessageContext.MaxMessageSize; OperationContext context = ValidateRequest(requestHeader, RequestType.CreateSession); - + try { // check the server uri. @@ -466,7 +467,7 @@ namespace Opc.Ua.Server } throw TranslateException((DiagnosticsMasks)requestHeader.ReturnDiagnostics, new StringCollection(), e); - } + } finally { OnRequestComplete(context); @@ -489,31 +490,31 @@ namespace Opc.Ua.Server /// Returns a object /// public override ResponseHeader ActivateSession( - RequestHeader requestHeader, - SignatureData clientSignature, + RequestHeader requestHeader, + SignatureData clientSignature, SignedSoftwareCertificateCollection clientSoftwareCertificates, - StringCollection localeIds, - ExtensionObject userIdentityToken, - SignatureData userTokenSignature, - out byte[] serverNonce, - out StatusCodeCollection results, - out DiagnosticInfoCollection diagnosticInfos) - { + StringCollection localeIds, + ExtensionObject userIdentityToken, + SignatureData userTokenSignature, + out byte[] serverNonce, + out StatusCodeCollection results, + out DiagnosticInfoCollection diagnosticInfos) + { serverNonce = null; results = null; diagnosticInfos = null; - + OperationContext context = ValidateRequest(requestHeader, RequestType.ActivateSession); - + try { // validate client's software certificates. List softwareCertificates = new List(); - + if (context.SecurityPolicyUri != SecurityPolicies.None) { bool diagnosticsExist = false; - + if ((context.DiagnosticsMask & DiagnosticsMasks.OperationAll) != 0) { diagnosticInfos = new DiagnosticInfoCollection(); @@ -534,7 +535,7 @@ namespace Opc.Ua.Server if (ServiceResult.IsBad(result)) { results.Add(result.Code); - + // add diagnostics if requested. if ((context.DiagnosticsMask & DiagnosticsMasks.OperationAll) != 0) { @@ -547,7 +548,7 @@ namespace Opc.Ua.Server { softwareCertificates.Add(softwareCertificate); results.Add(StatusCodes.Good); - + // add diagnostics if requested. if ((context.DiagnosticsMask & DiagnosticsMasks.OperationAll) != 0) { @@ -561,10 +562,10 @@ namespace Opc.Ua.Server diagnosticInfos.Clear(); } } - + // check if certificates meet the server's requirements. ValidateSoftwareCertificates(softwareCertificates); - + // activate the session. bool identityChanged = ServerInternal.SessionManager.ActivateSession( context, @@ -606,7 +607,7 @@ namespace Opc.Ua.Server finally { OnRequestComplete(context); - } + } } /// @@ -704,11 +705,11 @@ namespace Opc.Ua.Server } throw TranslateException(context, e); - } + } finally { OnRequestComplete(context); - } + } } /// @@ -722,16 +723,16 @@ namespace Opc.Ua.Server /// public override ResponseHeader Cancel( RequestHeader requestHeader, - uint requestHandle, - out uint cancelCount) + uint requestHandle, + out uint cancelCount) { cancelCount = 0; OperationContext context = ValidateRequest(requestHeader, RequestType.Cancel); - + try { - m_serverInternal.RequestManager.CancelRequests(requestHandle, out cancelCount); + m_serverInternal.RequestManager.CancelRequests(requestHandle, out cancelCount); return CreateResponse(requestHeader, context.StringTable); } catch (ServiceResultException e) @@ -747,11 +748,11 @@ namespace Opc.Ua.Server } throw TranslateException(context, e); - } + } finally { OnRequestComplete(context); - } + } } /// @@ -767,11 +768,11 @@ namespace Opc.Ua.Server /// Returns a object /// public override ResponseHeader Browse( - RequestHeader requestHeader, - ViewDescription view, - uint requestedMaxReferencesPerNode, - BrowseDescriptionCollection nodesToBrowse, - out BrowseResultCollection results, + RequestHeader requestHeader, + ViewDescription view, + uint requestedMaxReferencesPerNode, + BrowseDescriptionCollection nodesToBrowse, + out BrowseResultCollection results, out DiagnosticInfoCollection diagnosticInfos) { results = null; @@ -809,11 +810,11 @@ namespace Opc.Ua.Server } throw TranslateException(context, e); - } + } finally { OnRequestComplete(context); - } + } } /// @@ -828,17 +829,17 @@ namespace Opc.Ua.Server /// Returns a object /// public override ResponseHeader BrowseNext( - RequestHeader requestHeader, - bool releaseContinuationPoints, - ByteStringCollection continuationPoints, - out BrowseResultCollection results, + RequestHeader requestHeader, + bool releaseContinuationPoints, + ByteStringCollection continuationPoints, + out BrowseResultCollection results, out DiagnosticInfoCollection diagnosticInfos) - { + { results = null; diagnosticInfos = null; - + OperationContext context = ValidateRequest(requestHeader, RequestType.BrowseNext); - + try { if (continuationPoints == null || continuationPoints.Count == 0) @@ -853,7 +854,7 @@ namespace Opc.Ua.Server out results, out diagnosticInfos); - return CreateResponse(requestHeader, context.StringTable); + return CreateResponse(requestHeader, context.StringTable); } catch (ServiceResultException e) { @@ -868,11 +869,11 @@ namespace Opc.Ua.Server } throw TranslateException(context, e); - } + } finally { OnRequestComplete(context); - } + } } /// @@ -885,8 +886,8 @@ namespace Opc.Ua.Server /// Returns a object /// public override ResponseHeader RegisterNodes( - RequestHeader requestHeader, - NodeIdCollection nodesToRegister, + RequestHeader requestHeader, + NodeIdCollection nodesToRegister, out NodeIdCollection registeredNodeIds) { registeredNodeIds = null; @@ -924,7 +925,7 @@ namespace Opc.Ua.Server finally { OnRequestComplete(context); - } + } } /// @@ -969,7 +970,7 @@ namespace Opc.Ua.Server finally { OnRequestComplete(context); - } + } } /// @@ -983,10 +984,10 @@ namespace Opc.Ua.Server /// Returns a object /// public override ResponseHeader TranslateBrowsePathsToNodeIds( - RequestHeader requestHeader, - BrowsePathCollection browsePaths, - out BrowsePathResultCollection results, - out DiagnosticInfoCollection diagnosticInfos) + RequestHeader requestHeader, + BrowsePathCollection browsePaths, + out BrowsePathResultCollection results, + out DiagnosticInfoCollection diagnosticInfos) { results = null; diagnosticInfos = null; @@ -1006,7 +1007,7 @@ namespace Opc.Ua.Server out results, out diagnosticInfos); - return CreateResponse(requestHeader, context.StringTable); + return CreateResponse(requestHeader, context.StringTable); } catch (ServiceResultException e) { @@ -1021,11 +1022,11 @@ namespace Opc.Ua.Server } throw TranslateException(context, e); - } + } finally { OnRequestComplete(context); - } + } } /// @@ -1041,11 +1042,11 @@ namespace Opc.Ua.Server /// Returns a object /// public override ResponseHeader Read( - RequestHeader requestHeader, - double maxAge, - TimestampsToReturn timestampsToReturn, - ReadValueIdCollection nodesToRead, - out DataValueCollection results, + RequestHeader requestHeader, + double maxAge, + TimestampsToReturn timestampsToReturn, + ReadValueIdCollection nodesToRead, + out DataValueCollection results, out DiagnosticInfoCollection diagnosticInfos) { OperationContext context = ValidateRequest(requestHeader, RequestType.Read); @@ -1065,7 +1066,7 @@ namespace Opc.Ua.Server out results, out diagnosticInfos); - return CreateResponse(requestHeader, context.StringTable); + return CreateResponse(requestHeader, context.StringTable); } catch (ServiceResultException e) { @@ -1080,11 +1081,11 @@ namespace Opc.Ua.Server } throw TranslateException(context, e); - } + } finally { OnRequestComplete(context); - } + } } /// @@ -1101,20 +1102,20 @@ namespace Opc.Ua.Server /// Returns a object /// public override ResponseHeader HistoryRead( - RequestHeader requestHeader, - ExtensionObject historyReadDetails, - TimestampsToReturn timestampsToReturn, - bool releaseContinuationPoints, - HistoryReadValueIdCollection nodesToRead, - out HistoryReadResultCollection results, - out DiagnosticInfoCollection diagnosticInfos) + RequestHeader requestHeader, + ExtensionObject historyReadDetails, + TimestampsToReturn timestampsToReturn, + bool releaseContinuationPoints, + HistoryReadValueIdCollection nodesToRead, + out HistoryReadResultCollection results, + out DiagnosticInfoCollection diagnosticInfos) { OperationContext context = ValidateRequest(requestHeader, RequestType.HistoryRead); - + try { if (nodesToRead == null || nodesToRead.Count == 0) - { + { throw new ServiceResultException(StatusCodes.BadNothingToDo); } @@ -1125,9 +1126,9 @@ namespace Opc.Ua.Server releaseContinuationPoints, nodesToRead, out results, - out diagnosticInfos); + out diagnosticInfos); - return CreateResponse(requestHeader, context.StringTable); + return CreateResponse(requestHeader, context.StringTable); } catch (ServiceResultException e) { @@ -1142,11 +1143,11 @@ namespace Opc.Ua.Server } throw TranslateException(context, e); - } + } finally { OnRequestComplete(context); - } + } } /// @@ -1160,9 +1161,9 @@ namespace Opc.Ua.Server /// Returns a object /// public override ResponseHeader Write( - RequestHeader requestHeader, - WriteValueCollection nodesToWrite, - out StatusCodeCollection results, + RequestHeader requestHeader, + WriteValueCollection nodesToWrite, + out StatusCodeCollection results, out DiagnosticInfoCollection diagnosticInfos) { OperationContext context = ValidateRequest(requestHeader, RequestType.Write); @@ -1195,7 +1196,7 @@ namespace Opc.Ua.Server } throw TranslateException(context, e); - } + } finally { OnRequestComplete(context); @@ -1213,17 +1214,17 @@ namespace Opc.Ua.Server /// Returns a object /// public override ResponseHeader HistoryUpdate( - RequestHeader requestHeader, - ExtensionObjectCollection historyUpdateDetails, + RequestHeader requestHeader, + ExtensionObjectCollection historyUpdateDetails, out HistoryUpdateResultCollection results, - out DiagnosticInfoCollection diagnosticInfos) - { + out DiagnosticInfoCollection diagnosticInfos) + { OperationContext context = ValidateRequest(requestHeader, RequestType.HistoryUpdate); - + try { if (historyUpdateDetails == null || historyUpdateDetails.Count == 0) - { + { throw new ServiceResultException(StatusCodes.BadNothingToDo); } @@ -1231,9 +1232,9 @@ namespace Opc.Ua.Server context, historyUpdateDetails, out results, - out diagnosticInfos); + out diagnosticInfos); - return CreateResponse(requestHeader, context.StringTable); + return CreateResponse(requestHeader, context.StringTable); } catch (ServiceResultException e) { @@ -1248,7 +1249,7 @@ namespace Opc.Ua.Server } throw TranslateException(context, e); - } + } finally { OnRequestComplete(context); @@ -1274,16 +1275,16 @@ namespace Opc.Ua.Server /// public override ResponseHeader CreateSubscription( RequestHeader requestHeader, - double requestedPublishingInterval, - uint requestedLifetimeCount, - uint requestedMaxKeepAliveCount, - uint maxNotificationsPerPublish, - bool publishingEnabled, - byte priority, - out uint subscriptionId, - out double revisedPublishingInterval, - out uint revisedLifetimeCount, - out uint revisedMaxKeepAliveCount) + double requestedPublishingInterval, + uint requestedLifetimeCount, + uint requestedMaxKeepAliveCount, + uint maxNotificationsPerPublish, + bool publishingEnabled, + byte priority, + out uint subscriptionId, + out double revisedPublishingInterval, + out uint revisedLifetimeCount, + out uint revisedMaxKeepAliveCount) { OperationContext context = ValidateRequest(requestHeader, RequestType.CreateSubscription); @@ -1300,9 +1301,9 @@ namespace Opc.Ua.Server out subscriptionId, out revisedPublishingInterval, out revisedLifetimeCount, - out revisedMaxKeepAliveCount); + out revisedMaxKeepAliveCount); - return CreateResponse(requestHeader, context.StringTable); + return CreateResponse(requestHeader, context.StringTable); } catch (ServiceResultException e) { @@ -1317,11 +1318,11 @@ namespace Opc.Ua.Server } throw TranslateException(context, e); - } + } finally { OnRequestComplete(context); - } + } } /// @@ -1335,9 +1336,9 @@ namespace Opc.Ua.Server /// Returns a object /// public override ResponseHeader DeleteSubscriptions( - RequestHeader requestHeader, - UInt32Collection subscriptionIds, - out StatusCodeCollection results, + RequestHeader requestHeader, + UInt32Collection subscriptionIds, + out StatusCodeCollection results, out DiagnosticInfoCollection diagnosticInfos) { OperationContext context = ValidateRequest(requestHeader, RequestType.DeleteSubscriptions); @@ -1347,15 +1348,15 @@ namespace Opc.Ua.Server if (subscriptionIds == null || subscriptionIds.Count == 0) { throw new ServiceResultException(StatusCodes.BadNothingToDo); - } + } ServerInternal.SubscriptionManager.DeleteSubscriptions( context, subscriptionIds, out results, - out diagnosticInfos); + out diagnosticInfos); - return CreateResponse(requestHeader, context.StringTable); + return CreateResponse(requestHeader, context.StringTable); } catch (ServiceResultException e) { @@ -1370,11 +1371,11 @@ namespace Opc.Ua.Server } throw TranslateException(context, e); - } + } finally { OnRequestComplete(context); - } + } } /// @@ -1392,14 +1393,14 @@ namespace Opc.Ua.Server /// Returns a object /// public override ResponseHeader Publish( - RequestHeader requestHeader, - SubscriptionAcknowledgementCollection subscriptionAcknowledgements, - out uint subscriptionId, - out UInt32Collection availableSequenceNumbers, - out bool moreNotifications, - out NotificationMessage notificationMessage, - out StatusCodeCollection results, - out DiagnosticInfoCollection diagnosticInfos) + RequestHeader requestHeader, + SubscriptionAcknowledgementCollection subscriptionAcknowledgements, + out uint subscriptionId, + out UInt32Collection availableSequenceNumbers, + out bool moreNotifications, + out NotificationMessage notificationMessage, + out StatusCodeCollection results, + out DiagnosticInfoCollection diagnosticInfos) { OperationContext context = ValidateRequest(requestHeader, RequestType.Publish); @@ -1415,9 +1416,9 @@ namespace Opc.Ua.Server requestHeader.Timestamp); } */ - + Utils.Trace("PUBLISH #{0} RECEIVED. TIME={1:hh:mm:ss.fff}", requestHeader.RequestHandle, requestHeader.Timestamp); - + notificationMessage = ServerInternal.SubscriptionManager.Publish( context, subscriptionAcknowledgements, @@ -1440,7 +1441,7 @@ namespace Opc.Ua.Server } */ - return CreateResponse(requestHeader, context.StringTable); + return CreateResponse(requestHeader, context.StringTable); } catch (ServiceResultException e) { @@ -1455,7 +1456,7 @@ namespace Opc.Ua.Server } throw TranslateException(context, e); - } + } finally { OnRequestComplete(context); @@ -1474,13 +1475,13 @@ namespace Opc.Ua.Server try { AsyncPublishOperation operation = new AsyncPublishOperation(context, request, this); - - uint subscriptionId = 0; - UInt32Collection availableSequenceNumbers = null; - bool moreNotifications = false; - NotificationMessage notificationMessage = null; - StatusCodeCollection results = null; - DiagnosticInfoCollection diagnosticInfos = null; + + uint subscriptionId = 0; + UInt32Collection availableSequenceNumbers = null; + bool moreNotifications = false; + NotificationMessage notificationMessage = null; + StatusCodeCollection results = null; + DiagnosticInfoCollection diagnosticInfos = null; notificationMessage = ServerInternal.SubscriptionManager.Publish( context, @@ -1574,9 +1575,9 @@ namespace Opc.Ua.Server /// Returns a object /// public override ResponseHeader Republish( - RequestHeader requestHeader, - uint subscriptionId, - uint retransmitSequenceNumber, + RequestHeader requestHeader, + uint subscriptionId, + uint retransmitSequenceNumber, out NotificationMessage notificationMessage) { OperationContext context = ValidateRequest(requestHeader, RequestType.Republish); @@ -1586,9 +1587,9 @@ namespace Opc.Ua.Server notificationMessage = ServerInternal.SubscriptionManager.Republish( context, subscriptionId, - retransmitSequenceNumber); + retransmitSequenceNumber); - return CreateResponse(requestHeader, context.StringTable); + return CreateResponse(requestHeader, context.StringTable); } catch (ServiceResultException e) { @@ -1603,11 +1604,11 @@ namespace Opc.Ua.Server } throw TranslateException(context, e); - } + } finally { OnRequestComplete(context); - } + } } /// @@ -1628,18 +1629,18 @@ namespace Opc.Ua.Server /// public override ResponseHeader ModifySubscription( RequestHeader requestHeader, - uint subscriptionId, - double requestedPublishingInterval, - uint requestedLifetimeCount, - uint requestedMaxKeepAliveCount, - uint maxNotificationsPerPublish, - byte priority, - out double revisedPublishingInterval, - out uint revisedLifetimeCount, - out uint revisedMaxKeepAliveCount) + uint subscriptionId, + double requestedPublishingInterval, + uint requestedLifetimeCount, + uint requestedMaxKeepAliveCount, + uint maxNotificationsPerPublish, + byte priority, + out double revisedPublishingInterval, + out uint revisedLifetimeCount, + out uint revisedMaxKeepAliveCount) { OperationContext context = ValidateRequest(requestHeader, RequestType.ModifySubscription); - + try { ServerInternal.SubscriptionManager.ModifySubscription( @@ -1654,7 +1655,7 @@ namespace Opc.Ua.Server out revisedLifetimeCount, out revisedMaxKeepAliveCount); - return CreateResponse(requestHeader, context.StringTable); + return CreateResponse(requestHeader, context.StringTable); } catch (ServiceResultException e) { @@ -1669,11 +1670,11 @@ namespace Opc.Ua.Server } throw TranslateException(context, e); - } + } finally { OnRequestComplete(context); - } + } } /// @@ -1688,20 +1689,20 @@ namespace Opc.Ua.Server /// Returns a object /// public override ResponseHeader SetPublishingMode( - RequestHeader requestHeader, - bool publishingEnabled, - UInt32Collection subscriptionIds, - out StatusCodeCollection results, + RequestHeader requestHeader, + bool publishingEnabled, + UInt32Collection subscriptionIds, + out StatusCodeCollection results, out DiagnosticInfoCollection diagnosticInfos) { OperationContext context = ValidateRequest(requestHeader, RequestType.SetPublishingMode); - + try { if (subscriptionIds == null || subscriptionIds.Count == 0) { throw new ServiceResultException(StatusCodes.BadNothingToDo); - } + } ServerInternal.SubscriptionManager.SetPublishingMode( context, @@ -1710,7 +1711,7 @@ namespace Opc.Ua.Server out results, out diagnosticInfos); - return CreateResponse(requestHeader, context.StringTable); + return CreateResponse(requestHeader, context.StringTable); } catch (ServiceResultException e) { @@ -1725,11 +1726,11 @@ namespace Opc.Ua.Server } throw TranslateException(context, e); - } + } finally { OnRequestComplete(context); - } + } } /// @@ -1802,7 +1803,7 @@ namespace Opc.Ua.Server finally { OnRequestComplete(context); - } + } } /// @@ -1818,12 +1819,12 @@ namespace Opc.Ua.Server /// Returns a object /// public override ResponseHeader CreateMonitoredItems( - RequestHeader requestHeader, - uint subscriptionId, - TimestampsToReturn timestampsToReturn, - MonitoredItemCreateRequestCollection itemsToCreate, - out MonitoredItemCreateResultCollection results, - out DiagnosticInfoCollection diagnosticInfos) + RequestHeader requestHeader, + uint subscriptionId, + TimestampsToReturn timestampsToReturn, + MonitoredItemCreateRequestCollection itemsToCreate, + out MonitoredItemCreateResultCollection results, + out DiagnosticInfoCollection diagnosticInfos) { OperationContext context = ValidateRequest(requestHeader, RequestType.CreateMonitoredItems); @@ -1832,7 +1833,7 @@ namespace Opc.Ua.Server if (itemsToCreate == null || itemsToCreate.Count == 0) { throw new ServiceResultException(StatusCodes.BadNothingToDo); - } + } ServerInternal.SubscriptionManager.CreateMonitoredItems( context, @@ -1842,7 +1843,7 @@ namespace Opc.Ua.Server out results, out diagnosticInfos); - return CreateResponse(requestHeader, context.StringTable); + return CreateResponse(requestHeader, context.StringTable); } catch (ServiceResultException e) { @@ -1857,11 +1858,11 @@ namespace Opc.Ua.Server } throw TranslateException(context, e); - } + } finally { OnRequestComplete(context); - } + } } /// @@ -1877,12 +1878,12 @@ namespace Opc.Ua.Server /// Returns a object /// public override ResponseHeader ModifyMonitoredItems( - RequestHeader requestHeader, - uint subscriptionId, - TimestampsToReturn timestampsToReturn, - MonitoredItemModifyRequestCollection itemsToModify, - out MonitoredItemModifyResultCollection results, - out DiagnosticInfoCollection diagnosticInfos) + RequestHeader requestHeader, + uint subscriptionId, + TimestampsToReturn timestampsToReturn, + MonitoredItemModifyRequestCollection itemsToModify, + out MonitoredItemModifyResultCollection results, + out DiagnosticInfoCollection diagnosticInfos) { OperationContext context = ValidateRequest(requestHeader, RequestType.ModifyMonitoredItems); @@ -1891,7 +1892,7 @@ namespace Opc.Ua.Server if (itemsToModify == null || itemsToModify.Count == 0) { throw new ServiceResultException(StatusCodes.BadNothingToDo); - } + } ServerInternal.SubscriptionManager.ModifyMonitoredItems( context, @@ -1901,7 +1902,7 @@ namespace Opc.Ua.Server out results, out diagnosticInfos); - return CreateResponse(requestHeader, context.StringTable); + return CreateResponse(requestHeader, context.StringTable); } catch (ServiceResultException e) { @@ -1916,11 +1917,11 @@ namespace Opc.Ua.Server } throw TranslateException(context, e); - } + } finally { OnRequestComplete(context); - } + } } /// @@ -1935,20 +1936,20 @@ namespace Opc.Ua.Server /// Returns a object /// public override ResponseHeader DeleteMonitoredItems( - RequestHeader requestHeader, - uint subscriptionId, - UInt32Collection monitoredItemIds, - out StatusCodeCollection results, + RequestHeader requestHeader, + uint subscriptionId, + UInt32Collection monitoredItemIds, + out StatusCodeCollection results, out DiagnosticInfoCollection diagnosticInfos) { OperationContext context = ValidateRequest(requestHeader, RequestType.DeleteMonitoredItems); - + try { if (monitoredItemIds == null || monitoredItemIds.Count == 0) { throw new ServiceResultException(StatusCodes.BadNothingToDo); - } + } ServerInternal.SubscriptionManager.DeleteMonitoredItems( context, @@ -1972,7 +1973,7 @@ namespace Opc.Ua.Server } throw TranslateException(context, e); - } + } finally { OnRequestComplete(context); @@ -1992,21 +1993,21 @@ namespace Opc.Ua.Server /// Returns a object /// public override ResponseHeader SetMonitoringMode( - RequestHeader requestHeader, - uint subscriptionId, - MonitoringMode monitoringMode, - UInt32Collection monitoredItemIds, - out StatusCodeCollection results, + RequestHeader requestHeader, + uint subscriptionId, + MonitoringMode monitoringMode, + UInt32Collection monitoredItemIds, + out StatusCodeCollection results, out DiagnosticInfoCollection diagnosticInfos) { OperationContext context = ValidateRequest(requestHeader, RequestType.SetMonitoringMode); - + try { if (monitoredItemIds == null || monitoredItemIds.Count == 0) { throw new ServiceResultException(StatusCodes.BadNothingToDo); - } + } ServerInternal.SubscriptionManager.SetMonitoringMode( context, @@ -2031,7 +2032,7 @@ namespace Opc.Ua.Server } throw TranslateException(context, e); - } + } finally { OnRequestComplete(context); @@ -2049,13 +2050,13 @@ namespace Opc.Ua.Server /// Returns a object /// public override ResponseHeader Call( - RequestHeader requestHeader, - CallMethodRequestCollection methodsToCall, + RequestHeader requestHeader, + CallMethodRequestCollection methodsToCall, out CallMethodResultCollection results, - out DiagnosticInfoCollection diagnosticInfos) + out DiagnosticInfoCollection diagnosticInfos) { OperationContext context = ValidateRequest(requestHeader, RequestType.Call); - + try { if (methodsToCall == null || methodsToCall.Count == 0) @@ -2100,15 +2101,15 @@ namespace Opc.Ua.Server } throw TranslateException(context, e); - } + } finally { OnRequestComplete(context); } } -#endregion + #endregion -#region Public Methods used by the Host Process + #region Public Methods used by the Host Process /// /// The state object associated with the server. /// It provides the shared components for the Server. @@ -2121,13 +2122,13 @@ namespace Opc.Ua.Server lock (m_lock) { if (m_serverInternal == null) - { + { throw new ServiceResultException(StatusCodes.BadServerHalted); } return m_serverInternal; } - } + } } /// @@ -2155,7 +2156,7 @@ namespace Opc.Ua.Server { ApplicationConfiguration configuration = string.IsNullOrEmpty(base.Configuration.SourceFilePath) ? base.Configuration : await ApplicationConfiguration.Load(new FileInfo(base.Configuration.SourceFilePath), ApplicationType.Server, null, false); CertificateValidationEventHandler registrationCertificateValidator = new CertificateValidationEventHandler(RegistrationValidator_CertificateValidation); - configuration.CertificateValidator.CertificateValidation += registrationCertificateValidator; + configuration.CertificateValidator.CertificateValidation += registrationCertificateValidator; try { @@ -2259,8 +2260,8 @@ namespace Opc.Ua.Server { configuration.CertificateValidator.CertificateValidation -= registrationCertificateValidator; } - } - + } + return false; } @@ -2298,7 +2299,7 @@ namespace Opc.Ua.Server try { lock (m_registrationLock) - { + { // halt any outstanding timer. if (m_registrationTimer != null) { @@ -2311,13 +2312,13 @@ namespace Opc.Ua.Server { // schedule next registration. lock (m_registrationLock) - { + { if (m_maxRegistrationInterval > 0) { m_registrationTimer = new Timer( - OnRegisterServer, - this, - m_maxRegistrationInterval, + OnRegisterServer, + this, + m_maxRegistrationInterval, Timeout.Infinite); m_lastRegistrationInterval = m_minRegistrationInterval; @@ -2328,7 +2329,7 @@ namespace Opc.Ua.Server else { lock (m_registrationLock) - { + { if (m_registrationTimer == null) { // calculate next registration attempt. @@ -2340,7 +2341,7 @@ namespace Opc.Ua.Server } Utils.Trace("Register server failed. Trying again in {0} ms", m_lastRegistrationInterval); - + // create timer. m_registrationTimer = new Timer(OnRegisterServer, this, m_lastRegistrationInterval, Timeout.Infinite); } @@ -2348,13 +2349,13 @@ namespace Opc.Ua.Server } } catch (Exception e) - { + { Utils.Trace(e, "Unexpected exception handling registration timer."); } } -#endregion + #endregion -#region Protected Members used for Request Processing + #region Protected Members used for Request Processing /// /// The synchronization object. /// @@ -2371,12 +2372,12 @@ namespace Opc.Ua.Server ServerInternalData serverInternal = m_serverInternal; if (serverInternal == null) - { + { throw new ServiceResultException(StatusCodes.BadServerHalted); } return serverInternal; - } + } } /// @@ -2392,7 +2393,7 @@ namespace Opc.Ua.Server { throw new ServiceResultException(error); } - + // check server state. ServerInternalData serverInternal = m_serverInternal; @@ -2418,7 +2419,7 @@ namespace Opc.Ua.Server } if (m_serverInternal == null) - { + { throw new ServiceResultException(StatusCodes.BadServerHalted); } @@ -2517,7 +2518,7 @@ namespace Opc.Ua.Server { return null; } - + // check if inner result required. ServiceResult innerResult = null; @@ -2574,16 +2575,16 @@ namespace Opc.Ua.Server lock (m_lock) { if (m_serverInternal == null) - { + { throw new ServiceResultException(StatusCodes.BadServerHalted); } - + m_serverInternal.RequestManager.RequestCompleted(context); } - } -#endregion + } + #endregion -#region Protected Members used for Initialization + #region Protected Members used for Initialization /// /// Raised when the configuration changes. /// @@ -2649,7 +2650,7 @@ namespace Opc.Ua.Server lock (m_lock) { base.OnServerStarting(configuration); - + // save minimum nonce length. m_minNonceLength = configuration.SecurityConfiguration.NonceLength; @@ -2657,7 +2658,7 @@ namespace Opc.Ua.Server m_useRegisterServer2 = true; } } - + /// /// Creates the endpoints and creates the hosts. /// @@ -2668,8 +2669,8 @@ namespace Opc.Ua.Server /// Returns IList of a host for a UA service. /// protected override IList InitializeServiceHosts( - ApplicationConfiguration configuration, - out ApplicationDescription serverDescription, + ApplicationConfiguration configuration, + out ApplicationDescription serverDescription, out EndpointDescriptionCollection endpoints) { serverDescription = null; @@ -2679,17 +2680,17 @@ namespace Opc.Ua.Server // ensure at least one security policy exists. if (configuration.ServerConfiguration.SecurityPolicies.Count == 0) - { + { configuration.ServerConfiguration.SecurityPolicies.Add(new ServerSecurityPolicy()); } - + // ensure at least one user token policy exists. if (configuration.ServerConfiguration.UserTokenPolicies.Count == 0) - { + { UserTokenPolicy userTokenPolicy = new UserTokenPolicy(); - + userTokenPolicy.TokenType = UserTokenType.Anonymous; - userTokenPolicy.PolicyId = userTokenPolicy.TokenType.ToString(); + userTokenPolicy.PolicyId = userTokenPolicy.TokenType.ToString(); configuration.ServerConfiguration.UserTokenPolicies.Add(userTokenPolicy); } @@ -2706,27 +2707,25 @@ namespace Opc.Ua.Server endpoints = new EndpointDescriptionCollection(); IList endpointsForHost = null; - // create UA TCP host. - endpointsForHost = CreateUaTcpServiceHost( - hosts, - configuration, - configuration.ServerConfiguration.BaseAddresses, - serverDescription, - configuration.ServerConfiguration.SecurityPolicies); + foreach (var scheme in Utils.DefaultUriSchemes) + { + var binding = TransportBindings.Listeners.GetBinding(scheme); + if (binding != null) + { + endpointsForHost = binding.CreateServiceHost( + this, + hosts, + configuration, + configuration.ServerConfiguration.BaseAddresses, + serverDescription, + configuration.ServerConfiguration.SecurityPolicies, + InstanceCertificate, + InstanceCertificateChain + ); + endpoints.AddRange(endpointsForHost); + } + } - endpoints.InsertRange(0, endpointsForHost); - - // create HTTPS host. -#if !NO_HTTPS - endpointsForHost = CreateHttpsServiceHost( - hosts, - configuration, - configuration.ServerConfiguration.BaseAddresses, - serverDescription, - configuration.ServerConfiguration.SecurityPolicies); - - endpoints.AddRange(endpointsForHost); -#endif return new List(hosts.Values); } @@ -2753,19 +2752,19 @@ namespace Opc.Ua.Server protected override void StartApplication(ApplicationConfiguration configuration) { base.StartApplication(configuration); - + lock (m_lock) { try { // create the datastore for the instance. m_serverInternal = new ServerInternalData( - ServerProperties, - configuration, + ServerProperties, + configuration, MessageContext, new CertificateValidator(), InstanceCertificate); - + // create the manager responsible for providing localized string resources. ResourceManager resourceManager = CreateResourceManager(m_serverInternal, configuration); @@ -2774,20 +2773,20 @@ namespace Opc.Ua.Server // create the master node manager. MasterNodeManager masterNodeManager = CreateMasterNodeManager(m_serverInternal, configuration); - + // add the node manager to the datastore. m_serverInternal.SetNodeManager(masterNodeManager); // put the node manager into a state that allows it to be used by other objects. masterNodeManager.Startup(); - + // create the manager responsible for handling events. EventManager eventManager = CreateEventManager(m_serverInternal, configuration); // creates the server object. m_serverInternal.CreateServerObject( eventManager, - resourceManager, + resourceManager, requestManager); // do any additional processing now that the node manager is up and running. @@ -2795,20 +2794,20 @@ namespace Opc.Ua.Server // create the manager responsible for aggregates. m_serverInternal.AggregateManager = CreateAggregateManager(m_serverInternal, configuration); - + // start the session manager. SessionManager sessionManager = CreateSessionManager(m_serverInternal, configuration); sessionManager.Startup(); - + // start the subscription manager. SubscriptionManager subscriptionManager = CreateSubscriptionManager(m_serverInternal, configuration); subscriptionManager.Startup(); - + // add the session manager to the datastore. m_serverInternal.SetSessionManager(sessionManager, subscriptionManager); - + ServerError = null; - + // setup registration information. lock (m_registrationLock) { @@ -2840,7 +2839,7 @@ namespace Opc.Ua.Server m_registrationInfo.DiscoveryUrls.Add(uri.ToString()); } - + // build list of registration endpoints. m_registrationEndpoints = new ConfiguredEndpointCollection(configuration); @@ -2858,7 +2857,7 @@ namespace Opc.Ua.Server m_registrationEndpoints.Add(endpoint); - m_minRegistrationInterval = 1000; + m_minRegistrationInterval = 1000; m_lastRegistrationInterval = m_minRegistrationInterval; // start registration timer. @@ -2878,8 +2877,8 @@ namespace Opc.Ua.Server SetServerState(ServerState.Running); // all initialization is complete. - OnServerStarted(m_serverInternal); - + OnServerStarted(m_serverInternal); + // monitor the configuration file. if (!String.IsNullOrEmpty(configuration.SourceFilePath)) { @@ -2939,7 +2938,7 @@ namespace Opc.Ua.Server } catch (Exception e) { - ServerError = new ServiceResult(e); + ServerError = new ServiceResult(e); } finally { @@ -3061,7 +3060,7 @@ namespace Opc.Ua.Server return manager; } - + /// /// Creates the resource manager for the server. /// diff --git a/Stack/Opc.Ua.Bindings.Https/Opc.Ua.Bindings.Https.csproj b/Stack/Opc.Ua.Bindings.Https/Opc.Ua.Bindings.Https.csproj new file mode 100644 index 0000000000..eb2ee31210 --- /dev/null +++ b/Stack/Opc.Ua.Bindings.Https/Opc.Ua.Bindings.Https.csproj @@ -0,0 +1,51 @@ + + + + $(DefineConstants);NET_STANDARD + $(LibTargetFrameworks) + 6 + Opc.Ua.Bindings.Https + OPCFoundation.NetStandard.Opc.Ua.Bindings.Https + Opc.Ua.Bindings + OPC UA Https Binding Library + true + true + + + + $(PackageId).Debug + + + + $(DefineConstants);SIGNASSEMBLY + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Stack/Opc.Ua.Bindings.Https/Properties/AssemblyInfo.cs b/Stack/Opc.Ua.Bindings.Https/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..289dc8e489 --- /dev/null +++ b/Stack/Opc.Ua.Bindings.Https/Properties/AssemblyInfo.cs @@ -0,0 +1,24 @@ +/* Copyright (c) 1996-2020 The OPC Foundation. All rights reserved. + The source code in this file is covered under a dual-license scenario: + - RCL: for OPC Foundation members in good-standing + - GPL V2: everybody else + RCL license terms accompanied with this source code. See http://opcfoundation.org/License/RCL/1.00/ + GNU General Public License as published by the Free Software Foundation; + version 2 of the License are accompanied with this source code. See http://opcfoundation.org/License/GPLv2 + This source code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +*/ +using System.Runtime.CompilerServices; + +#if SIGNASSEMBLY +[assembly: InternalsVisibleTo("Opc.Ua.Core.Tests, PublicKey = " + + // OPC Foundation Strong Name Public Key + "0024000004800000940000000602000000240000525341310004000001000100d987b12f068b35" + + "80429f3dde01397508880fc7e62621397618456ca1549aeacfbdb90c62adfe918f05ce3677b390" + + "f78357b8745cb6e1334655afce1a9527ac92fc829ff585ea79f007e52ba0f83ead627e3edda40b" + + "ec5ae574128fc9342cb57cb8285aa4e5b589c0ebef3be571b5c8f2ab1067f7c880e8f8882a73c8" + + "0a12a1ef")] +#else +[assembly: InternalsVisibleTo("Opc.Ua.Core.Tests")] +#endif diff --git a/Stack/Opc.Ua.Bindings.Https/Stack/Https/HttpsServiceHost.cs b/Stack/Opc.Ua.Bindings.Https/Stack/Https/HttpsServiceHost.cs new file mode 100644 index 0000000000..ee8f41e50f --- /dev/null +++ b/Stack/Opc.Ua.Bindings.Https/Stack/Https/HttpsServiceHost.cs @@ -0,0 +1,175 @@ +/* ======================================================================== + * Copyright (c) 2005-2020 The OPC Foundation, Inc. All rights reserved. + * + * OPC Reciprocal Community License ("RCL") Version 1.00 + * + * Unless explicitly acquired and licensed from Licensor under another + * license, the contents of this file are subject to the Reciprocal + * Community License ("RCL") Version 1.00, or subsequent versions + * as allowed by the RCL, and You may not copy or use this file in either + * source code or executable form, except in compliance with the terms and + * conditions of the RCL. + * + * All software distributed under the RCL is provided strictly on an + * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, + * AND LICENSOR HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT + * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, QUIET ENJOYMENT, OR NON-INFRINGEMENT. See the RCL for specific + * language governing rights and limitations under the RCL. + * + * The complete license agreement can be found here: + * http://opcfoundation.org/License/RCL/1.00/ + * ======================================================================*/ + +using System; +using System.Collections.Generic; +using System.Security.Cryptography.X509Certificates; +using System.Threading.Tasks; + + +namespace Opc.Ua.Bindings +{ + /// + /// Creates a new with + /// interface. + /// + public abstract class HttpsServiceHost : ITransportListenerFactory + { + /// + /// The protocol supported by the listener. + /// + public abstract string UriScheme { get; } + + /// + /// The method creates a new instance of a . + /// + /// The transport listener. + public abstract ITransportListener Create(); + + /// + /// + /// Create a new service host for UA HTTPS. + /// + public List CreateServiceHost( + ServerBase serverBase, + IDictionary hosts, + ApplicationConfiguration configuration, + IList baseAddresses, + ApplicationDescription serverDescription, + List securityPolicies, + X509Certificate2 instanceCertificate, + X509Certificate2Collection instanceCertificateChain + ) + { + // generate a unique host name. + string hostName = String.Empty; + + if (hosts.ContainsKey(hostName)) + { + hostName = "/Https"; + } + + if (hosts.ContainsKey(hostName)) + { + hostName += Utils.Format("/{0}", hosts.Count); + } + + // build list of uris. + List uris = new List(); + EndpointDescriptionCollection endpoints = new EndpointDescriptionCollection(); + + // create the endpoint configuration to use. + EndpointConfiguration endpointConfiguration = EndpointConfiguration.Create(configuration); + string computerName = Utils.GetHostName(); + + for (int ii = 0; ii < baseAddresses.Count; ii++) + { + if (!baseAddresses[ii].StartsWith(Utils.UriSchemeHttps, StringComparison.Ordinal)) + { + continue; + } + + UriBuilder uri = new UriBuilder(baseAddresses[ii]); + + if (uri.Path[uri.Path.Length - 1] != '/') + { + uri.Path += "/"; + } + + if (String.Compare(uri.Host, "localhost", StringComparison.OrdinalIgnoreCase) == 0) + { + uri.Host = computerName; + } + + uris.Add(uri.Uri); + + if (uri.Scheme == Utils.UriSchemeHttps) + { + // Can only support one policy with HTTPS + // So pick the first policy with security mode sign and encrypt + ServerSecurityPolicy bestPolicy = null; + foreach (ServerSecurityPolicy policy in securityPolicies) + { + if (policy.SecurityMode != MessageSecurityMode.SignAndEncrypt) + { + continue; + } + + bestPolicy = policy; + break; + } + + // Pick the first policy from the list if no policies with sign and encrypt defined + if (bestPolicy == null) + { + bestPolicy = securityPolicies[0]; + } + + EndpointDescription description = new EndpointDescription(); + + description.EndpointUrl = uri.ToString(); + description.Server = serverDescription; + + if (instanceCertificate != null) + { + description.ServerCertificate = instanceCertificate.RawData; + // check if complete chain should be sent. + if (configuration.SecurityConfiguration.SendCertificateChain && + instanceCertificateChain != null && + instanceCertificateChain.Count > 0) + { + List serverCertificateChain = new List(); + + for (int i = 0; i < instanceCertificateChain.Count; i++) + { + serverCertificateChain.AddRange(instanceCertificateChain[i].RawData); + } + + description.ServerCertificate = serverCertificateChain.ToArray(); + } + } + + description.SecurityMode = bestPolicy.SecurityMode; + description.SecurityPolicyUri = bestPolicy.SecurityPolicyUri; + description.SecurityLevel = ServerSecurityPolicy.CalculateSecurityLevel(bestPolicy.SecurityMode, bestPolicy.SecurityPolicyUri); + description.UserIdentityTokens = serverBase.GetUserTokenPolicies(configuration, description); + description.TransportProfileUri = Profiles.HttpsBinaryTransport; + + ITransportListener listener = Create(); + if (listener != null) + { + endpoints.Add(description); + serverBase.CreateServiceHostEndpoint(uri.Uri, endpoints, endpointConfiguration, listener, + configuration.CertificateValidator.GetChannelValidator()); + } + else + { + Utils.Trace(Utils.TraceMasks.Error, "Failed to create endpoint {0} because the transport profile is unsupported.", uri); + } + } + } + + return endpoints; + } + } +} diff --git a/Stack/Opc.Ua.Core/Stack/Https/HttpsTransportChannel.cs b/Stack/Opc.Ua.Bindings.Https/Stack/Https/HttpsTransportChannel.cs similarity index 85% rename from Stack/Opc.Ua.Core/Stack/Https/HttpsTransportChannel.cs rename to Stack/Opc.Ua.Bindings.Https/Stack/Https/HttpsTransportChannel.cs index bb4b586049..1ef2f2e264 100644 --- a/Stack/Opc.Ua.Core/Stack/Https/HttpsTransportChannel.cs +++ b/Stack/Opc.Ua.Bindings.Https/Stack/Https/HttpsTransportChannel.cs @@ -11,18 +11,36 @@ */ -#if !NO_HTTPS - using System; using System.IO; -using System.Runtime.InteropServices; -using System.Threading.Tasks; using System.Net.Http; using System.Net.Http.Headers; +using System.Runtime.InteropServices; +using System.Threading.Tasks; namespace Opc.Ua.Bindings { + /// + /// Creates a new HttpsTransportChannel with ITransportChannel interface. + /// + public class HttpsTransportChannelFactory : ITransportChannelFactory + { + /// + /// The protocol supported by the channel. + /// + public string UriScheme => Utils.UriSchemeHttps; + + /// + /// The method creates a new instance of a Https transport channel + /// + /// The transport channel + public ITransportChannel Create() + { + return new HttpsTransportChannel(); + } + } + /// /// Wraps the HttpsTransportChannel and provides an ITransportChannel implementation. /// @@ -34,34 +52,22 @@ namespace Opc.Ua.Bindings } /// - public TransportChannelFeatures SupportedFeatures - { - get { return TransportChannelFeatures.Open | TransportChannelFeatures.Reconnect | TransportChannelFeatures.BeginSendRequest; } - } + public string UriScheme => Utils.UriSchemeHttps; /// - public EndpointDescription EndpointDescription - { - get { return m_settings.Description; } - } + public TransportChannelFeatures SupportedFeatures => TransportChannelFeatures.Open | TransportChannelFeatures.Reconnect | TransportChannelFeatures.BeginSendRequest; /// - public EndpointConfiguration EndpointConfiguration - { - get { return m_settings.Configuration; } - } + public EndpointDescription EndpointDescription => m_settings.Description; /// - public ServiceMessageContext MessageContext - { - get { return m_quotas.MessageContext; } - } + public EndpointConfiguration EndpointConfiguration => m_settings.Configuration; /// - public ChannelToken CurrentToken - { - get { return null; } - } + public ServiceMessageContext MessageContext => m_quotas.MessageContext; + + /// + public ChannelToken CurrentToken => null; /// public int OperationTimeout @@ -114,8 +120,7 @@ namespace Opc.Ua.Bindings try { handler.ServerCertificateCustomValidationCallback = - (httpRequestMessage, cert, chain, policyErrors) => - { + (httpRequestMessage, cert, chain, policyErrors) => { try { m_quotas.CertificateValidator?.Validate(cert); @@ -187,22 +192,21 @@ namespace Opc.Ua.Bindings content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); AsyncResult result = new AsyncResult(callback, callbackData, m_operationTimeout, request, null); - Task.Run(async () => - { - try - { - response = await m_client.PostAsync(m_url, content); - response.EnsureSuccessStatusCode(); - } - catch (Exception ex) - { - Utils.Trace("Exception sending HTTPS request: " + ex.Message); - result.Exception = ex; - response = null; - } - result.Response = response; - result.OperationCompleted(); - }); + Task.Run(async () => { + try + { + response = await m_client.PostAsync(m_url, content); + response.EnsureSuccessStatusCode(); + } + catch (Exception ex) + { + Utils.Trace("Exception sending HTTPS request: " + ex.Message); + result.Exception = ex; + response = null; + } + result.Response = response; + result.OperationCompleted(); + }); return result; } @@ -345,4 +349,4 @@ namespace Opc.Ua.Bindings private HttpClient m_client; } } -#endif + diff --git a/Stack/Opc.Ua.Core/Stack/Https/HttpsListener.cs b/Stack/Opc.Ua.Bindings.Https/Stack/Https/HttpsTransportListener.cs similarity index 93% rename from Stack/Opc.Ua.Core/Stack/Https/HttpsListener.cs rename to Stack/Opc.Ua.Bindings.Https/Stack/Https/HttpsTransportListener.cs index 5409597a77..520bacd15e 100644 --- a/Stack/Opc.Ua.Core/Stack/Https/HttpsListener.cs +++ b/Stack/Opc.Ua.Bindings.Https/Stack/Https/HttpsTransportListener.cs @@ -21,8 +21,6 @@ * http://opcfoundation.org/License/RCL/1.00/ * ======================================================================*/ -#if !NO_HTTPS - using System; using System.IO; using System.Net; @@ -38,14 +36,35 @@ using Microsoft.AspNetCore.Server.Kestrel.Https; namespace Opc.Ua.Bindings { /// - /// Implements the startup of the Https listener. + /// Creates a new with + /// interface. + /// + public class HttpsTransportListenerFactory : HttpsServiceHost + { + /// + /// The protocol supported by the listener. + /// + public override string UriScheme => Utils.UriSchemeHttps; + + /// + /// The method creates a new instance of a . + /// + /// The transport listener. + public override ITransportListener Create() + { + return new HttpsTransportListener(); + } + } + + /// + /// Implements the kestrel startup of the Https listener. /// public class Startup { /// /// Get the Https listener. /// - public static UaHttpsChannelListener Listener { get; set; } + public static HttpsTransportListener Listener { get; set; } /// /// Configure the request pipeline for the listener. @@ -72,13 +91,13 @@ namespace Opc.Ua.Bindings /// /// Manages the connections for a UA HTTPS server. /// - public partial class UaHttpsChannelListener : ITransportListener + public class HttpsTransportListener : ITransportListener { #region Constructors /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public UaHttpsChannelListener() + public HttpsTransportListener() { } #endregion @@ -381,5 +400,3 @@ namespace Opc.Ua.Bindings #endregion } } - -#endif diff --git a/Stack/Opc.Ua.Core/Documentation/Opc.Ua.Bindings.cs b/Stack/Opc.Ua.Core/Documentation/Opc.Ua.Bindings.cs index f2044565e5..a49f80ea1c 100644 --- a/Stack/Opc.Ua.Core/Documentation/Opc.Ua.Bindings.cs +++ b/Stack/Opc.Ua.Core/Documentation/Opc.Ua.Bindings.cs @@ -13,7 +13,7 @@ namespace Opc.Ua.Bindings { /// - /// The Opc.Ua.Bindings namespace contains classes that implement the WCF bindings for the + /// The Opc.Ua.Bindings namespace contains classes that implement the bindings for the /// mappings described in Part 6 of the UA specification. It also includes an implementation for /// the UA TCP protocol. /// diff --git a/Stack/Opc.Ua.Core/Opc.Ua.Core.csproj b/Stack/Opc.Ua.Core/Opc.Ua.Core.csproj index e6cfd90ab8..49c5235370 100644 --- a/Stack/Opc.Ua.Core/Opc.Ua.Core.csproj +++ b/Stack/Opc.Ua.Core/Opc.Ua.Core.csproj @@ -16,10 +16,6 @@ $(PackageId).Debug - - $(DefineConstants);NO_HTTPS - - $(DefineConstants);SIGNASSEMBLY @@ -47,33 +43,20 @@ - - - - - - - - - - - - - diff --git a/Stack/Opc.Ua.Core/Security/Constants/SecurityConstants.cs b/Stack/Opc.Ua.Core/Security/Constants/SecurityConstants.cs index d1aa17c959..60fc921df4 100644 --- a/Stack/Opc.Ua.Core/Security/Constants/SecurityConstants.cs +++ b/Stack/Opc.Ua.Core/Security/Constants/SecurityConstants.cs @@ -102,7 +102,12 @@ namespace Opc.Ua /// Communicates with UA TCP, UA Security and UA Binary. /// public const string UaTcpTransport = "http://opcfoundation.org/UA-Profile/Transport/uatcp-uasc-uabinary"; - + + /// + /// Communicates with UA TCP over secure Websockets, UA Security and UA Binary. + /// + public const string UaWssTransport = "http://opcfoundation.org/UA-Profile/Transport/uawss-uasc-uabinary"; + /// /// Communicates with UA Binary over HTTPS. /// diff --git a/Stack/Opc.Ua.Core/Stack/Bindings/BaseBinding.cs b/Stack/Opc.Ua.Core/Stack/Bindings/BaseBinding.cs index 1a45f8b3b9..a2520e518a 100644 --- a/Stack/Opc.Ua.Core/Stack/Bindings/BaseBinding.cs +++ b/Stack/Opc.Ua.Core/Stack/Bindings/BaseBinding.cs @@ -10,14 +10,12 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ -using System.ServiceModel.Channels; - namespace Opc.Ua.Bindings { /// /// The binding for the UA native stack /// - public abstract class BaseBinding : Binding + public abstract class BaseBinding { #region Constructors /// diff --git a/Stack/Opc.Ua.Core/Stack/Bindings/ITransportBindings.cs b/Stack/Opc.Ua.Core/Stack/Bindings/ITransportBindings.cs new file mode 100644 index 0000000000..918471ce93 --- /dev/null +++ b/Stack/Opc.Ua.Core/Stack/Bindings/ITransportBindings.cs @@ -0,0 +1,118 @@ +/* Copyright (c) 1996-2020 The OPC Foundation. All rights reserved. + The source code in this file is covered under a dual-license scenario: + - RCL: for OPC Foundation members in good-standing + - GPL V2: everybody else + RCL license terms accompanied with this source code. See http://opcfoundation.org/License/RCL/1.00/ + GNU General Public License as published by the Free Software Foundation; + version 2 of the License are accompanied with this source code. See http://opcfoundation.org/License/GPLv2 + This source code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +*/ + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Security.Cryptography.X509Certificates; +using System.Threading.Tasks; + +namespace Opc.Ua.Bindings +{ + /// + /// This is an interface to the scheme of a transport binding. + /// + public interface ITransportBindingScheme + { + /// + /// The protocol scheme supported by the binding. + /// + string UriScheme { get; } + } + + /// + /// This is an interface to the factory of a transport binding. + /// + public interface ITransportBindingFactory + : ITransportBindingScheme + { + /// + /// The factory to create a new transport. + /// + /// The transport. + T Create(); + } + + /// + /// The interface to manage transport bindings. + /// + public interface ITransportBindings + { + /// + /// Get a transport binding for a uri scheme. + /// + /// The uri scheme. + T GetBinding(string uriScheme); + + /// + /// Return if there is a transport listener for a uri scheme. + /// + /// The uri scheme. + bool HasBinding(string uriScheme); + + /// + /// Set the transport factory to the binding. + /// Overrides other bindings with the same uri scheme. + /// + void SetBinding(T binding); + + /// + /// Add all bindings with interface exported from a assembly. + /// + /// The assembly with the bindings. + IEnumerable AddBindings(Assembly assembly); + + /// + /// Add all bindings with interface from a list. + /// + /// The array of binding types with interface . + IEnumerable AddBindings(IEnumerable bindings); + } + + /// + /// This is the transport listener factory interface for a binding (server). + /// + public interface ITransportListenerFactory : + ITransportBindingFactory + { + /// + /// Create the service host for a server using + /// + /// The server base. + /// The service hosts are added to this list. + /// The application configuration. + /// The base addreses for the service host. + /// The server description. + /// The list of supported security policies. + /// The server certificate. + /// The cert cahin of the server certificate. + List CreateServiceHost( + ServerBase serverBase, + IDictionary hosts, + ApplicationConfiguration configuration, + IList baseAddresses, + ApplicationDescription serverDescription, + List securityPolicies, + X509Certificate2 instanceCertificate, + X509Certificate2Collection instanceCertificateChain + ); + } + + /// + /// This is the transport channel factory interface for a binding (client). + /// + public interface ITransportChannelFactory : + ITransportBindingFactory + { + } + +} diff --git a/Stack/Opc.Ua.Core/Stack/Bindings/TransportBindings.cs b/Stack/Opc.Ua.Core/Stack/Bindings/TransportBindings.cs new file mode 100644 index 0000000000..b8f343a165 --- /dev/null +++ b/Stack/Opc.Ua.Core/Stack/Bindings/TransportBindings.cs @@ -0,0 +1,88 @@ +/* Copyright (c) 1996-2020 The OPC Foundation. All rights reserved. + The source code in this file is covered under a dual-license scenario: + - RCL: for OPC Foundation members in good-standing + - GPL V2: everybody else + RCL license terms accompanied with this source code. See http://opcfoundation.org/License/RCL/1.00/ + GNU General Public License as published by the Free Software Foundation; + version 2 of the License are accompanied with this source code. See http://opcfoundation.org/License/GPLv2 + This source code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +*/ + +using System; + +namespace Opc.Ua.Bindings +{ + /// + /// The transpaort bindings available for teh stack. + /// + public static class TransportBindings + { + static TransportBindings() + { + Channels = new TransportChannelBindings(new Type[] { typeof(TcpTransportChannelFactory) }); + Listeners = new TransportListenerBindings(new Type[] { typeof(TcpTransportListenerFactory) }); + } + + /// + /// The bindings for transport channels (client). + /// + public static TransportChannelBindings Channels { get; private set; } + + /// + /// The bindings for transport listeners (server). + /// + public static TransportListenerBindings Listeners { get; private set; } + + /// + /// The bindings for the transport channels. + /// + public class TransportChannelBindings : + TransportBindingsBase + { + /// + /// Initialize the transport listener. + /// + /// List of known default bindings. + public TransportChannelBindings(Type[] defaultBindings) : base(defaultBindings) + { + } + + /// + /// Get a transport channel for the specified uri scheme. + /// + /// The uri scheme of the transport. + public ITransportChannel GetChannel(string uriScheme) + { + var binding = GetBinding(uriScheme); + return binding?.Create(); + } + } + + /// + /// The bindings for the transport listeners. + /// + public class TransportListenerBindings : + TransportBindingsBase + { + /// + /// Initialize the transport listener. + /// + /// List of known default bindings. + public TransportListenerBindings(Type[] defaultBindings) : base(defaultBindings) + { + } + + /// + /// Get a transport listener for the specified uri scheme. + /// + /// The uri scheme of the transport. + public ITransportListener GetListener(string uriScheme) + { + var binding = GetBinding(uriScheme); + return binding?.Create(); + } + } + } +} diff --git a/Stack/Opc.Ua.Core/Stack/Bindings/TransportBindingsBase.cs b/Stack/Opc.Ua.Core/Stack/Bindings/TransportBindingsBase.cs new file mode 100644 index 0000000000..004252534f --- /dev/null +++ b/Stack/Opc.Ua.Core/Stack/Bindings/TransportBindingsBase.cs @@ -0,0 +1,174 @@ +/* Copyright (c) 1996-2020 The OPC Foundation. All rights reserved. + The source code in this file is covered under a dual-license scenario: + - RCL: for OPC Foundation members in good-standing + - GPL V2: everybody else + RCL license terms accompanied with this source code. See http://opcfoundation.org/License/RCL/1.00/ + GNU General Public License as published by the Free Software Foundation; + version 2 of the License are accompanied with this source code. See http://opcfoundation.org/License/GPLv2 + This source code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace Opc.Ua.Bindings +{ + /// + /// The bindings for the transport listeners. + /// + public class TransportBindingsBase : + ITransportBindings where T : class, ITransportBindingScheme + { + /// + /// Implement the default constructor. + /// + /// + /// The default constructor adds all interfaces T. + /// + protected TransportBindingsBase() + { + Bindings = new Dictionary(); + AddBindings(typeof(TransportBindingsBase).Assembly); + } + + /// + /// Initialize object with default list of bindings. + /// + protected TransportBindingsBase(Type[] defaultBindings) + { + Bindings = new Dictionary(); + AddBindings(defaultBindings); + } + + #region Public Properties + /// + /// Dictionary of bindings of type . + /// + protected Dictionary Bindings { get; private set; } + #endregion + + #region ITransportBindings + /// + public T GetBinding(string uriScheme) + { + T binding; + if (!Bindings.TryGetValue(uriScheme, out binding)) + { + TryAddDefaultTransportBindings(uriScheme); + if (!Bindings.TryGetValue(uriScheme, out binding)) + { + return default(T); + } + } + return binding; + } + + /// + public bool HasBinding(string uriScheme) + { + T binding; + if (Bindings.TryGetValue(uriScheme, out binding)) + { + return true; + } + return false; + } + + /// + public void SetBinding(T binding) + { + Bindings[binding.UriScheme] = binding; + } + + /// + public IEnumerable AddBindings(Assembly assembly) + { + var bindings = assembly.GetExportedTypes().Where(type => IsBindingType(type)); + return AddBindings(bindings); + } + + /// + public IEnumerable AddBindings(IEnumerable bindings) + { + var result = new List(); + foreach (Type bindingType in bindings) + { + var binding = Activator.CreateInstance(bindingType) as T; + if (binding != null) + { + Bindings[binding.UriScheme] = binding; + result.Add(bindingType); + } + } + return result; + } + #endregion + + #region Private Methods + /// + /// Validate the type is a transport listener. + /// + protected static bool IsBindingType(System.Type bindingType) + { + if (bindingType == null) + { + return false; + } + + var bindingTypeInfo = bindingType.GetTypeInfo(); + if (bindingTypeInfo.IsAbstract || + !typeof(T).GetTypeInfo().IsAssignableFrom(bindingTypeInfo)) + { + return false; + } + + var listener = Activator.CreateInstance(bindingType) as T; + if (listener == null) + { + return false; + } + + return true; + } + + /// + /// Try to load a binding from well known assemblies at runtime. + /// + /// The uri scheme of the binding. + private bool TryAddDefaultTransportBindings(string scheme) + { + string assemblyName; + if (Utils.DefaultBindings.TryGetValue(scheme, out assemblyName)) + { + Assembly assembly = null; + string fullName = Utils.DefaultOpcUaCoreAssemblyFullName.Replace(Utils.DefaultOpcUaCoreAssemblyName, assemblyName); + try + { + assembly = Assembly.Load(fullName); + } + catch + { + Utils.Trace(Utils.TraceMasks.Error, "Failed to load the assembly {0} for transport binding {1}.", + fullName, scheme + ); + } + + if (assembly != null) + { + var listeners = AddBindings(assembly); + return listeners.Count() > 0; + } + } + else + { + Utils.Trace(Utils.TraceMasks.Error, "The transport binding {0} is unsupported.", scheme); + } + return false; + } + #endregion + } +} diff --git a/Stack/Opc.Ua.Core/Stack/Bindings/TransportListenerBindings.cs b/Stack/Opc.Ua.Core/Stack/Bindings/TransportListenerBindings.cs deleted file mode 100644 index db9f56a309..0000000000 --- a/Stack/Opc.Ua.Core/Stack/Bindings/TransportListenerBindings.cs +++ /dev/null @@ -1,96 +0,0 @@ -/* Copyright (c) 1996-2020 The OPC Foundation. All rights reserved. - The source code in this file is covered under a dual-license scenario: - - RCL: for OPC Foundation members in good-standing - - GPL V2: everybody else - RCL license terms accompanied with this source code. See http://opcfoundation.org/License/RCL/1.00/ - GNU General Public License as published by the Free Software Foundation; - version 2 of the License are accompanied with this source code. See http://opcfoundation.org/License/GPLv2 - This source code is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -*/ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; - -namespace Opc.Ua.Bindings -{ - /// - /// The binding for the transport listeners. - /// - public static class TransportListenerBindings - { - static TransportListenerBindings() - { - TransportListeners = new Dictionary(); - var listeners = typeof(TransportListenerBindings).Assembly.GetExportedTypes().Where(type => IsTransportListenerType(type)); - foreach (var listenerType in listeners) - { - Add(listenerType); - } - } - - #region Public Properties - /// - /// Dictionary of transported listeners. - /// - public static Dictionary TransportListeners { get; private set; } - - /// - /// Get a transport listener for a uri scheme. - /// - /// The uri scheme. - public static ITransportListener GetTransportListener(string uriScheme) - { - Type listenerType; - if (TransportListeners.TryGetValue(uriScheme, out listenerType)) - { - return Activator.CreateInstance(listenerType) as ITransportListener; - } - return null; - } - - /// - /// Add a transport listener to the binding. - /// - public static void Add(Type listenerType) - { - if (IsTransportListenerType(listenerType)) - { - var listener = Activator.CreateInstance(listenerType) as ITransportListener; - TransportListeners.Add(listener.UriScheme, listenerType); - } - } - - /// - /// Validate the type is a transport listener. - /// - private static bool IsTransportListenerType(System.Type systemType) - { - if (systemType == null) - { - return false; - } - - var systemTypeInfo = systemType.GetTypeInfo(); - if (systemTypeInfo.IsAbstract || - !typeof(ITransportListener).GetTypeInfo().IsAssignableFrom(systemTypeInfo)) - { - return false; - } - - var listener = Activator.CreateInstance(systemType) as ITransportListener; - if (listener == null) - { - return false; - } - - listener.Dispose(); - - return true; - } - #endregion - } -} diff --git a/Stack/Opc.Ua.Core/Stack/Client/ReverseConnectHost.cs b/Stack/Opc.Ua.Core/Stack/Client/ReverseConnectHost.cs index 10bbbed0df..8df9a91cea 100644 --- a/Stack/Opc.Ua.Core/Stack/Client/ReverseConnectHost.cs +++ b/Stack/Opc.Ua.Core/Stack/Client/ReverseConnectHost.cs @@ -32,9 +32,13 @@ namespace Opc.Ua { if (url == null) throw new ArgumentNullException(nameof(url)); - var listener = TransportListenerBindings.GetTransportListener(url.Scheme); - - if (listener == null) throw new ArgumentException(nameof(url), "No suitable listener found."); + var listener = TransportBindings.Listeners.GetListener(url.Scheme); + if (listener == null) + { + throw ServiceResultException.Create( + StatusCodes.BadProtocolVersionUnsupported, + "Unsupported transport profile for scheme {0}\r\n", url.Scheme); + } m_listener = listener; Url = url; diff --git a/Stack/Opc.Ua.Core/Stack/Client/UaChannelBase.cs b/Stack/Opc.Ua.Core/Stack/Client/UaChannelBase.cs index d97703a95c..3e6ab9f4ad 100644 --- a/Stack/Opc.Ua.Core/Stack/Client/UaChannelBase.cs +++ b/Stack/Opc.Ua.Core/Stack/Client/UaChannelBase.cs @@ -692,28 +692,14 @@ namespace Opc.Ua X509Certificate2Collection clientCertificateChain, ServiceMessageContext messageContext) { - bool useUaTcp = description.EndpointUrl.StartsWith(Utils.UriSchemeOpcTcp); - bool useHttps = description.EndpointUrl.StartsWith(Utils.UriSchemeHttps); - // initialize the channel which will be created with the server. - ITransportChannel channel = null; - - if (useUaTcp) - { - channel = new TcpTransportChannel(); - } -#if !NO_HTTPS - else if (useHttps) - { - channel = new HttpsTransportChannel(); - } -#endif - + string uriScheme = new Uri(description.EndpointUrl).Scheme; + ITransportChannel channel = TransportBindings.Channels.GetChannel(uriScheme); if (channel == null) { throw ServiceResultException.Create( StatusCodes.BadProtocolVersionUnsupported, - "Unsupported transport profile\r\n"); + "Unsupported transport profile for scheme {0}\r\n", uriScheme); } // create a UA channel. @@ -780,42 +766,37 @@ namespace Opc.Ua X509Certificate2Collection clientCertificateChain, ServiceMessageContext messageContext) { - bool useUaTcp = description.EndpointUrl.StartsWith(Utils.UriSchemeOpcTcp); - bool useHttps = description.EndpointUrl.StartsWith(Utils.UriSchemeHttps); + string uriScheme = new Uri(description.EndpointUrl).Scheme; switch (description.TransportProfileUri) { case Profiles.UaTcpTransport: { - useUaTcp = true; + uriScheme = Utils.UriSchemeOpcTcp; break; } case Profiles.HttpsBinaryTransport: { - useHttps = true; + uriScheme = Utils.UriSchemeHttps; break; } + + case Profiles.UaWssTransport: + { + uriScheme = Utils.UriSchemeOpcWss; + break; + } + } // initialize the channel which will be created with the server. - ITransportChannel channel = null; - if (useUaTcp) - { - channel = new TcpTransportChannel(); - } -#if !NO_HTTPS - else if (useHttps) - { - channel = new HttpsTransportChannel(); - } -#endif - + ITransportChannel channel = TransportBindings.Channels.GetChannel(uriScheme); if (channel == null) { throw ServiceResultException.Create( StatusCodes.BadProtocolVersionUnsupported, - "Unsupported transport profile\r\n"); + "Unsupported transport profile for scheme {0}\r\n", uriScheme); } // create a UA-TCP channel. diff --git a/Stack/Opc.Ua.Core/Stack/Server/ServerBase.cs b/Stack/Opc.Ua.Core/Stack/Server/ServerBase.cs index 2e59f8ad9e..b4a4f3a96e 100644 --- a/Stack/Opc.Ua.Core/Stack/Server/ServerBase.cs +++ b/Stack/Opc.Ua.Core/Stack/Server/ServerBase.cs @@ -699,282 +699,60 @@ namespace Opc.Ua } /// - /// Create a new service host for UA TCP. + /// Create the transport listener for the service host endpoint. /// - protected List CreateUaTcpServiceHost( - IDictionary hosts, - ApplicationConfiguration configuration, - IList baseAddresses, - ApplicationDescription serverDescription, - List securityPolicies) + /// The endpoint Uri. + /// The description of the endpoints. + /// The configuration of the endpoints. + /// The transport listener. + /// The certificate validator for the transport. + public virtual void CreateServiceHostEndpoint( + Uri endpointUri, + EndpointDescriptionCollection endpoints, + EndpointConfiguration endpointConfiguration, + ITransportListener listener, + ICertificateValidator certificateValidator + ) { - // generate a unique host name. - string hostName = String.Empty; - - if (hosts.ContainsKey(hostName)) + // create the stack listener. + try { - hostName = "/Tcp"; - } + TransportListenerSettings settings = new TransportListenerSettings(); - if (hosts.ContainsKey(hostName)) + settings.Descriptions = endpoints; + settings.Configuration = endpointConfiguration; + settings.ServerCertificate = InstanceCertificate; + settings.CertificateValidator = certificateValidator; + settings.NamespaceUris = MessageContext.NamespaceUris; + settings.Factory = MessageContext.Factory; + + listener.Open( + endpointUri, + settings, + GetEndpointInstance(this)); + + TransportListeners.Add(listener); + + listener.ConnectionStatusChanged += OnConnectionStatusChanged; + } + catch (Exception e) { - hostName += Utils.Format("/{0}", hosts.Count); + string message = $"Could not load {endpointUri.Scheme} Stack Listener."; + if (e.InnerException != null) + { + message += (" " + e.InnerException.Message); + } + Utils.Trace(e, message); } - - // build list of uris. - List uris = new List(); - EndpointDescriptionCollection endpoints = new EndpointDescriptionCollection(); - - // create the endpoint configuration to use. - EndpointConfiguration endpointConfiguration = EndpointConfiguration.Create(configuration); - string computerName = Utils.GetHostName(); - - for (int ii = 0; ii < baseAddresses.Count; ii++) - { - // UA TCP and HTTPS endpoints support multiple policies. - if (!baseAddresses[ii].StartsWith(Utils.UriSchemeOpcTcp, StringComparison.Ordinal)) - { - continue; - } - - UriBuilder uri = new UriBuilder(baseAddresses[ii]); - - if (String.Compare(uri.Host, "localhost", StringComparison.OrdinalIgnoreCase) == 0) - { - uri.Host = computerName; - } - - uris.Add(uri.Uri); - - foreach (ServerSecurityPolicy policy in securityPolicies) - { - // create the endpoint description. - EndpointDescription description = new EndpointDescription(); - - description.EndpointUrl = uri.ToString(); - description.Server = serverDescription; - - description.SecurityMode = policy.SecurityMode; - description.SecurityPolicyUri = policy.SecurityPolicyUri; - description.SecurityLevel = ServerSecurityPolicy.CalculateSecurityLevel(policy.SecurityMode, policy.SecurityPolicyUri); - description.UserIdentityTokens = GetUserTokenPolicies(configuration, description); - description.TransportProfileUri = Profiles.UaTcpTransport; - - bool requireEncryption = RequireEncryption(description); - - if (requireEncryption) - { - description.ServerCertificate = InstanceCertificate.RawData; - - // check if complete chain should be sent. - if (configuration.SecurityConfiguration.SendCertificateChain && - InstanceCertificateChain != null && - InstanceCertificateChain.Count > 0) - { - List serverCertificateChain = new List(); - - for (int i = 0; i < InstanceCertificateChain.Count; i++) - { - serverCertificateChain.AddRange(InstanceCertificateChain[i].RawData); - } - - description.ServerCertificate = serverCertificateChain.ToArray(); - } - } - - endpoints.Add(description); - } - - // create the UA-TCP stack listener. - try - { - TransportListenerSettings settings = new TransportListenerSettings(); - - settings.Descriptions = endpoints; - settings.Configuration = endpointConfiguration; - settings.CertificateValidator = configuration.CertificateValidator.GetChannelValidator(); - settings.NamespaceUris = this.MessageContext.NamespaceUris; - settings.Factory = this.MessageContext.Factory; - settings.ServerCertificate = this.InstanceCertificate; - - if (configuration.SecurityConfiguration.SendCertificateChain) - { - settings.ServerCertificateChain = this.InstanceCertificateChain; - } - - ITransportListener listener = new Opc.Ua.Bindings.TcpListener(); - - listener.Open( - uri.Uri, - settings, - GetEndpointInstance(this)); - - TransportListeners.Add(listener); - - listener.ConnectionStatusChanged += OnConnectionStatusChanged; - } - catch (Exception e) - { - Utils.Trace(e, "Could not load UA-TCP Stack Listener."); - throw; - } - } - - return endpoints; } -#if !NO_HTTPS - /// - /// Create a new service host for UA HTTPS. - /// - protected List CreateHttpsServiceHost( - IDictionary hosts, - ApplicationConfiguration configuration, - IList baseAddresses, - ApplicationDescription serverDescription, - List securityPolicies) - { - // generate a unique host name. - string hostName = String.Empty; - - if (hosts.ContainsKey(hostName)) - { - hostName = "/Https"; - } - - if (hosts.ContainsKey(hostName)) - { - hostName += Utils.Format("/{0}", hosts.Count); - } - - // build list of uris. - List uris = new List(); - EndpointDescriptionCollection endpoints = new EndpointDescriptionCollection(); - - // create the endpoint configuration to use. - EndpointConfiguration endpointConfiguration = EndpointConfiguration.Create(configuration); - string computerName = Utils.GetHostName(); - - for (int ii = 0; ii < baseAddresses.Count; ii++) - { - if (!baseAddresses[ii].StartsWith(Utils.UriSchemeHttps, StringComparison.Ordinal)) - { - continue; - } - - UriBuilder uri = new UriBuilder(baseAddresses[ii]); - - if (uri.Path[uri.Path.Length - 1] != '/') - { - uri.Path += "/"; - } - - if (String.Compare(uri.Host, "localhost", StringComparison.OrdinalIgnoreCase) == 0) - { - uri.Host = computerName; - } - - uris.Add(uri.Uri); - - if (uri.Scheme == Utils.UriSchemeHttps) - { - // Can only support one policy with HTTPS - // So pick the first policy with security mode sign and encrypt - ServerSecurityPolicy bestPolicy = null; - foreach (ServerSecurityPolicy policy in securityPolicies) - { - if (policy.SecurityMode != MessageSecurityMode.SignAndEncrypt) - { - continue; - } - - bestPolicy = policy; - break; - } - - // Pick the first policy from the list if no policies with sign and encrypt defined - if (bestPolicy == null) - { - bestPolicy = securityPolicies[0]; - } - - EndpointDescription description = new EndpointDescription(); - - description.EndpointUrl = uri.ToString(); - description.Server = serverDescription; - - if (InstanceCertificate != null) - { - description.ServerCertificate = InstanceCertificate.RawData; - - // check if complete chain should be sent. - if (configuration.SecurityConfiguration.SendCertificateChain && InstanceCertificateChain != null && InstanceCertificateChain.Count > 0) - { - List serverCertificateChain = new List(); - - for (int i = 0; i < InstanceCertificateChain.Count; i++) - { - serverCertificateChain.AddRange(InstanceCertificateChain[i].RawData); - } - - description.ServerCertificate = serverCertificateChain.ToArray(); - } - } - - description.SecurityMode = bestPolicy.SecurityMode; - description.SecurityPolicyUri = bestPolicy.SecurityPolicyUri; - description.SecurityLevel = ServerSecurityPolicy.CalculateSecurityLevel(bestPolicy.SecurityMode, bestPolicy.SecurityPolicyUri); - description.UserIdentityTokens = GetUserTokenPolicies(configuration, description); - description.TransportProfileUri = Profiles.HttpsBinaryTransport; - - endpoints.Add(description); - } - - // create the stack listener. - try - { - TransportListenerSettings settings = new TransportListenerSettings(); - - settings.Descriptions = endpoints; - settings.Configuration = endpointConfiguration; - settings.ServerCertificate = this.InstanceCertificate; - settings.CertificateValidator = configuration.CertificateValidator.GetChannelValidator(); - settings.NamespaceUris = this.MessageContext.NamespaceUris; - settings.Factory = this.MessageContext.Factory; - - ITransportListener listener = new Opc.Ua.Bindings.UaHttpsChannelListener(); - - listener.Open( - uri.Uri, - settings, - GetEndpointInstance(this)); - - TransportListeners.Add(listener); - - listener.ConnectionStatusChanged += OnConnectionStatusChanged; - } - catch (Exception e) - { - string message = "Could not load HTTPS Stack Listener."; - if (e.InnerException != null) - { - message += (" " + e.InnerException.Message); - } - Utils.Trace(e, message); - } - } - - return endpoints; - } -#endif - /// /// Returns the UserTokenPolicies supported by the server. /// /// The configuration. /// The description. /// Returns a collection of UserTokenPolicy objects, the return type is . - protected virtual UserTokenPolicyCollection GetUserTokenPolicies(ApplicationConfiguration configuration, EndpointDescription description) + public virtual UserTokenPolicyCollection GetUserTokenPolicies(ApplicationConfiguration configuration, EndpointDescription description) { UserTokenPolicyCollection policies = new UserTokenPolicyCollection(); diff --git a/Stack/Opc.Ua.Core/Stack/Tcp/ChannelQuotas.cs b/Stack/Opc.Ua.Core/Stack/Tcp/ChannelQuotas.cs index d736acd641..6581fadd57 100644 --- a/Stack/Opc.Ua.Core/Stack/Tcp/ChannelQuotas.cs +++ b/Stack/Opc.Ua.Core/Stack/Tcp/ChannelQuotas.cs @@ -10,8 +10,6 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ -using System.IdentityModel.Selectors; - namespace Opc.Ua.Bindings { /// diff --git a/Stack/Opc.Ua.Core/Stack/Tcp/TcpMessageSocket.cs b/Stack/Opc.Ua.Core/Stack/Tcp/TcpMessageSocket.cs index 6b30a3bf36..223e186b54 100644 --- a/Stack/Opc.Ua.Core/Stack/Tcp/TcpMessageSocket.cs +++ b/Stack/Opc.Ua.Core/Stack/Tcp/TcpMessageSocket.cs @@ -39,6 +39,11 @@ namespace Opc.Ua.Bindings /// public class TcpTransportChannelFactory : ITransportChannelFactory { + /// + /// The protocol supported by the channel. + /// + public string UriScheme => Utils.UriSchemeOpcTcp; + /// /// The method creates a new instance of a TCP transport channel /// @@ -311,7 +316,6 @@ namespace Opc.Ua.Bindings /// The socket handle. public int Handle => m_socket != null ? m_socket.GetHashCode() : -1; - /// /// Gets the transport channel features implemented by this message socket. /// diff --git a/Stack/Opc.Ua.Core/Stack/Tcp/TcpServiceHost.cs b/Stack/Opc.Ua.Core/Stack/Tcp/TcpServiceHost.cs new file mode 100644 index 0000000000..5f3a6c5ca3 --- /dev/null +++ b/Stack/Opc.Ua.Core/Stack/Tcp/TcpServiceHost.cs @@ -0,0 +1,142 @@ +/* Copyright (c) 1996-2020 The OPC Foundation. All rights reserved. + The source code in this file is covered under a dual-license scenario: + - RCL: for OPC Foundation members in good-standing + - GPL V2: everybody else + RCL license terms accompanied with this source code. See http://opcfoundation.org/License/RCL/1.00/ + GNU General Public License as published by the Free Software Foundation; + version 2 of the License are accompanied with this source code. See http://opcfoundation.org/License/GPLv2 + This source code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +*/ + +using System; +using System.Collections.Generic; +using System.Security.Cryptography.X509Certificates; +using System.Threading.Tasks; + +namespace Opc.Ua.Bindings +{ + /// + /// Creates a new Tcp service host. + /// + /// + /// This class can be used by a transport which + /// implements the + /// + public abstract class TcpServiceHost : ITransportListenerFactory + { + /// + public abstract string UriScheme { get; } + + /// + public abstract ITransportListener Create(); + + /// + /// + /// Create a new service host for UA TCP. + /// + public List CreateServiceHost( + ServerBase serverBase, + IDictionary hosts, + ApplicationConfiguration configuration, + IList baseAddresses, + ApplicationDescription serverDescription, + List securityPolicies, + X509Certificate2 instanceCertificate, + X509Certificate2Collection instanceCertificateChain) + { + // generate a unique host name. + string hostName = String.Empty; + + if (hosts.ContainsKey(hostName)) + { + hostName = "/Tcp"; + } + + if (hosts.ContainsKey(hostName)) + { + hostName += Utils.Format("/{0}", hosts.Count); + } + + // build list of uris. + List uris = new List(); + EndpointDescriptionCollection endpoints = new EndpointDescriptionCollection(); + + // create the endpoint configuration to use. + EndpointConfiguration endpointConfiguration = EndpointConfiguration.Create(configuration); + string computerName = Utils.GetHostName(); + + for (int ii = 0; ii < baseAddresses.Count; ii++) + { + // UA TCP and HTTPS endpoints support multiple policies. + if (!baseAddresses[ii].StartsWith(Utils.UriSchemeOpcTcp, StringComparison.Ordinal)) + { + continue; + } + + UriBuilder uri = new UriBuilder(baseAddresses[ii]); + + if (String.Compare(uri.Host, "localhost", StringComparison.OrdinalIgnoreCase) == 0) + { + uri.Host = computerName; + } + + ITransportListener listener = this.Create(); + if (listener != null) + { + uris.Add(uri.Uri); + + foreach (ServerSecurityPolicy policy in securityPolicies) + { + // create the endpoint description. + EndpointDescription description = new EndpointDescription(); + + description.EndpointUrl = uri.ToString(); + description.Server = serverDescription; + + description.SecurityMode = policy.SecurityMode; + description.SecurityPolicyUri = policy.SecurityPolicyUri; + description.SecurityLevel = ServerSecurityPolicy.CalculateSecurityLevel(policy.SecurityMode, policy.SecurityPolicyUri); + description.UserIdentityTokens = serverBase.GetUserTokenPolicies(configuration, description); + description.TransportProfileUri = Profiles.UaTcpTransport; + + bool requireEncryption = ServerBase.RequireEncryption(description); + + if (requireEncryption) + { + description.ServerCertificate = instanceCertificate.RawData; + + // check if complete chain should be sent. + if (configuration.SecurityConfiguration.SendCertificateChain && + instanceCertificateChain != null && + instanceCertificateChain.Count > 0) + { + List serverCertificateChain = new List(); + + for (int i = 0; i < instanceCertificateChain.Count; i++) + { + serverCertificateChain.AddRange(instanceCertificateChain[i].RawData); + } + + description.ServerCertificate = serverCertificateChain.ToArray(); + } + } + + endpoints.Add(description); + } + + serverBase.CreateServiceHostEndpoint(uri.Uri, endpoints, endpointConfiguration, listener, + configuration.CertificateValidator.GetChannelValidator() + ); + } + else + { + Utils.Trace(Utils.TraceMasks.Error, "Failed to create endpoint {0} because the transport profile is unsupported.", uri); + } + } + + return endpoints; + } + } +} diff --git a/Stack/Opc.Ua.Core/Stack/Tcp/TcpListener.cs b/Stack/Opc.Ua.Core/Stack/Tcp/TcpTransportListener.cs similarity index 97% rename from Stack/Opc.Ua.Core/Stack/Tcp/TcpListener.cs rename to Stack/Opc.Ua.Core/Stack/Tcp/TcpTransportListener.cs index 3e3e31e680..640b86b94a 100644 --- a/Stack/Opc.Ua.Core/Stack/Tcp/TcpListener.cs +++ b/Stack/Opc.Ua.Core/Stack/Tcp/TcpTransportListener.cs @@ -20,9 +20,24 @@ using System.Threading.Tasks; namespace Opc.Ua.Bindings { /// - /// Manages the connections for a UA TCP server. + /// Creates a new TcpTransportListener with ITransportListener interface. /// - public class TcpListener : ITransportListener, ITcpChannelListener + public class TcpTransportListenerFactory : TcpServiceHost + { + /// + public override string UriScheme => Utils.UriSchemeOpcTcp; + + /// + public override ITransportListener Create() + { + return new TcpTransportListener(); + } + } + + /// + /// Manages the transport for a UA TCP server. + /// + public class TcpTransportListener : ITransportListener, ITcpChannelListener { #region IDisposable Members /// diff --git a/Stack/Opc.Ua.Core/Stack/Transport/ITransportChannel.cs b/Stack/Opc.Ua.Core/Stack/Transport/ITransportChannel.cs index 8b6a65535c..18c45c50fb 100644 --- a/Stack/Opc.Ua.Core/Stack/Transport/ITransportChannel.cs +++ b/Stack/Opc.Ua.Core/Stack/Transport/ITransportChannel.cs @@ -15,18 +15,6 @@ using Opc.Ua.Bindings; namespace Opc.Ua { - /// - /// This is an interface to a channel which supports a factory - /// - public interface ITransportChannelFactory - { - /// - /// The method creates a new transport channel - /// - /// the transport channel - ITransportChannel Create(); - } - /// /// This is an interface to a channel which supports /// diff --git a/Stack/Opc.Ua.Core/Stack/Transport/TransportListenerSettings.cs b/Stack/Opc.Ua.Core/Stack/Transport/TransportListenerSettings.cs index 050c659f3a..0122b590f0 100644 --- a/Stack/Opc.Ua.Core/Stack/Transport/TransportListenerSettings.cs +++ b/Stack/Opc.Ua.Core/Stack/Transport/TransportListenerSettings.cs @@ -10,7 +10,6 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ -using System.IdentityModel.Selectors; using System.Security.Cryptography.X509Certificates; namespace Opc.Ua diff --git a/Stack/Opc.Ua.Core/Types/Encoders/EncodeableFactory.cs b/Stack/Opc.Ua.Core/Types/Encoders/EncodeableFactory.cs index ae5e38eece..ce67819561 100644 --- a/Stack/Opc.Ua.Core/Types/Encoders/EncodeableFactory.cs +++ b/Stack/Opc.Ua.Core/Types/Encoders/EncodeableFactory.cs @@ -55,7 +55,7 @@ namespace Opc.Ua public EncodeableFactory(bool shared) { m_encodeableTypes = new Dictionary(); - AddEncodeableTypes("Opc.Ua.Core"); + AddEncodeableTypes(Utils.DefaultOpcUaCoreAssemblyFullName); #if DEBUG m_instanceId = Interlocked.Increment(ref m_globalInstanceCount); diff --git a/Stack/Opc.Ua.Core/Types/Utils/Utils.cs b/Stack/Opc.Ua.Core/Types/Utils/Utils.cs index 06d1b2e534..9210aa8e07 100644 --- a/Stack/Opc.Ua.Core/Types/Utils/Utils.cs +++ b/Stack/Opc.Ua.Core/Types/Utils/Utils.cs @@ -25,7 +25,7 @@ using System.Threading.Tasks; using System.Diagnostics; using System.Security.Cryptography; using System.Net; - +using System.Collections.ObjectModel; namespace Opc.Ua { @@ -50,11 +50,30 @@ namespace Opc.Ua /// public const string UriSchemeOpcTcp = "opc.tcp"; + /// + /// The URI scheme for the UA TCP protocol over Secure WebSockets. + /// + public const string UriSchemeOpcWss = "opc.wss"; + + /// + /// The URI schemes which are supported in the core server. + /// + public static readonly string[] DefaultUriSchemes = new string [] + { + Utils.UriSchemeOpcTcp, + Utils.UriSchemeHttps + }; + /// /// The default port for the UA TCP protocol. /// public const int UaTcpDefaultPort = 4840; + /// + /// The default port for the UA TCP protocol over WebSockets. + /// + public const int UaWebSocketsDefaultPort = 4843; + /// /// The urls of the discovery servers on a node. /// @@ -67,11 +86,6 @@ namespace Opc.Ua "http://{0}/UADiscovery/Default.svc" }; - /// - /// The class that provides the default implementation for the UA TCP protocol. - /// - public const string UaTcpBindingDefault = "Opc.Ua.Bindings.UaTcpBinding"; - /// /// The default certificate store's type. /// @@ -87,6 +101,23 @@ namespace Opc.Ua /// public static string DefaultLocalFolder = Directory.GetCurrentDirectory(); + /// + /// The full name of the Opc.Ua.Core assembly. + /// + public static readonly string DefaultOpcUaCoreAssemblyFullName = typeof(Utils).Assembly.GetName().FullName; + + /// + /// The name of the Opc.Ua.Core assembly. + /// + public static readonly string DefaultOpcUaCoreAssemblyName = typeof(Utils).Assembly.GetName().Name; + + /// + /// List of known default bindings hosted in other assemblies. + /// + public static ReadOnlyDictionary DefaultBindings = new ReadOnlyDictionary( + new Dictionary() { + { Utils.UriSchemeHttps, "Opc.Ua.Bindings.Https"} + }); #endregion #region Trace Support @@ -854,7 +885,6 @@ namespace Opc.Ua #region String, Object and Data Convienence Functions private const int MAX_MESSAGE_LENGTH = 1024; - private const uint FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200; private const uint FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000; diff --git a/Tests/Opc.Ua.Core.Tests/Opc.Ua.Core.Tests.csproj b/Tests/Opc.Ua.Core.Tests/Opc.Ua.Core.Tests.csproj index 14221f9736..ae1c254fc8 100644 --- a/Tests/Opc.Ua.Core.Tests/Opc.Ua.Core.Tests.csproj +++ b/Tests/Opc.Ua.Core.Tests/Opc.Ua.Core.Tests.csproj @@ -22,6 +22,7 @@ + diff --git a/Tests/Opc.Ua.Gds.Tests/Opc.Ua.Gds.Tests.csproj b/Tests/Opc.Ua.Gds.Tests/Opc.Ua.Gds.Tests.csproj index f93fef7748..10f96647b0 100644 --- a/Tests/Opc.Ua.Gds.Tests/Opc.Ua.Gds.Tests.csproj +++ b/Tests/Opc.Ua.Gds.Tests/Opc.Ua.Gds.Tests.csproj @@ -22,6 +22,7 @@ + diff --git a/UA Core Library.sln b/UA Core Library.sln index 725b95e149..1985786346 100644 --- a/UA Core Library.sln +++ b/UA Core Library.sln @@ -54,6 +54,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "azurepipelines", "azurepipe .azurepipelines\testcc.yml = .azurepipelines\testcc.yml EndProjectSection EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Opc.Ua.Bindings.Https", "Stack\Opc.Ua.Bindings.Https\Opc.Ua.Bindings.Https.csproj", "{759563A2-AF35-4E7B-B813-082F047C8705}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -100,6 +102,10 @@ Global {7F726313-45CA-4571-AC8E-67DDBC196B21}.Debug|Any CPU.Build.0 = Debug|Any CPU {7F726313-45CA-4571-AC8E-67DDBC196B21}.Release|Any CPU.ActiveCfg = Release|Any CPU {7F726313-45CA-4571-AC8E-67DDBC196B21}.Release|Any CPU.Build.0 = Release|Any CPU + {759563A2-AF35-4E7B-B813-082F047C8705}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {759563A2-AF35-4E7B-B813-082F047C8705}.Debug|Any CPU.Build.0 = Debug|Any CPU + {759563A2-AF35-4E7B-B813-082F047C8705}.Release|Any CPU.ActiveCfg = Release|Any CPU + {759563A2-AF35-4E7B-B813-082F047C8705}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/UA Reference.sln b/UA Reference.sln index 38aa6502f3..ea1a329d34 100644 --- a/UA Reference.sln +++ b/UA Reference.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27130.2010 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30523.141 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Opc.Ua.Server", "Libraries\Opc.Ua.Server\Opc.Ua.Server.csproj", "{6C449A1E-244E-4515-9949-A7E22012537C}" EndProject @@ -37,6 +37,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Reference Server", "Applica EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleReferenceClient", "Applications\ConsoleReferenceClient\ConsoleReferenceClient.csproj", "{8121879E-D2FD-4B6A-97D9-0F6CBC294411}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Opc.Ua.Bindings.Https", "Stack\Opc.Ua.Bindings.Https\Opc.Ua.Bindings.Https.csproj", "{92D98D3D-2A7D-4F4B-8C72-1A98908D0221}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -180,6 +182,18 @@ Global {8121879E-D2FD-4B6A-97D9-0F6CBC294411}.Release|x64.Build.0 = Release|Any CPU {8121879E-D2FD-4B6A-97D9-0F6CBC294411}.Release|x86.ActiveCfg = Release|Any CPU {8121879E-D2FD-4B6A-97D9-0F6CBC294411}.Release|x86.Build.0 = Release|Any CPU + {92D98D3D-2A7D-4F4B-8C72-1A98908D0221}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {92D98D3D-2A7D-4F4B-8C72-1A98908D0221}.Debug|Any CPU.Build.0 = Debug|Any CPU + {92D98D3D-2A7D-4F4B-8C72-1A98908D0221}.Debug|x64.ActiveCfg = Debug|Any CPU + {92D98D3D-2A7D-4F4B-8C72-1A98908D0221}.Debug|x64.Build.0 = Debug|Any CPU + {92D98D3D-2A7D-4F4B-8C72-1A98908D0221}.Debug|x86.ActiveCfg = Debug|Any CPU + {92D98D3D-2A7D-4F4B-8C72-1A98908D0221}.Debug|x86.Build.0 = Debug|Any CPU + {92D98D3D-2A7D-4F4B-8C72-1A98908D0221}.Release|Any CPU.ActiveCfg = Release|Any CPU + {92D98D3D-2A7D-4F4B-8C72-1A98908D0221}.Release|Any CPU.Build.0 = Release|Any CPU + {92D98D3D-2A7D-4F4B-8C72-1A98908D0221}.Release|x64.ActiveCfg = Release|Any CPU + {92D98D3D-2A7D-4F4B-8C72-1A98908D0221}.Release|x64.Build.0 = Release|Any CPU + {92D98D3D-2A7D-4F4B-8C72-1A98908D0221}.Release|x86.ActiveCfg = Release|Any CPU + {92D98D3D-2A7D-4F4B-8C72-1A98908D0221}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/nuget/Opc.Ua.Symbols.nuspec b/nuget/Opc.Ua.Symbols.nuspec index 6c1d3fd39f..ef36589737 100644 --- a/nuget/Opc.Ua.Symbols.nuspec +++ b/nuget/Opc.Ua.Symbols.nuspec @@ -14,6 +14,7 @@ OPCFoundation OPC-UA netstandard ios linux dotnet net netcore uwp + diff --git a/nuget/Opc.Ua.nuspec b/nuget/Opc.Ua.nuspec index 83fa9d8771..02733abec4 100644 --- a/nuget/Opc.Ua.nuspec +++ b/nuget/Opc.Ua.nuspec @@ -14,6 +14,7 @@ OPCFoundation OPC-UA netstandard ios linux dotnet net netcore uwp +