diff --git a/SampleApplications/SDK/Opc.Ua.Client/Session.cs b/SampleApplications/SDK/Opc.Ua.Client/Session.cs
index bcfeef10be..437bcb7777 100644
--- a/SampleApplications/SDK/Opc.Ua.Client/Session.cs
+++ b/SampleApplications/SDK/Opc.Ua.Client/Session.cs
@@ -122,6 +122,7 @@ namespace Opc.Ua.Client
m_handle = template.m_handle;
m_identity = template.m_identity;
m_keepAliveInterval = template.m_keepAliveInterval;
+ m_checkDomain = template.m_checkDomain;
if (copyEventHandlers)
{
@@ -137,7 +138,9 @@ namespace Opc.Ua.Client
this.AddSubscription(new Subscription(subscription, copyEventHandlers));
}
}
+ #endregion
+ #region Private Methods
///
/// Initializes the channel.
///
@@ -270,6 +273,67 @@ namespace Opc.Ua.Client
m_defaultSubscription.Priority = 255;
m_defaultSubscription.PublishingEnabled = true;
}
+
+ private static void CheckCertificateDomain(ConfiguredEndpoint endpoint)
+ {
+ bool domainFound = false;
+
+ X509Certificate2 serverCertificate = new X509Certificate2(endpoint.Description.ServerCertificate);
+
+ // check the certificate domains.
+ IList domains = Utils.GetDomainsFromCertficate(serverCertificate);
+
+ if (domains != null)
+ {
+ string hostname;
+ string dnsHostName = hostname = endpoint.EndpointUrl.DnsSafeHost;
+ bool isLocalHost = false;
+ if (endpoint.EndpointUrl.HostNameType == UriHostNameType.Dns)
+ {
+ if (dnsHostName.ToLowerInvariant() == "localhost")
+ {
+ isLocalHost = true;
+ }
+ else
+ { // strip domain names from hostname
+ hostname = dnsHostName.Split('.')[0];
+ }
+ }
+ else
+ { // dnsHostname is a IPv4 or IPv6 address
+ // normalize ip addresses, cert parser returns normalized addresses
+ hostname = Utils.NormalizedIPAddress(dnsHostName);
+ if (hostname == "127.0.0.1" || hostname == "::1")
+ {
+ isLocalHost = true;
+ }
+ }
+
+ if (isLocalHost)
+ {
+ dnsHostName = Utils.GetFullQualifiedDomainName();
+ hostname = Utils.GetHostName();
+ }
+
+ for (int ii = 0; ii < domains.Count; ii++)
+ {
+ if (String.Compare(hostname, domains[ii], StringComparison.OrdinalIgnoreCase) == 0 ||
+ String.Compare(dnsHostName, domains[ii], StringComparison.OrdinalIgnoreCase) == 0)
+ {
+ domainFound = true;
+ break;
+ }
+ }
+ }
+
+ if (!domainFound)
+ {
+ string message = Utils.Format(
+ "The domain '{0}' is not listed in the server certificate.",
+ endpoint.EndpointUrl.DnsSafeHost);
+ throw new ServiceResultException(StatusCodes.BadCertificateHostNameInvalid, message);
+ }
+ }
#endregion
#region IDisposable Members
@@ -694,8 +758,7 @@ namespace Opc.Ua.Client
}
#endregion
- #region Public Methods
-
+ #region Public Static Methods
///
/// Creates a new communication session with a server by invoking the CreateSession service
///
@@ -813,49 +876,6 @@ namespace Opc.Ua.Client
return session;
}
- private static void CheckCertificateDomain(ConfiguredEndpoint endpoint)
- {
- bool domainFound = false;
-
- if (endpoint.EndpointUrl.HostNameType != UriHostNameType.Dns)
- {
- // ignore endpoints configured with IPv4 / IPv6 addresses
- return;
- }
-
- X509Certificate2 serverCertificate = new X509Certificate2(endpoint.Description.ServerCertificate);
-
- // check the certificate domains.
- IList domains = Utils.GetDomainsFromCertficate(serverCertificate);
-
- if (domains != null)
- {
- string [] hostnames = endpoint.EndpointUrl.DnsSafeHost.Split('.');
-
- if (String.Compare(hostnames[0], "localhost", StringComparison.CurrentCultureIgnoreCase)==0)
- {
- hostnames[0] = Utils.GetHostName();
- }
-
- for (int ii = 0; ii < domains.Count; ii++)
- {
- foreach (string hostname in hostnames)
- {
- if (String.Compare(hostname, domains[ii], StringComparison.CurrentCultureIgnoreCase) == 0)
- {
- domainFound = true;
- break;
- }
- }
- }
-
- if (!domainFound)
- {
- throw new ServiceResultException(StatusCodes.BadCertificateHostNameInvalid);
- }
- }
- }
-
///
/// Recreates a session based on a specified template.
@@ -882,7 +902,8 @@ namespace Opc.Ua.Client
template.m_sessionName,
(uint)template.m_sessionTimeout,
template.m_identity,
- template.m_preferredLocales);
+ template.m_preferredLocales,
+ template.m_checkDomain);
// create the subscriptions.
foreach (Subscription subscription in session.Subscriptions)
@@ -898,7 +919,8 @@ namespace Opc.Ua.Client
return session;
}
-
+ #endregion
+ #region Delegates and Events
///
/// Used to handle renews of user identity tokens before reconnect.
///
@@ -914,7 +936,8 @@ namespace Opc.Ua.Client
}
private event RenewUserIdentityEventHandler m_RenewUserIdentity;
-
+ #endregion
+ #region Public Methods
///
/// Reconnects to the server after a network failure.
///
@@ -1938,11 +1961,11 @@ namespace Opc.Ua.Client
/// If set to true then the domain in the certificate must match the endpoint used.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")]
public void Open(
- string sessionName,
- uint sessionTimeout,
+ string sessionName,
+ uint sessionTimeout,
IUserIdentity identity,
IList preferredLocales,
- bool checkDomain)
+ bool checkDomain)
{
// check connection state.
lock (SyncRoot)
@@ -1991,7 +2014,6 @@ namespace Opc.Ua.Client
// validate the server certificate.
X509Certificate2 serverCertificate = null;
byte[] certificateData = m_endpoint.Description.ServerCertificate;
-
if (certificateData != null && certificateData.Length > 0 && requireEncryption)
{
serverCertificate = Utils.ParseCertificateBlob(certificateData);
@@ -2001,6 +2023,8 @@ namespace Opc.Ua.Client
{
CheckCertificateDomain(m_endpoint);
}
+ // save for reconnect
+ m_checkDomain = checkDomain;
//X509Certificate2Collection certificateChain = Utils.ParseCertificateChainBlob(certificateData);
//if (certificateChain.Count > 0)
@@ -2291,7 +2315,7 @@ namespace Opc.Ua.Client
{
for (int i = 0; i < certificateResults.Count; i++)
{
- Utils.Trace("ActivateSession result[{0}] = {1}", i, certificateResults[i]);
+ Utils.Trace("ActivateSession result[{0}] = {1}", i, certificateResults[i]);
}
}
@@ -2719,7 +2743,8 @@ namespace Opc.Ua.Client
}
}
}
-
+ #endregion
+ #region Close Methods
///
/// Disconnects from the server and frees any network resources.
///
@@ -2805,7 +2830,9 @@ namespace Opc.Ua.Client
Dispose();
return result;
}
+ #endregion
+ #region Subscription Methods
///
/// Adds a subscription to the session.
///
@@ -2909,8 +2936,9 @@ namespace Opc.Ua.Client
return true;
}
+ #endregion
-#region Browse Methods
+ #region Browse Methods
///
/// Invokes the Browse service.
///
@@ -3056,9 +3084,9 @@ namespace Opc.Ua.Client
return responseHeader;
}
-#endregion
+ #endregion
-#region BrowseNext Methods
+ #region BrowseNext Methods
///
/// Invokes the BrowseNext service.
///
@@ -3148,8 +3176,9 @@ namespace Opc.Ua.Client
return responseHeader;
}
-#endregion
+ #endregion
+ #region Call Methods
///
/// Calls the specified method and returns the output arguments.
///
@@ -3204,9 +3233,9 @@ namespace Opc.Ua.Client
return outputArguments;
}
-#endregion
+ #endregion
-#region Protected Methods
+ #region Protected Methods
///
/// Returns the software certificates assigned to the application.
///
@@ -3564,9 +3593,9 @@ namespace Opc.Ua.Client
return true;
}
-#endregion
+ #endregion
-#region Publish Methods
+ #region Publish Methods
///
/// Sends an additional publish request.
///
@@ -3675,7 +3704,7 @@ namespace Opc.Ua.Client
Utils.Trace("Error - Publish call finished. ResultCode={0}; SubscriptionId={1};", code.ToString(), subscriptionId);
}
}
-
+
// nothing more to do if session changed.
if (sessionId != SessionId)
{
@@ -4041,9 +4070,9 @@ namespace Opc.Ua.Client
Utils.Trace(e, "Session: Unexpected rrror while raising Notification event.");
}
}
-#endregion
+ #endregion
-#region Private Fields
+ #region Private Fields
private SubscriptionAcknowledgementCollection m_acknowledgementsToSend;
private Dictionary m_latestAcknowledgementsSent;
private List m_subscriptions;
@@ -4061,6 +4090,7 @@ namespace Opc.Ua.Client
private ConfiguredEndpoint m_endpoint;
private X509Certificate2 m_instanceCertificate;
private X509Certificate2Collection m_instanceCertificateChain;
+ private bool m_checkDomain;
private List m_identityHistory;
private string m_sessionName;
@@ -4094,16 +4124,16 @@ namespace Opc.Ua.Client
private event PublishErrorEventHandler m_PublishError;
private event EventHandler m_SubscriptionsChanged;
private event EventHandler m_SessionClosing;
-#endregion
+ #endregion
}
-#region KeepAliveEventArgs Class
+ #region KeepAliveEventArgs Class
///
/// The event arguments provided when a keep alive response arrives.
///
public class KeepAliveEventArgs : EventArgs
{
-#region Constructors
+ #region Constructors
///
/// Creates a new instance.
///
@@ -4116,9 +4146,9 @@ namespace Opc.Ua.Client
m_currentState = currentState;
m_currentTime = currentTime;
}
-#endregion
+ #endregion
-#region Public Properties
+ #region Public Properties
///
/// Gets the status associated with the keep alive operation.
///
@@ -4151,29 +4181,29 @@ namespace Opc.Ua.Client
get { return m_cancelKeepAlive; }
set { m_cancelKeepAlive = value; }
}
-#endregion
+ #endregion
-#region Private Fields
+ #region Private Fields
private ServiceResult m_status;
private ServerState m_currentState;
private DateTime m_currentTime;
private bool m_cancelKeepAlive;
-#endregion
+ #endregion
}
///
/// The delegate used to receive keep alive notifications.
///
public delegate void KeepAliveEventHandler(Session session, KeepAliveEventArgs e);
-#endregion
+ #endregion
-#region NotificationEventArgs Class
+ #region NotificationEventArgs Class
///
/// Represents the event arguments provided when a new notification message arrives.
///
public class NotificationEventArgs : EventArgs
{
-#region Constructors
+ #region Constructors
///
/// Creates a new instance.
///
@@ -4186,9 +4216,9 @@ namespace Opc.Ua.Client
m_notificationMessage = notificationMessage;
m_stringTable = stringTable;
}
-#endregion
+ #endregion
-#region Public Properties
+ #region Public Properties
///
/// Gets the subscription that the notification applies to.
///
@@ -4212,28 +4242,28 @@ namespace Opc.Ua.Client
{
get { return m_stringTable; }
}
-#endregion
+ #endregion
-#region Private Fields
+ #region Private Fields
private Subscription m_subscription;
private NotificationMessage m_notificationMessage;
private IList m_stringTable;
-#endregion
+ #endregion
}
///
/// The delegate used to receive publish notifications.
///
public delegate void NotificationEventHandler(Session session, NotificationEventArgs e);
-#endregion
+ #endregion
-#region PublishErrorEventArgs Class
+ #region PublishErrorEventArgs Class
///
/// Represents the event arguments provided when a publish error occurs.
///
public class PublishErrorEventArgs : EventArgs
{
-#region Constructors
+ #region Constructors
///
/// Creates a new instance.
///
@@ -4251,9 +4281,9 @@ namespace Opc.Ua.Client
m_subscriptionId = subscriptionId;
m_sequenceNumber = sequenceNumber;
}
-#endregion
+ #endregion
-#region Public Properties
+ #region Public Properties
///
/// Gets the status associated with the keep alive operation.
///
@@ -4277,18 +4307,18 @@ namespace Opc.Ua.Client
{
get { return m_sequenceNumber; }
}
-#endregion
+ #endregion
-#region Private Fields
+ #region Private Fields
private uint m_subscriptionId;
private uint m_sequenceNumber;
private ServiceResult m_status;
-#endregion
+ #endregion
}
///
/// The delegate used to receive pubish error notifications.
///
public delegate void PublishErrorEventHandler(Session session, PublishErrorEventArgs e);
-#endregion
+ #endregion
}
diff --git a/SampleApplications/SDK/Opc.Ua.Client/SessionReconnectHandler.cs b/SampleApplications/SDK/Opc.Ua.Client/SessionReconnectHandler.cs
index c66c24f8c7..10c53c4078 100644
--- a/SampleApplications/SDK/Opc.Ua.Client/SessionReconnectHandler.cs
+++ b/SampleApplications/SDK/Opc.Ua.Client/SessionReconnectHandler.cs
@@ -28,10 +28,7 @@
* ======================================================================*/
using System;
-using System.Collections.Generic;
-using System.Text;
using System.Threading;
-using Opc.Ua;
namespace Opc.Ua.Client
{
@@ -39,7 +36,7 @@ namespace Opc.Ua.Client
/// Attempts to reconnect to the server.
///
public class SessionReconnectHandler : IDisposable
- {
+ {
#region IDisposable Members
///
/// Frees any unmanaged resources.
@@ -66,8 +63,8 @@ namespace Opc.Ua.Client
}
}
}
- #endregion
-
+ #endregion
+
#region Public Methods
///
/// Gets the session managed by the handler.
@@ -164,14 +161,16 @@ namespace Opc.Ua.Client
ServiceResultException sre = exception as ServiceResultException;
// check if the server endpoint could not be reached.
- if ((sre != null && (sre.StatusCode == StatusCodes.BadTcpInternalError || sre.StatusCode == StatusCodes.BadCommunicationError)) ||
+ if ((sre != null &&
+ (sre.StatusCode == StatusCodes.BadTcpInternalError ||
+ sre.StatusCode == StatusCodes.BadCommunicationError ||
+ sre.StatusCode == StatusCodes.BadNotConnected)) ||
exception is System.ServiceModel.EndpointNotFoundException)
{
// check if reconnecting is still an option.
if (m_session.LastKeepAliveTime.AddMilliseconds(m_session.SessionTimeout) > DateTime.UtcNow)
{
Utils.Trace("Calling OnReconnectSession in {0} ms.", m_reconnectPeriod);
-
return false;
}
}
diff --git a/SampleApplications/Samples/Controls.Net4/ClientForm.cs b/SampleApplications/Samples/Controls.Net4/ClientForm.cs
index 014432d82c..3f9878e5f4 100644
--- a/SampleApplications/Samples/Controls.Net4/ClientForm.cs
+++ b/SampleApplications/Samples/Controls.Net4/ClientForm.cs
@@ -193,7 +193,7 @@ namespace Opc.Ua.Sample.Controls
}
catch (Exception exception)
{
- GuiUtils.HandleException(this.Text, MethodBase.GetCurrentMethod(), exception);
+ GuiUtils.HandleException(this.Text, MethodBase.GetCurrentMethod(), exception);
e.UpdateControl = false;
}
}
@@ -206,7 +206,7 @@ namespace Opc.Ua.Sample.Controls
}
catch (Exception)
{
- // GuiUtils.HandleException(this.Text, MethodBase.GetCurrentMethod(), exception);
+ // GuiUtils.HandleException(this.Text, MethodBase.GetCurrentMethod(), exception);
}
}
@@ -349,7 +349,7 @@ namespace Opc.Ua.Sample.Controls
}
catch (Exception exception)
{
- GuiUtils.HandleException(this.Text, MethodBase.GetCurrentMethod(), exception);
+ GuiUtils.HandleException(this.Text, MethodBase.GetCurrentMethod(), exception);
}
}
@@ -387,7 +387,7 @@ namespace Opc.Ua.Sample.Controls
}
catch (Exception exception)
{
- GuiUtils.HandleException(this.Text, MethodBase.GetCurrentMethod(), exception);
+ GuiUtils.HandleException(this.Text, MethodBase.GetCurrentMethod(), exception);
}
}
@@ -425,7 +425,7 @@ namespace Opc.Ua.Sample.Controls
}
catch (Exception exception)
{
- GuiUtils.HandleException(this.Text, MethodBase.GetCurrentMethod(), exception);
+ GuiUtils.HandleException(this.Text, MethodBase.GetCurrentMethod(), exception);
}
}
@@ -440,7 +440,7 @@ namespace Opc.Ua.Sample.Controls
}
catch (Exception exception)
{
- GuiUtils.HandleException(this.Text, MethodBase.GetCurrentMethod(), exception);
+ GuiUtils.HandleException(this.Text, MethodBase.GetCurrentMethod(), exception);
}
}
@@ -457,7 +457,7 @@ namespace Opc.Ua.Sample.Controls
}
catch (Exception exception)
{
- Utils.Trace(exception, "Could not register with the LDS");
+ Utils.Trace(exception, "Could not register with the LDS");
}
}
diff --git a/SampleApplications/Samples/Controls.Net4/Sessions/SessionOpenDlg.cs b/SampleApplications/Samples/Controls.Net4/Sessions/SessionOpenDlg.cs
index 12e333767e..146cd97ea1 100644
--- a/SampleApplications/Samples/Controls.Net4/Sessions/SessionOpenDlg.cs
+++ b/SampleApplications/Samples/Controls.Net4/Sessions/SessionOpenDlg.cs
@@ -27,19 +27,14 @@
* http://opcfoundation.org/License/MIT/1.00/
* ======================================================================*/
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Data;
-using System.Drawing;
-using System.Text;
-using System.Windows.Forms;
-using System.Reflection;
-using System.Threading;
-using System.Security.Cryptography.X509Certificates;
-
using Opc.Ua.Client;
using Opc.Ua.Client.Controls;
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Text;
+using System.Threading;
+using System.Windows.Forms;
namespace Opc.Ua.Sample.Controls
{
@@ -58,6 +53,7 @@ namespace Opc.Ua.Sample.Controls
private const string m_BrowseCertificates = "";
private static long m_Counter = 0;
private IList m_preferredLocales;
+ private bool m_checkDomain = true;
#endregion
#region Public Interface
@@ -163,7 +159,7 @@ namespace Opc.Ua.Sample.Controls
Cursor = Cursors.WaitCursor;
- ThreadPool.QueueUserWorkItem(Open, new object[] { m_session, SessionNameTB.Text, identity, m_preferredLocales });
+ ThreadPool.QueueUserWorkItem(Open, new object[] { m_session, SessionNameTB.Text, identity, m_preferredLocales, m_checkDomain });
CancelBTN.Enabled = false;
OkBTN.Enabled = false;
@@ -194,6 +190,35 @@ namespace Opc.Ua.Sample.Controls
{
Cursor = Cursors.Default;
+ ServiceResultException sre = e as ServiceResultException;
+ if (sre != null)
+ {
+ if (m_checkDomain && sre.StatusCode == StatusCodes.BadCertificateHostNameInvalid)
+ {
+ StringBuilder buffer = new StringBuilder();
+
+ buffer.AppendFormat(sre.Message);
+ buffer.AppendFormat("\r\n\r\nRetry without certificate hostname validation?");
+
+ DialogResult result = MessageBox.Show(
+ buffer.ToString(),
+ "Exception: BadCertificateHostNameInvalid",
+ MessageBoxButtons.YesNo,
+ MessageBoxIcon.Warning);
+
+ if (result == DialogResult.Yes)
+ {
+ m_checkDomain = false;
+ OkBTN.Enabled = true;
+ OkBTN.PerformClick();
+ return;
+ }
+
+ DialogResult = DialogResult.OK;
+ return;
+ }
+ }
+
if (e != null)
{
GuiUtils.HandleException(this.Text, MethodBase.GetCurrentMethod(), (Exception)e);
@@ -228,9 +253,10 @@ namespace Opc.Ua.Sample.Controls
string sessionName = ((object[])state)[1] as string;
IUserIdentity identity = ((object[])state)[2] as IUserIdentity;
IList preferredLocales = ((object[])state)[3] as IList;
+ bool? checkDomain = ((object[])state)[4] as bool?;
// open the session.
- session.Open(sessionName, (uint)session.SessionTimeout, identity, preferredLocales);
+ session.Open(sessionName, (uint)session.SessionTimeout, identity, preferredLocales, checkDomain ?? true);
OpenComplete(null);
}
diff --git a/SampleApplications/Samples/Controls/Sessions/SessionOpenDlg.xaml.cs b/SampleApplications/Samples/Controls/Sessions/SessionOpenDlg.xaml.cs
index 0becec5177..7409bd1601 100644
--- a/SampleApplications/Samples/Controls/Sessions/SessionOpenDlg.xaml.cs
+++ b/SampleApplications/Samples/Controls/Sessions/SessionOpenDlg.xaml.cs
@@ -54,6 +54,7 @@ namespace Opc.Ua.Sample.Controls
private const string m_BrowseCertificates = "";
private static long m_Counter = 0;
private IList m_preferredLocales;
+ private bool m_checkDomain = false;
#endregion
#region Public Interface
@@ -171,7 +172,7 @@ namespace Opc.Ua.Sample.Controls
CancelBTN.IsEnabled = false;
OkBTN.IsEnabled = false;
- object state = new object[] { m_session, SessionNameTB.Text, identity, m_preferredLocales };
+ object state = new object[] { m_session, SessionNameTB.Text, identity, m_preferredLocales, m_checkDomain };
await Task.Run(() => m_session = Open(state));
dialogPopup.IsOpen = false;
}
@@ -204,9 +205,10 @@ namespace Opc.Ua.Sample.Controls
string sessionName = ((object[])state)[1] as string;
IUserIdentity identity = ((object[])state)[2] as IUserIdentity;
IList preferredLocales = ((object[])state)[3] as IList;
+ bool? checkDomain = ((object[])state)[4] as bool?;
// open the session.
- session.Open(sessionName, (uint)session.SessionTimeout, identity, preferredLocales);
+ session.Open(sessionName, (uint)session.SessionTimeout, identity, preferredLocales, checkDomain ?? true);
return session;
}
diff --git a/Stack/Opc.Ua.Core/Security/Certificates/X509SubjectAltNameExtension.cs b/Stack/Opc.Ua.Core/Security/Certificates/X509SubjectAltNameExtension.cs
index 7d11741436..8e7fd386d3 100644
--- a/Stack/Opc.Ua.Core/Security/Certificates/X509SubjectAltNameExtension.cs
+++ b/Stack/Opc.Ua.Core/Security/Certificates/X509SubjectAltNameExtension.cs
@@ -16,6 +16,7 @@
using System;
using System.Collections.Generic;
+using System.Net;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
@@ -91,7 +92,7 @@ namespace Opc.Ua
buffer.Append(m_uris[ii]);
}
- for (int ii = 0; ii < m_domainNames.Count; ii++)
+ for (int ii = 0; ii < m_domainNames.Count; ii++)
{
if (buffer.Length > 0)
{
@@ -208,7 +209,7 @@ namespace Opc.Ua
domainNames.Add(generalName.Name.ToString());
break;
case Org.BouncyCastle.Asn1.X509.GeneralName.IPAddress:
- ipAddresses.Add(generalName.Name.ToString());
+ ipAddresses.Add(IPAddressToString(Org.BouncyCastle.Asn1.DerOctetString.GetInstance(generalName.Name).GetOctets()));
break;
default:
break;
@@ -219,6 +220,27 @@ namespace Opc.Ua
m_ipAddresses = new ReadOnlyList(ipAddresses);
}
+ ///
+ /// Create a normalized IPv4 or IPv6 address from a 4 byte or 16 byte array.
+ ///
+ private string IPAddressToString(byte[] encodedIPAddress)
+ {
+ try
+ {
+ IPAddress address = new IPAddress(encodedIPAddress);
+ return address.ToString();
+ }
+ catch
+ {
+ throw new ServiceResultException(
+ StatusCodes.BadCertificateInvalid,
+ "Certificate contains invalid IP address.");
+ }
+ }
+
+ ///
+ /// Parse certificate for alternate name extension.
+ ///
private void Parse(byte[] data)
{
if (base.Oid.Value == SubjectAltNameOid ||
@@ -252,6 +274,6 @@ namespace Opc.Ua
private ReadOnlyList m_uris;
private ReadOnlyList m_domainNames;
private ReadOnlyList m_ipAddresses;
-#endregion
+ #endregion
}
}
diff --git a/Stack/Opc.Ua.Core/Stack/Configuration/ApplicationConfiguration.cs b/Stack/Opc.Ua.Core/Stack/Configuration/ApplicationConfiguration.cs
index 7506107ac9..51c66e444d 100644
--- a/Stack/Opc.Ua.Core/Stack/Configuration/ApplicationConfiguration.cs
+++ b/Stack/Opc.Ua.Core/Stack/Configuration/ApplicationConfiguration.cs
@@ -23,9 +23,9 @@ namespace Opc.Ua
///
/// Loads the configuration section for an application.
///
- public class ApplicationConfigurationSection
- {
-#region IConfigurationSectionHandler Members
+ public class ApplicationConfigurationSection
+ {
+ #region IConfigurationSectionHandler Members
///
/// Creates the configuration object from the configuration section.
///
@@ -33,21 +33,21 @@ namespace Opc.Ua
/// The configuration context object.
/// The section as XML node.
/// The created section handler object.
- public object Create(object parent, object configContext, System.Xml.XmlNode section)
- {
+ public object Create(object parent, object configContext, System.Xml.XmlNode section)
+ {
if (section == null)
{
throw new ArgumentNullException("section");
}
- XmlNode element = section.FirstChild;
+ XmlNode element = section.FirstChild;
- while (element != null && typeof(XmlElement) != element.GetType())
- {
- element = element.NextSibling;
- }
-
- XmlReader reader = XmlReader.Create(new StringReader(element.OuterXml));
+ while (element != null && typeof(XmlElement) != element.GetType())
+ {
+ element = element.NextSibling;
+ }
+
+ XmlReader reader = XmlReader.Create(new StringReader(element.OuterXml));
try
{
@@ -59,54 +59,54 @@ namespace Opc.Ua
{
reader.Dispose();
}
- }
-#endregion
- }
+ }
+ #endregion
+ }
///
/// Represents the location of a configuration file.
///
[DataContract(Namespace = Namespaces.OpcUaConfig)]
public class ConfigurationLocation
{
-#region Persistent Properties
+ #region Persistent Properties
///
/// Gets or sets the relative or absolute path to the configuration file.
///
/// The file path.
- [DataMember(IsRequired=true, Order=0)]
+ [DataMember(IsRequired = true, Order = 0)]
public string FilePath
{
get { return m_filePath; }
set { m_filePath = value; }
}
-#endregion
+ #endregion
-#region Private Fields
+ #region Private Fields
private string m_filePath;
-#endregion
+ #endregion
}
///
/// Stores the configurable configuration information for a UA application.
///
public partial class ApplicationConfiguration
- {
-#region Public Methods
+ {
+ #region Public Methods
///
/// Gets the file that was used to load the configuration.
///
/// The source file path.
public string SourceFilePath
{
- get { return m_sourceFilePath; }
+ get { return m_sourceFilePath; }
}
-
+
public CertificateValidator CertificateValidator
{
- get { return m_certificateValidator; }
+ get { return m_certificateValidator; }
set { m_certificateValidator = value; }
}
-
+
///
/// Returns the domain names which the server is configured to use.
///
@@ -154,9 +154,13 @@ namespace Opc.Ua
string domainName = url.DnsSafeHost;
- if (String.Compare(domainName, "localhost", StringComparison.OrdinalIgnoreCase) == 0)
+ if (url.HostNameType == UriHostNameType.Dns)
{
- domainName = Utils.GetHostName();
+ domainName = Utils.ReplaceLocalhost(domainName);
+ }
+ else // IPv4/IPv6 address
+ {
+ domainName = Utils.NormalizedIPAddress(domainName);
}
if (!Utils.FindStringIgnoreCase(domainNames, domainName))
@@ -167,7 +171,7 @@ namespace Opc.Ua
return domainNames;
}
-
+
///
/// Creates the message context from the configuration.
///
@@ -198,7 +202,7 @@ namespace Opc.Ua
[Obsolete("Warning: Behavoir changed return a copy instead of a reference. Should call CreateMessageContext() instead.")]
public ServiceMessageContext MessageContext
{
- get
+ get
{
if (m_messageContext == null)
{
@@ -230,7 +234,7 @@ namespace Opc.Ua
public static async Task Load(string sectionName, ApplicationType applicationType, Type systemType)
{
string filePath = GetFilePathFromAppConfig(sectionName);
-
+
FileInfo file = new FileInfo(filePath);
if (!file.Exists)
@@ -244,7 +248,7 @@ namespace Opc.Ua
return await Load(file, applicationType, systemType);
}
-
+
///
/// Loads but does not validate the application configuration from a configuration section.
///
@@ -376,7 +380,7 @@ namespace Opc.Ua
/// The file path.
/// Calls GetType() on the current instance and passes that to the DataContractSerializer.
public void SaveToFile(string filePath)
- {
+ {
Stream ostrm = File.Open(filePath, FileMode.Create, FileAccess.ReadWrite);
XmlWriterSettings settings = new XmlWriterSettings();
@@ -504,7 +508,7 @@ namespace Opc.Ua
if (filePath == null)
{
filePath = m_clientConfiguration.EndpointCacheFilePath;
-
+
if (!Utils.IsPathRooted(filePath))
{
FileInfo sourceFile = new FileInfo(this.SourceFilePath);
@@ -584,16 +588,16 @@ namespace Opc.Ua
public void UpdateExtension(Type type, object value)
{
}
-#endregion
+ #endregion
}
-#region TraceConfiguration Class
+ #region TraceConfiguration Class
///
/// Specifies parameters used for tracing.
///
public partial class TraceConfiguration
{
-#region Public Methods
+ #region Public Methods
///
/// Applies the trace settings to the current process.
///
@@ -611,17 +615,17 @@ namespace Opc.Ua
Utils.SetTraceOutput(Utils.TraceOutput.DebugAndFile);
}
}
-#endregion
+ #endregion
}
-#endregion
+ #endregion
-#region ServerBaseConfiguration Class
+ #region ServerBaseConfiguration Class
///
/// Specifies the configuration for a server application.
///
public partial class ServerBaseConfiguration
{
-#region Public Methods
+ #region Public Methods
///
/// Validates the configuration.
///
@@ -632,17 +636,17 @@ namespace Opc.Ua
m_securityPolicies.Add(new ServerSecurityPolicy());
}
}
-#endregion
+ #endregion
}
-#endregion
+ #endregion
-#region ServerConfiguration Class
+ #region ServerConfiguration Class
///
/// Specifies the configuration for a server application.
///
public partial class ServerConfiguration : ServerBaseConfiguration
{
-#region Public Methods
+ #region Public Methods
///
/// Validates the configuration.
///
@@ -655,17 +659,17 @@ namespace Opc.Ua
m_userTokenPolicies.Add(new UserTokenPolicy());
}
}
-#endregion
+ #endregion
}
-#endregion
+ #endregion
-#region ClientConfiguration Class
+ #region ClientConfiguration Class
///
/// The configuration for a client application.
///
public partial class ClientConfiguration
{
-#region Public Methods
+ #region Public Methods
///
/// Validates the configuration.
///
@@ -676,7 +680,7 @@ namespace Opc.Ua
WellKnownDiscoveryUrls.AddRange(Utils.DiscoveryUrls);
}
}
-#endregion
+ #endregion
}
-#endregion
+ #endregion
}
diff --git a/Stack/Opc.Ua.Core/Types/Utils/Utils.cs b/Stack/Opc.Ua.Core/Types/Utils/Utils.cs
index 60ceb5be09..bac02233a1 100644
--- a/Stack/Opc.Ua.Core/Types/Utils/Utils.cs
+++ b/Stack/Opc.Ua.Core/Types/Utils/Utils.cs
@@ -920,6 +920,41 @@ namespace Opc.Ua
return Dns.GetHostName().Split('.')[0].ToLowerInvariant();
}
+ public static string GetFullQualifiedDomainName()
+ {
+ string domainName = null;
+ try
+ {
+#if !NETSTANDARD1_4 && !NETSTANDARD1_3
+ domainName = Dns.GetHostEntry("localhost").HostName;
+#endif
+ }
+ catch
+ {
+ }
+ if (String.IsNullOrEmpty(domainName))
+ {
+ return Dns.GetHostName();
+ }
+ return domainName;
+ }
+
+ ///
+ /// Normalize ipv4/ipv6 address for comparisons.
+ ///
+ public static string NormalizedIPAddress(string ipAddress)
+ {
+ try
+ {
+ IPAddress normalizedAddress = IPAddress.Parse(ipAddress);
+ return normalizedAddress.ToString();
+ }
+ catch
+ {
+ return ipAddress;
+ }
+ }
+
///
/// Replaces the localhost domain with the current host name.
///