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
This commit is contained in:
Martin Regen
2020-10-21 00:03:37 +02:00
committed by GitHub
parent 3514e4d5fe
commit 62eedc19be
43 changed files with 1386 additions and 827 deletions
+5
View File
@@ -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
+3
View File
@@ -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
@@ -6,12 +6,15 @@
<OutputType>Exe</OutputType>
<PackageId>ConsoleReferenceClient</PackageId>
<Company>OPC Foundation</Company>
<Product>OPC UA SDK</Product>
<Description>.NET Console Reference Client</Description>
<Copyright>Copyright © 2004-2020 OPC Foundation, Inc</Copyright>
<RootNamespace>Quickstarts.ConsoleReferenceClient</RootNamespace>
</PropertyGroup>
<ItemGroup Condition=" '$(NoHttps)' != 'true' ">
<ProjectReference Include="..\..\Stack\Opc.Ua.Bindings.Https\Opc.Ua.Bindings.Https.csproj" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Stack\Opc.Ua.Core\Opc.Ua.Core.csproj" />
<ProjectReference Include="..\..\Libraries\Opc.Ua.Configuration\Opc.Ua.Configuration.csproj" />
@@ -33,7 +33,7 @@ using Opc.Ua.Configuration;
namespace Quickstarts.ConsoleReferenceClient
{
public class Program
public static class Program
{
public static void Main(string[] args)
{
@@ -6,7 +6,6 @@
<OutputType>Exe</OutputType>
<PackageId>ConsoleReferenceServer</PackageId>
<Company>OPC Foundation</Company>
<Product>OPC UA SDK</Product>
<Description>.NET Core Reference Server</Description>
<Copyright>Copyright © 2004-2020 OPC Foundation, Inc</Copyright>
</PropertyGroup>
@@ -19,6 +18,10 @@
<Compile Include="..\ReferenceServer\ReferenceNodeManager.cs;..\ReferenceServer\ReferenceServer.cs;..\ReferenceServer\ReferenceServerConfiguration.cs" Exclude="bin\**;obj\**;**\*.xproj;packages\**" />
</ItemGroup>
<ItemGroup Condition=" '$(NoHttps)' != 'true' ">
<ProjectReference Include="..\..\Stack\Opc.Ua.Bindings.Https\Opc.Ua.Bindings.Https.csproj" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Stack\Opc.Ua.Core\Opc.Ua.Core.csproj" />
<ProjectReference Include="..\..\Libraries\Opc.Ua.Configuration\Opc.Ua.Configuration.csproj" />
@@ -35,8 +38,4 @@
</None>
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
</Project>
@@ -120,6 +120,10 @@
</BootstrapperPackage>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Stack\Opc.Ua.Bindings.Https\Opc.Ua.Bindings.Https.csproj">
<Project>{92d98d3d-2a7d-4f4b-8c72-1a98908d0221}</Project>
<Name>Opc.Ua.Bindings.Https</Name>
</ProjectReference>
<ProjectReference Include="..\..\Stack\Opc.Ua.Core\Opc.Ua.Core.csproj">
<Project>{4569aaa2-710b-4e8b-a4c6-f917777814da}</Project>
<Name>Opc.Ua.Core</Name>
@@ -67,6 +67,7 @@
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.IdentityModel" />
<Reference Include="System.Numerics" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.ServiceModel" />
<Reference Include="System.Windows.Forms" />
@@ -134,6 +135,10 @@
</BootstrapperPackage>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Stack\Opc.Ua.Bindings.Https\Opc.Ua.Bindings.Https.csproj">
<Project>{92d98d3d-2a7d-4f4b-8c72-1a98908d0221}</Project>
<Name>Opc.Ua.Bindings.Https</Name>
</ProjectReference>
<ProjectReference Include="..\..\Stack\Opc.Ua.Core\Opc.Ua.Core.csproj">
<Project>{4569aaa2-710b-4e8b-a4c6-f917777814da}</Project>
<Name>Opc.Ua.Core</Name>
@@ -26,6 +26,7 @@
<ItemGroup Condition="'$(TargetFramework)' == 'net462'">
<Reference Include="System.Reflection" />
<Reference Include="System.Numerics" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp2.0'">
@@ -19,10 +19,6 @@
<ProjectReference Include="..\..\Stack\Opc.Ua.Core\Opc.Ua.Core.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net462'">
<Reference Include="System.IdentityModel" />
<Reference Include="System.ServiceModel" />
@@ -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
@@ -16,17 +16,9 @@
<PackageId>$(PackageId).Debug</PackageId>
</PropertyGroup>
<PropertyGroup>
<DefineConstants Condition=" '$(NoHttps)' == 'true' ">$(DefineConstants);NO_HTTPS</DefineConstants>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Stack\Opc.Ua.Core\Opc.Ua.Core.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net462'">
<Reference Include="System.IdentityModel" />
@@ -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
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,51 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<DefineConstants>$(DefineConstants);NET_STANDARD</DefineConstants>
<TargetFrameworks>$(LibTargetFrameworks)</TargetFrameworks>
<LangVersion>6</LangVersion>
<AssemblyName>Opc.Ua.Bindings.Https</AssemblyName>
<PackageId>OPCFoundation.NetStandard.Opc.Ua.Bindings.Https</PackageId>
<RootNamespace>Opc.Ua.Bindings</RootNamespace>
<Description>OPC UA Https Binding Library</Description>
<IsPackable>true</IsPackable>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<PackageId>$(PackageId).Debug</PackageId>
</PropertyGroup>
<PropertyGroup Condition="'$(SignAssembly)' == 'true'">
<DefineConstants>$(DefineConstants);SIGNASSEMBLY</DefineConstants>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net462'">
<Reference Include="System.Net.Http" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets" Version="2.2.1" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel.Https" Version="2.2.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="System.Net.Http" Version="4.3.4" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.1'">
<PackageReference Include="System.Net.Http" Version="4.3.4" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Opc.Ua.Core\Opc.Ua.Core.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
<Target Name="GetPackagingOutputs" />
</Project>
@@ -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
@@ -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
{
/// <summary>
/// Creates a new <see cref="HttpsTransportListener"/> with
/// <see cref="ITransportListener"/> interface.
/// </summary>
public abstract class HttpsServiceHost : ITransportListenerFactory
{
/// <summary>
/// The protocol supported by the listener.
/// </summary>
public abstract string UriScheme { get; }
/// <summary>
/// The method creates a new instance of a <see cref="HttpsTransportListener"/>.
/// </summary>
/// <returns>The transport listener.</returns>
public abstract ITransportListener Create();
/// <inheritdoc/>
/// <summary>
/// Create a new service host for UA HTTPS.
/// </summary>
public List<EndpointDescription> CreateServiceHost(
ServerBase serverBase,
IDictionary<string, Task> hosts,
ApplicationConfiguration configuration,
IList<string> baseAddresses,
ApplicationDescription serverDescription,
List<ServerSecurityPolicy> 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<Uri> uris = new List<Uri>();
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<byte> serverCertificateChain = new List<byte>();
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;
}
}
}
@@ -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
{
/// <summary>
/// Creates a new HttpsTransportChannel with ITransportChannel interface.
/// </summary>
public class HttpsTransportChannelFactory : ITransportChannelFactory
{
/// <summary>
/// The protocol supported by the channel.
/// </summary>
public string UriScheme => Utils.UriSchemeHttps;
/// <summary>
/// The method creates a new instance of a Https transport channel
/// </summary>
/// <returns>The transport channel</returns>
public ITransportChannel Create()
{
return new HttpsTransportChannel();
}
}
/// <summary>
/// Wraps the HttpsTransportChannel and provides an ITransportChannel implementation.
/// </summary>
@@ -34,34 +52,22 @@ namespace Opc.Ua.Bindings
}
/// <inheritdoc/>
public TransportChannelFeatures SupportedFeatures
{
get { return TransportChannelFeatures.Open | TransportChannelFeatures.Reconnect | TransportChannelFeatures.BeginSendRequest; }
}
public string UriScheme => Utils.UriSchemeHttps;
/// <inheritdoc/>
public EndpointDescription EndpointDescription
{
get { return m_settings.Description; }
}
public TransportChannelFeatures SupportedFeatures => TransportChannelFeatures.Open | TransportChannelFeatures.Reconnect | TransportChannelFeatures.BeginSendRequest;
/// <inheritdoc/>
public EndpointConfiguration EndpointConfiguration
{
get { return m_settings.Configuration; }
}
public EndpointDescription EndpointDescription => m_settings.Description;
/// <inheritdoc/>
public ServiceMessageContext MessageContext
{
get { return m_quotas.MessageContext; }
}
public EndpointConfiguration EndpointConfiguration => m_settings.Configuration;
/// <inheritdoc/>
public ChannelToken CurrentToken
{
get { return null; }
}
public ServiceMessageContext MessageContext => m_quotas.MessageContext;
/// <inheritdoc/>
public ChannelToken CurrentToken => null;
/// <inheritdoc/>
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
@@ -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
{
/// <summary>
/// Implements the startup of the Https listener.
/// Creates a new <see cref="HttpsTransportListener"/> with
/// <see cref="ITransportListener"/> interface.
/// </summary>
public class HttpsTransportListenerFactory : HttpsServiceHost
{
/// <summary>
/// The protocol supported by the listener.
/// </summary>
public override string UriScheme => Utils.UriSchemeHttps;
/// <summary>
/// The method creates a new instance of a <see cref="HttpsTransportListener"/>.
/// </summary>
/// <returns>The transport listener.</returns>
public override ITransportListener Create()
{
return new HttpsTransportListener();
}
}
/// <summary>
/// Implements the kestrel startup of the Https listener.
/// </summary>
public class Startup
{
/// <summary>
/// Get the Https listener.
/// </summary>
public static UaHttpsChannelListener Listener { get; set; }
public static HttpsTransportListener Listener { get; set; }
/// <summary>
/// Configure the request pipeline for the listener.
@@ -72,13 +91,13 @@ namespace Opc.Ua.Bindings
/// <summary>
/// Manages the connections for a UA HTTPS server.
/// </summary>
public partial class UaHttpsChannelListener : ITransportListener
public class HttpsTransportListener : ITransportListener
{
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="UaHttpsChannelListener"/> class.
/// Initializes a new instance of the <see cref="HttpsTransportListener"/> class.
/// </summary>
public UaHttpsChannelListener()
public HttpsTransportListener()
{
}
#endregion
@@ -381,5 +400,3 @@ namespace Opc.Ua.Bindings
#endregion
}
}
#endif
@@ -13,7 +13,7 @@
namespace Opc.Ua.Bindings
{
/// <summary>
/// The <b>Opc.Ua.Bindings</b> namespace contains classes that implement the WCF bindings for the
/// The <b>Opc.Ua.Bindings</b> 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.
/// </summary>
-17
View File
@@ -16,10 +16,6 @@
<PackageId>$(PackageId).Debug</PackageId>
</PropertyGroup>
<PropertyGroup>
<DefineConstants Condition=" '$(NoHttps)' == 'true' ">$(DefineConstants);NO_HTTPS</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(SignAssembly)' == 'true'">
<DefineConstants>$(DefineConstants);SIGNASSEMBLY</DefineConstants>
</PropertyGroup>
@@ -47,33 +43,20 @@
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net462'">
<Reference Include="System.IdentityModel" />
<Reference Include="System.IdentityModel.Selectors" />
<Reference Include="System.Security" />
<Reference Include="System.ServiceModel" />
<Reference Include="System.Net.Http" />
</ItemGroup>
<ItemGroup Condition="$(DefineConstants.Contains('NO_HTTPS'))=='false'">
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets" Version="2.2.1" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel.Https" Version="2.2.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="System.Data.Common" Version="4.3.0" />
<PackageReference Include="System.ServiceModel.Primitives" Version="4.5.3" />
<PackageReference Include="System.Net.NameResolution" Version="4.3.0" />
<PackageReference Include="System.Reflection.TypeExtensions" Version="4.4.0" />
<PackageReference Include="System.Net.Http" Version="4.3.4" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.1'">
<PackageReference Include="System.Data.Common" Version="4.3.0" />
<PackageReference Include="System.ServiceModel.Primitives" Version="4.7.0" />
<PackageReference Include="System.Net.NameResolution" Version="4.3.0" />
<PackageReference Include="System.Reflection.TypeExtensions" Version="4.7.0" />
<PackageReference Include="System.Net.Http" Version="4.3.4" />
</ItemGroup>
<ItemGroup>
@@ -102,7 +102,12 @@ namespace Opc.Ua
/// Communicates with UA TCP, UA Security and UA Binary.
/// </summary>
public const string UaTcpTransport = "http://opcfoundation.org/UA-Profile/Transport/uatcp-uasc-uabinary";
/// <summary>
/// Communicates with UA TCP over secure Websockets, UA Security and UA Binary.
/// </summary>
public const string UaWssTransport = "http://opcfoundation.org/UA-Profile/Transport/uawss-uasc-uabinary";
/// <summary>
/// Communicates with UA Binary over HTTPS.
/// </summary>
@@ -10,14 +10,12 @@
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
using System.ServiceModel.Channels;
namespace Opc.Ua.Bindings
{
/// <summary>
/// The binding for the UA native stack
/// </summary>
public abstract class BaseBinding : Binding
public abstract class BaseBinding
{
#region Constructors
/// <summary>
@@ -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
{
/// <summary>
/// This is an interface to the scheme of a transport binding.
/// </summary>
public interface ITransportBindingScheme
{
/// <summary>
/// The protocol scheme supported by the binding.
/// </summary>
string UriScheme { get; }
}
/// <summary>
/// This is an interface to the factory of a transport binding.
/// </summary>
public interface ITransportBindingFactory<T>
: ITransportBindingScheme
{
/// <summary>
/// The factory to create a new transport.
/// </summary>
/// <returns>The transport.</returns>
T Create();
}
/// <summary>
/// The interface to manage transport bindings.
/// </summary>
public interface ITransportBindings<T>
{
/// <summary>
/// Get a transport binding for a uri scheme.
/// </summary>
/// <param name="uriScheme">The uri scheme.</param>
T GetBinding(string uriScheme);
/// <summary>
/// Return if there is a transport listener for a uri scheme.
/// </summary>
/// <param name="uriScheme">The uri scheme.</param>
bool HasBinding(string uriScheme);
/// <summary>
/// Set the transport factory to the binding.
/// Overrides other bindings with the same uri scheme.
/// </summary>
void SetBinding(T binding);
/// <summary>
/// Add all bindings with interface <see cref="T"/> exported from a assembly.
/// </summary>
/// <param name="assembly">The assembly with the bindings.</param>
IEnumerable<Type> AddBindings(Assembly assembly);
/// <summary>
/// Add all bindings with interface <see cref="T"/> from a list.
/// </summary>
/// <param name="bindings">The array of binding types with interface <see cref="T"/>.</param>
IEnumerable<Type> AddBindings(IEnumerable<Type> bindings);
}
/// <summary>
/// This is the transport listener factory interface for a binding (server).
/// </summary>
public interface ITransportListenerFactory :
ITransportBindingFactory<ITransportListener>
{
/// <summary>
/// Create the service host for a server using <see cref="ServerBase"/>
/// </summary>
/// <param name="serverBase">The server base.</param>
/// <param name="hosts">The service hosts are added to this list.</param>
/// <param name="configuration">The application configuration.</param>
/// <param name="baseAddresses">The base addreses for the service host.</param>
/// <param name="serverDescription">The server description.</param>
/// <param name="securityPolicies">The list of supported security policies.</param>
/// <param name="instanceCertificate">The server certificate.</param>
/// <param name="instanceCertificateChain">The cert cahin of the server certificate.</param>
List<EndpointDescription> CreateServiceHost(
ServerBase serverBase,
IDictionary<string, Task> hosts,
ApplicationConfiguration configuration,
IList<string> baseAddresses,
ApplicationDescription serverDescription,
List<ServerSecurityPolicy> securityPolicies,
X509Certificate2 instanceCertificate,
X509Certificate2Collection instanceCertificateChain
);
}
/// <summary>
/// This is the transport channel factory interface for a binding (client).
/// </summary>
public interface ITransportChannelFactory :
ITransportBindingFactory<ITransportChannel>
{
}
}
@@ -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
{
/// <summary>
/// The transpaort bindings available for teh stack.
/// </summary>
public static class TransportBindings
{
static TransportBindings()
{
Channels = new TransportChannelBindings(new Type[] { typeof(TcpTransportChannelFactory) });
Listeners = new TransportListenerBindings(new Type[] { typeof(TcpTransportListenerFactory) });
}
/// <summary>
/// The bindings for transport channels (client).
/// </summary>
public static TransportChannelBindings Channels { get; private set; }
/// <summary>
/// The bindings for transport listeners (server).
/// </summary>
public static TransportListenerBindings Listeners { get; private set; }
/// <summary>
/// The bindings for the transport channels.
/// </summary>
public class TransportChannelBindings :
TransportBindingsBase<ITransportChannelFactory>
{
/// <summary>
/// Initialize the transport listener.
/// </summary>
/// <param name="defaultBindings">List of known default bindings.</param>
public TransportChannelBindings(Type[] defaultBindings) : base(defaultBindings)
{
}
/// <summary>
/// Get a transport channel for the specified uri scheme.
/// </summary>
/// <param name="uriScheme">The uri scheme of the transport.</param>
public ITransportChannel GetChannel(string uriScheme)
{
var binding = GetBinding(uriScheme);
return binding?.Create();
}
}
/// <summary>
/// The bindings for the transport listeners.
/// </summary>
public class TransportListenerBindings :
TransportBindingsBase<ITransportListenerFactory>
{
/// <summary>
/// Initialize the transport listener.
/// </summary>
/// <param name="defaultBindings">List of known default bindings.</param>
public TransportListenerBindings(Type[] defaultBindings) : base(defaultBindings)
{
}
/// <summary>
/// Get a transport listener for the specified uri scheme.
/// </summary>
/// <param name="uriScheme">The uri scheme of the transport.</param>
public ITransportListener GetListener(string uriScheme)
{
var binding = GetBinding(uriScheme);
return binding?.Create();
}
}
}
}
@@ -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
{
/// <summary>
/// The bindings for the transport listeners.
/// </summary>
public class TransportBindingsBase<T> :
ITransportBindings<T> where T : class, ITransportBindingScheme
{
/// <summary>
/// Implement the default constructor.
/// </summary>
/// <remarks>
/// The default constructor adds all interfaces T.
/// </remarks>
protected TransportBindingsBase()
{
Bindings = new Dictionary<string, T>();
AddBindings(typeof(TransportBindingsBase<T>).Assembly);
}
/// <summary>
/// Initialize object with default list of bindings.
/// </summary>
protected TransportBindingsBase(Type[] defaultBindings)
{
Bindings = new Dictionary<string, T>();
AddBindings(defaultBindings);
}
#region Public Properties
/// <summary>
/// Dictionary of bindings of type <see cref="T"/>.
/// </summary>
protected Dictionary<string, T> Bindings { get; private set; }
#endregion
#region ITransportBindings
/// <inheritdoc/>
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;
}
/// <inheritdoc/>
public bool HasBinding(string uriScheme)
{
T binding;
if (Bindings.TryGetValue(uriScheme, out binding))
{
return true;
}
return false;
}
/// <inheritdoc/>
public void SetBinding(T binding)
{
Bindings[binding.UriScheme] = binding;
}
/// <inheritdoc/>
public IEnumerable<Type> AddBindings(Assembly assembly)
{
var bindings = assembly.GetExportedTypes().Where(type => IsBindingType(type));
return AddBindings(bindings);
}
/// <inheritdoc/>
public IEnumerable<Type> AddBindings(IEnumerable<Type> bindings)
{
var result = new List<Type>();
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
/// <summary>
/// Validate the type is a transport listener.
/// </summary>
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;
}
/// <summary>
/// Try to load a binding from well known assemblies at runtime.
/// </summary>
/// <param name="scheme">The uri scheme of the binding.</param>
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
}
}
@@ -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
{
/// <summary>
/// The binding for the transport listeners.
/// </summary>
public static class TransportListenerBindings
{
static TransportListenerBindings()
{
TransportListeners = new Dictionary<string, Type>();
var listeners = typeof(TransportListenerBindings).Assembly.GetExportedTypes().Where(type => IsTransportListenerType(type));
foreach (var listenerType in listeners)
{
Add(listenerType);
}
}
#region Public Properties
/// <summary>
/// Dictionary of transported listeners.
/// </summary>
public static Dictionary<string, Type> TransportListeners { get; private set; }
/// <summary>
/// Get a transport listener for a uri scheme.
/// </summary>
/// <param name="uriScheme">The uri scheme.</param>
public static ITransportListener GetTransportListener(string uriScheme)
{
Type listenerType;
if (TransportListeners.TryGetValue(uriScheme, out listenerType))
{
return Activator.CreateInstance(listenerType) as ITransportListener;
}
return null;
}
/// <summary>
/// Add a transport listener to the binding.
/// </summary>
public static void Add(Type listenerType)
{
if (IsTransportListenerType(listenerType))
{
var listener = Activator.CreateInstance(listenerType) as ITransportListener;
TransportListeners.Add(listener.UriScheme, listenerType);
}
}
/// <summary>
/// Validate the type is a transport listener.
/// </summary>
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
}
}
@@ -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;
+15 -34
View File
@@ -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.
+40 -262
View File
@@ -699,282 +699,60 @@ namespace Opc.Ua
}
/// <summary>
/// Create a new service host for UA TCP.
/// Create the transport listener for the service host endpoint.
/// </summary>
protected List<EndpointDescription> CreateUaTcpServiceHost(
IDictionary<string, Task> hosts,
ApplicationConfiguration configuration,
IList<string> baseAddresses,
ApplicationDescription serverDescription,
List<ServerSecurityPolicy> securityPolicies)
/// <param name="endpointUri">The endpoint Uri.</param>
/// <param name="endpoints">The description of the endpoints.</param>
/// <param name="endpointConfiguration">The configuration of the endpoints.</param>
/// <param name="listener">The transport listener.</param>
/// <param name="certificateValidator">The certificate validator for the transport.</param>
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<Uri> uris = new List<Uri>();
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<byte> serverCertificateChain = new List<byte>();
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
/// <summary>
/// Create a new service host for UA HTTPS.
/// </summary>
protected List<EndpointDescription> CreateHttpsServiceHost(
IDictionary<string, Task> hosts,
ApplicationConfiguration configuration,
IList<string> baseAddresses,
ApplicationDescription serverDescription,
List<ServerSecurityPolicy> 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<Uri> uris = new List<Uri>();
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<byte> serverCertificateChain = new List<byte>();
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
/// <summary>
/// Returns the UserTokenPolicies supported by the server.
/// </summary>
/// <param name="configuration">The configuration.</param>
/// <param name="description">The description.</param>
/// <returns>Returns a collection of UserTokenPolicy objects, the return type is <seealso cref="UserTokenPolicyCollection"/> . </returns>
protected virtual UserTokenPolicyCollection GetUserTokenPolicies(ApplicationConfiguration configuration, EndpointDescription description)
public virtual UserTokenPolicyCollection GetUserTokenPolicies(ApplicationConfiguration configuration, EndpointDescription description)
{
UserTokenPolicyCollection policies = new UserTokenPolicyCollection();
@@ -10,8 +10,6 @@
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
using System.IdentityModel.Selectors;
namespace Opc.Ua.Bindings
{
/// <summary>
@@ -39,6 +39,11 @@ namespace Opc.Ua.Bindings
/// </summary>
public class TcpTransportChannelFactory : ITransportChannelFactory
{
/// <summary>
/// The protocol supported by the channel.
/// </summary>
public string UriScheme => Utils.UriSchemeOpcTcp;
/// <summary>
/// The method creates a new instance of a TCP transport channel
/// </summary>
@@ -311,7 +316,6 @@ namespace Opc.Ua.Bindings
/// <value>The socket handle.</value>
public int Handle => m_socket != null ? m_socket.GetHashCode() : -1;
/// <summary>
/// Gets the transport channel features implemented by this message socket.
/// </summary>
@@ -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
{
/// <summary>
/// Creates a new Tcp service host.
/// </summary>
/// <remarks>
/// This class can be used by a transport which
/// implements the <see cref="ITransportListenerFactory"/>
/// </remarks>
public abstract class TcpServiceHost : ITransportListenerFactory
{
/// <inheritdoc/>
public abstract string UriScheme { get; }
/// <inheritdoc/>
public abstract ITransportListener Create();
/// <inheritdoc/>
/// <summary>
/// Create a new service host for UA TCP.
/// </summary>
public List<EndpointDescription> CreateServiceHost(
ServerBase serverBase,
IDictionary<string, Task> hosts,
ApplicationConfiguration configuration,
IList<string> baseAddresses,
ApplicationDescription serverDescription,
List<ServerSecurityPolicy> 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<Uri> uris = new List<Uri>();
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<byte> serverCertificateChain = new List<byte>();
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;
}
}
}
@@ -20,9 +20,24 @@ using System.Threading.Tasks;
namespace Opc.Ua.Bindings
{
/// <summary>
/// Manages the connections for a UA TCP server.
/// Creates a new TcpTransportListener with ITransportListener interface.
/// </summary>
public class TcpListener : ITransportListener, ITcpChannelListener
public class TcpTransportListenerFactory : TcpServiceHost
{
/// <inheritdoc/>
public override string UriScheme => Utils.UriSchemeOpcTcp;
/// <inheritdoc/>
public override ITransportListener Create()
{
return new TcpTransportListener();
}
}
/// <summary>
/// Manages the transport for a UA TCP server.
/// </summary>
public class TcpTransportListener : ITransportListener, ITcpChannelListener
{
#region IDisposable Members
/// <summary>
@@ -15,18 +15,6 @@ using Opc.Ua.Bindings;
namespace Opc.Ua
{
/// <summary>
/// This is an interface to a channel which supports a factory
/// </summary>
public interface ITransportChannelFactory
{
/// <summary>
/// The method creates a new transport channel
/// </summary>
/// <returns> the transport channel</returns>
ITransportChannel Create();
}
/// <summary>
/// This is an interface to a channel which supports
/// </summary>
@@ -10,7 +10,6 @@
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
using System.IdentityModel.Selectors;
using System.Security.Cryptography.X509Certificates;
namespace Opc.Ua
@@ -55,7 +55,7 @@ namespace Opc.Ua
public EncodeableFactory(bool shared)
{
m_encodeableTypes = new Dictionary<ExpandedNodeId, System.Type>();
AddEncodeableTypes("Opc.Ua.Core");
AddEncodeableTypes(Utils.DefaultOpcUaCoreAssemblyFullName);
#if DEBUG
m_instanceId = Interlocked.Increment(ref m_globalInstanceCount);
+37 -7
View File
@@ -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
/// </summary>
public const string UriSchemeOpcTcp = "opc.tcp";
/// <summary>
/// The URI scheme for the UA TCP protocol over Secure WebSockets.
/// </summary>
public const string UriSchemeOpcWss = "opc.wss";
/// <summary>
/// The URI schemes which are supported in the core server.
/// </summary>
public static readonly string[] DefaultUriSchemes = new string []
{
Utils.UriSchemeOpcTcp,
Utils.UriSchemeHttps
};
/// <summary>
/// The default port for the UA TCP protocol.
/// </summary>
public const int UaTcpDefaultPort = 4840;
/// <summary>
/// The default port for the UA TCP protocol over WebSockets.
/// </summary>
public const int UaWebSocketsDefaultPort = 4843;
/// <summary>
/// The urls of the discovery servers on a node.
/// </summary>
@@ -67,11 +86,6 @@ namespace Opc.Ua
"http://{0}/UADiscovery/Default.svc"
};
/// <summary>
/// The class that provides the default implementation for the UA TCP protocol.
/// </summary>
public const string UaTcpBindingDefault = "Opc.Ua.Bindings.UaTcpBinding";
/// <summary>
/// The default certificate store's type.
/// </summary>
@@ -87,6 +101,23 @@ namespace Opc.Ua
/// </summary>
public static string DefaultLocalFolder = Directory.GetCurrentDirectory();
/// <summary>
/// The full name of the Opc.Ua.Core assembly.
/// </summary>
public static readonly string DefaultOpcUaCoreAssemblyFullName = typeof(Utils).Assembly.GetName().FullName;
/// <summary>
/// The name of the Opc.Ua.Core assembly.
/// </summary>
public static readonly string DefaultOpcUaCoreAssemblyName = typeof(Utils).Assembly.GetName().Name;
/// <summary>
/// List of known default bindings hosted in other assemblies.
/// </summary>
public static ReadOnlyDictionary<string, string> DefaultBindings = new ReadOnlyDictionary<string, string>(
new Dictionary<string, string>() {
{ 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;
@@ -22,6 +22,7 @@
<ItemGroup>
<ProjectReference Include="..\..\Stack\Opc.Ua.Core\Opc.Ua.Core.csproj" />
<ProjectReference Include="..\..\Stack\Opc.Ua.Bindings.Https\Opc.Ua.Bindings.Https.csproj" />
</ItemGroup>
</Project>
@@ -22,6 +22,7 @@
<ItemGroup>
<ProjectReference Include="..\..\Stack\Opc.Ua.Core\Opc.Ua.Core.csproj" />
<ProjectReference Include="..\..\Stack\Opc.Ua.Bindings.Https\Opc.Ua.Bindings.Https.csproj" />
<ProjectReference Include="..\..\Libraries\Opc.Ua.Client\Opc.Ua.Client.csproj" />
<ProjectReference Include="..\..\Libraries\Opc.Ua.Configuration\Opc.Ua.Configuration.csproj" />
<ProjectReference Include="..\..\Libraries\Opc.Ua.Server\Opc.Ua.Server.csproj" />
+6
View File
@@ -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
+16 -2
View File
@@ -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
+1
View File
@@ -14,6 +14,7 @@
<tags>OPCFoundation OPC-UA netstandard ios linux dotnet net netcore uwp</tags>
<dependencies>
<dependency id="OPCFoundation.NetStandard.Opc.Ua.Core.Debug" version="$version$"/>
<dependency id="OPCFoundation.NetStandard.Opc.Ua.Bindings.Https.Debug" version="$version$"/>
<dependency id="OPCFoundation.NetStandard.Opc.Ua.Configuration.Debug" version="$version$"/>
<dependency id="OPCFoundation.NetStandard.Opc.Ua.Client.Debug" version="$version$"/>
<dependency id="OPCFoundation.NetStandard.Opc.Ua.Server.Debug" version="$version$"/>
+1
View File
@@ -14,6 +14,7 @@
<tags>OPCFoundation OPC-UA netstandard ios linux dotnet net netcore uwp</tags>
<dependencies>
<dependency id="OPCFoundation.NetStandard.Opc.Ua.Core" version="$version$"/>
<dependency id="OPCFoundation.NetStandard.Opc.Ua.Bindings.Https" version="$version$"/>
<dependency id="OPCFoundation.NetStandard.Opc.Ua.Configuration" version="$version$"/>
<dependency id="OPCFoundation.NetStandard.Opc.Ua.Client" version="$version$"/>
<dependency id="OPCFoundation.NetStandard.Opc.Ua.Server" version="$version$"/>