/// <summary> /// Initializes a secure channel with the endpoint identified by the connection. /// </summary> /// <param name="connection">The connection to use.</param> /// <param name="settings">The settings to use when creating the channel.</param> /// <exception cref="ServiceResultException">Thrown if any communication error occurs.</exception> public void Initialize( ITransportWaitingConnection connection, TransportChannelSettings settings) { SaveSettings(connection.EndpointUrl, settings); CreateChannel(connection); }
/// <summary> /// Initializes a secure channel with the endpoint identified by the connection. /// </summary> /// <param name="connection">The connection to use.</param> /// <param name="settings">The settings to use when creating the channel.</param> /// <exception cref="ServiceResultException">Thrown if any communication error occurs.</exception> public void Initialize( ITransportWaitingConnection connection, TransportChannelSettings settings) { SaveSettings(connection.EndpointUrl, settings); var socket = connection.Handle as IMessageSocket; if (socket == null) { throw new ArgumentException("Connection Handle is not of type IMessageSocket."); } m_channel = new UaSCUaBinaryClientChannel( Guid.NewGuid().ToString(), m_bufferManager, m_messageSocketFactory, m_quotas, m_settings.ClientCertificate, m_settings.ClientCertificateChain, m_settings.ServerCertificate, m_settings.Description); m_channel.Socket = socket; m_channel.Socket.ChangeSink(m_channel); }
/// <summary> /// Opens the channel before sending the request. /// </summary> /// <param name="connection">A reverse connection, null otherwise.</param> private void CreateChannel(ITransportWaitingConnection connection = null) { IMessageSocket socket = null; if (connection != null) { socket = connection.Handle as IMessageSocket; if (socket == null) { throw new ArgumentException("Connection Handle is not of type IMessageSocket."); } } // create the channel. m_channel = new UaSCUaBinaryClientChannel( Guid.NewGuid().ToString(), m_bufferManager, m_messageSocketFactory, m_quotas, m_settings.ClientCertificate, m_settings.ClientCertificateChain, m_settings.ServerCertificate, m_settings.Description); // use socket for reverse connections, ignore otherwise if (socket != null) { m_channel.Socket = socket; m_channel.Socket.ChangeSink(m_channel); m_channel.ReverseSocket = true; } }
/// <summary> /// Creates a new transport channel that supports the ITransportWaitingConnection service contract. /// </summary> public static ITransportChannel Create( ApplicationConfiguration configuration, ITransportWaitingConnection connection, EndpointConfiguration endpointConfiguration, ServiceMessageContext messageContext, X509Certificate2 clientCertificate = null) { // create a default description. var endpoint = new EndpointDescription { EndpointUrl = connection.EndpointUrl.ToString(), SecurityMode = MessageSecurityMode.None, SecurityPolicyUri = SecurityPolicies.None }; endpoint.Server.ApplicationUri = endpoint.EndpointUrl; endpoint.Server.ApplicationType = ApplicationType.DiscoveryServer; ITransportChannel channel = CreateUaBinaryChannel( configuration, connection, endpoint, endpointConfiguration, clientCertificate, (X509Certificate2Collection)null, messageContext); return(channel); }
/// <summary> /// Creates a new UA-binary transport channel if requested. Null otherwise. /// </summary> public static ITransportChannel CreateUaBinaryChannel( ApplicationConfiguration configuration, ITransportWaitingConnection connection, EndpointDescription description, EndpointConfiguration endpointConfiguration, X509Certificate2 clientCertificate, 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 if (channel == null) { throw ServiceResultException.Create( StatusCodes.BadProtocolVersionUnsupported, "Unsupported transport profile\r\n"); } // create a UA channel. var settings = new TransportChannelSettings { Description = description, Configuration = endpointConfiguration, ClientCertificate = clientCertificate, ClientCertificateChain = clientCertificateChain }; if (description.ServerCertificate != null && description.ServerCertificate.Length > 0) { settings.ServerCertificate = Utils.ParseCertificateBlob(description.ServerCertificate); } if (configuration != null) { settings.CertificateValidator = configuration.CertificateValidator.GetChannelValidator(); } settings.NamespaceUris = messageContext.NamespaceUris; settings.Factory = messageContext.Factory; channel.Initialize(connection, settings); channel.Open(); return(channel); }
/// <summary> /// Finds the endpoint that best matches the current settings. /// </summary> public static EndpointDescription SelectEndpoint( ApplicationConfiguration application, ITransportWaitingConnection connection, bool useSecurity ) { return(SelectEndpoint(application, connection, useSecurity, DefaultDiscoverTimeout)); }
private Task <Session> CreateSession( ApplicationConfiguration config, ITransportWaitingConnection connection, EndpointDescription selectedEndpoint, IUserIdentity userIdentity) { var endpointConfiguration = EndpointConfiguration.Create(config); var endpoint = new ConfiguredEndpoint(null, selectedEndpoint, endpointConfiguration); return(Session.Create(config, connection, endpoint, false, false, "OPC UA Complex Types Client", 60000, userIdentity, null)); }
/// <summary> /// Creates a binding for to use for discovering servers. /// </summary> public static DiscoveryClient Create( ITransportWaitingConnection connection, EndpointConfiguration configuration) { if (configuration == null) { configuration = EndpointConfiguration.Create(); } ITransportChannel channel = DiscoveryChannel.Create(null, connection, configuration, new ServiceMessageContext()); return new DiscoveryClient(channel); }
/// <summary> /// Creates a binding for to use for discovering servers. /// </summary> public static DiscoveryClient Create( ApplicationConfiguration application, ITransportWaitingConnection connection, EndpointConfiguration configuration) { if (configuration == null) { configuration = EndpointConfiguration.Create(); } ITransportChannel channel = DiscoveryChannel.Create(application, connection, configuration, application.CreateMessageContext()); return(new DiscoveryClient(channel)); }
/// <summary> /// Creates a new UA-binary transport channel if requested. Null otherwise. /// </summary> public static ITransportChannel CreateUaBinaryChannel( ApplicationConfiguration configuration, ITransportWaitingConnection connection, EndpointDescription description, EndpointConfiguration endpointConfiguration, X509Certificate2 clientCertificate, X509Certificate2Collection clientCertificateChain, IServiceMessageContext messageContext) { // initialize the channel which will be created with the server. string uriScheme = new Uri(description.EndpointUrl).Scheme; ITransportChannel channel = TransportBindings.Channels.GetChannel(uriScheme); if (channel == null) { throw ServiceResultException.Create( StatusCodes.BadProtocolVersionUnsupported, "Unsupported transport profile for scheme {0}.", uriScheme); } // create a UA channel. var settings = new TransportChannelSettings { Description = description, Configuration = endpointConfiguration, ClientCertificate = clientCertificate, ClientCertificateChain = clientCertificateChain }; if (description.ServerCertificate != null && description.ServerCertificate.Length > 0) { settings.ServerCertificate = Utils.ParseCertificateBlob(description.ServerCertificate); } if (configuration != null) { settings.CertificateValidator = configuration.CertificateValidator.GetChannelValidator(); } settings.NamespaceUris = messageContext.NamespaceUris; settings.Factory = messageContext.Factory; channel.Initialize(connection, settings); channel.Open(); return(channel); }
/// <summary> /// Finds the endpoint that best matches the current settings. /// </summary> public static EndpointDescription SelectEndpoint( ApplicationConfiguration application, ITransportWaitingConnection connection, bool useSecurity, int discoverTimeout ) { var endpointConfiguration = EndpointConfiguration.Create(); endpointConfiguration.OperationTimeout = discoverTimeout > 0 ? discoverTimeout : DefaultDiscoverTimeout; using (DiscoveryClient client = DiscoveryClient.Create(application, connection, endpointConfiguration)) { var url = new Uri(client.Endpoint.EndpointUrl); var endpoints = client.GetEndpoints(null); return(SelectEndpoint(url, endpoints, useSecurity)); } }
/// <summary> /// Closes any existing secure channel and opens a new one. /// </summary> /// <param name="connection">A reverse connection, null otherwise.</param> /// <exception cref="ServiceResultException">Thrown if any communication error occurs.</exception> /// <remarks> /// Calling this method will cause outstanding requests over the current secure channel to fail. /// </remarks> public void Reconnect(ITransportWaitingConnection connection) { Utils.Trace("TransportChannel RECONNECT: Reconnecting to {0}.", m_url); lock (m_lock) { // the new channel must be created first because WinSock will reuse sockets and this // can result in messages sent over the old socket arriving as messages on the new socket. // if this happens the new channel is shutdown because of a security violation. UaSCUaBinaryClientChannel channel = m_channel; m_channel = null; try { // reconnect. CreateChannel(connection); // begin connect operation. IAsyncResult result = m_channel.BeginConnect(m_url, m_operationTimeout, null, null); m_channel.EndConnect(result); } finally { // close existing channel. if (channel != null) { try { channel.Close(1000); } catch (Exception e) { // do nothing. Utils.Trace(e, "Ignoring exception while closing transport channel during Reconnect."); } finally { channel.Dispose(); } } } } }
/// <summary> /// Creates a new transport channel that supports the ISessionChannel service contract. /// </summary> /// <param name="configuration">The application configuration.</param> /// <param name="connection"></param> /// <param name="description">The description for the endpoint.</param> /// <param name="endpointConfiguration">The configuration to use with the endpoint.</param> /// <param name="clientCertificate">The client certificate.</param> /// <param name="clientCertificateChain">The client certificate chain.</param> /// <param name="messageContext">The message context to use when serializing the messages.</param> /// <returns></returns> public static ITransportChannel Create( ApplicationConfiguration configuration, ITransportWaitingConnection connection, EndpointDescription description, EndpointConfiguration endpointConfiguration, X509Certificate2 clientCertificate, X509Certificate2Collection clientCertificateChain, IServiceMessageContext messageContext) { // create a UA binary channel. ITransportChannel channel = CreateUaBinaryChannel( configuration, connection, description, endpointConfiguration, clientCertificate, clientCertificateChain, messageContext); return(channel); }
/// <inheritdoc/> public override void Reconnect(ITransportWaitingConnection connection) { throw new NotImplementedException("Reconnect for waiting connections is not supported for this channel"); }
/// <inheritdoc/> /// <remarks>Not implemented here.</remarks> void ITransportChannel.Reconnect(ITransportWaitingConnection connection) { throw new NotImplementedException(); }
private async Task <Session> ConsoleSampleClient() { Console.WriteLine("1 - Create an Application Configuration."); ExitCode = ExitCode.ErrorCreateApplication; ApplicationInstance application = new ApplicationInstance { ApplicationName = "UA Core Complex Client", ApplicationType = ApplicationType.Client, ConfigSectionName = "Opc.Ua.ComplexClient" }; // load the application configuration. ApplicationConfiguration config = await application.LoadApplicationConfiguration(false).ConfigureAwait(false); // check the application certificate. bool haveAppCertificate = await application.CheckApplicationInstanceCertificate(false, 0).ConfigureAwait(false); if (!haveAppCertificate) { throw new Exception("Application instance certificate invalid!"); } ReverseConnectManager reverseConnectManager = null; if (ReverseConnectUri != null) { // start the reverse connection manager reverseConnectManager = new ReverseConnectManager(); reverseConnectManager.AddEndpoint(ReverseConnectUri); reverseConnectManager.StartService(config); } if (haveAppCertificate) { config.ApplicationUri = Utils.GetApplicationUriFromCertificate(config.SecurityConfiguration.ApplicationCertificate.Certificate); if (config.SecurityConfiguration.AutoAcceptUntrustedCertificates) { m_autoAccept = true; } config.CertificateValidator.CertificateValidation += new CertificateValidationEventHandler(CertificateValidator_CertificateValidation); } else { Console.WriteLine(" WARN: missing application certificate, using unsecure connection."); } Console.WriteLine("2 - Discover endpoints of {0}.", m_endpointURL); ExitCode = ExitCode.ErrorDiscoverEndpoints; EndpointDescription selectedEndpoint; if (reverseConnectManager == null) { selectedEndpoint = CoreClientUtils.SelectEndpoint(m_endpointURL, haveAppCertificate, 15000); } else { Console.WriteLine(" Waiting for reverse connection."); ITransportWaitingConnection connection = await reverseConnectManager.WaitForConnection( new Uri(m_endpointURL), null, new CancellationTokenSource(60000).Token); if (connection == null) { throw new ServiceResultException(StatusCodes.BadTimeout, "Waiting for a reverse connection timed out."); } selectedEndpoint = CoreClientUtils.SelectEndpoint(config, connection, haveAppCertificate, 15000); } Console.WriteLine(" Selected endpoint uses: {0}", selectedEndpoint.SecurityPolicyUri.Substring(selectedEndpoint.SecurityPolicyUri.LastIndexOf('#') + 1)); Console.WriteLine("3 - Create a session with OPC UA server."); ExitCode = ExitCode.ErrorCreateSession; // create the user identity UserIdentity userIdentity; if (String.IsNullOrEmpty(Username) && String.IsNullOrEmpty(Password)) { userIdentity = new UserIdentity(new AnonymousIdentityToken()); } else { userIdentity = new UserIdentity(Username, Password); } // create worker session if (reverseConnectManager == null) { m_session = await CreateSession(config, selectedEndpoint, userIdentity).ConfigureAwait(false); } else { Console.WriteLine(" Waiting for reverse connection."); ITransportWaitingConnection connection = await reverseConnectManager.WaitForConnection( new Uri(m_endpointURL), null, new CancellationTokenSource(60000).Token); if (connection == null) { throw new ServiceResultException(StatusCodes.BadTimeout, "Waiting for a reverse connection timed out."); } m_session = await CreateSession(config, connection, selectedEndpoint, userIdentity).ConfigureAwait(false); } // register keep alive handler m_session.KeepAlive += Client_KeepAlive; Console.WriteLine("4 - Browse for all custom type variables."); ExitCode = ExitCode.ErrorReadComplexTypes; Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); var allVariableNodes = BrowseAdddressSpace ? BrowseAllVariables() : new List <INode>(); var allCustomTypeVariables = allVariableNodes.Where(n => ((VariableNode)n).DataType.NamespaceIndex != 0).ToList(); stopWatch.Stop(); Console.WriteLine($" -- Browse all nodes took {stopWatch.ElapsedMilliseconds}ms."); Console.WriteLine($" -- Browsed {allVariableNodes.Count} nodes, from which {allCustomTypeVariables.Count} are custom type variables."); stopWatch.Reset(); // for testing clear the nodecache m_session.NodeCache.Clear(); stopWatch.Start(); if (LoadTypeSystem) { Console.WriteLine("5 - Load the server type dictionary."); ExitCode = ExitCode.ErrorLoadTypeDictionary; stopWatch.Reset(); stopWatch.Start(); var complexTypeSystem = new ComplexTypeSystem(m_session); await complexTypeSystem.Load().ConfigureAwait(false); stopWatch.Stop(); Console.WriteLine($"Load type system took {stopWatch.ElapsedMilliseconds}ms."); Console.WriteLine($"Custom types defined for this session:"); foreach (var type in complexTypeSystem.GetDefinedTypes()) { Console.WriteLine($"{type.Namespace}.{type.Name}"); } Console.WriteLine($"Loaded {m_session.DataTypeSystem.Count} dictionaries:"); foreach (var dictionary in m_session.DataTypeSystem) { Console.WriteLine($" + {dictionary.Value.Name}"); foreach (var type in dictionary.Value.DataTypes) { Console.WriteLine($" -- {type.Key}:{type.Value}"); } } } else { Console.WriteLine("4 - Not loading the server type dictionary."); } foreach (VariableNode variableNode in allCustomTypeVariables) { try { var value = m_session.ReadValue(variableNode.NodeId); CastInt32ToEnum(variableNode, value); Console.WriteLine($" -- {variableNode}:{value}"); if (value.Value is ExtensionObject extensionObject) { if (extensionObject.Body is BaseComplexType complexType) { foreach (var item in complexType.GetPropertyEnumerator()) { if (Verbose) { Console.WriteLine($" -- -- {item.Name}:{complexType[item.Name]}"); } if (WriteComplexInt && item.PropertyType == typeof(Int32)) { var data = complexType[item.Name]; if (data != null) { complexType[item.Name] = (Int32)data + 1; } Console.WriteLine($" -- -- Increment: {item.Name}, {complexType[item.Name]}"); WriteValue(m_session, variableNode.NodeId, value); } } } } if (PrintAsJson) { PrintValueAsJson(variableNode.BrowseName.Name, value); } } catch (ServiceResultException sre) { if (sre.StatusCode == StatusCodes.BadUserAccessDenied) { Console.WriteLine($" -- {variableNode}: Access denied!"); } } } Console.WriteLine("6 - Create test sessions which load only single types as needed."); if (LoadTypeSystem) { foreach (VariableNode variableNode in allCustomTypeVariables) { Session testSession = null; try { Console.WriteLine($"Open session for {variableNode}:"); testSession = await CreateSession(config, selectedEndpoint, userIdentity).ConfigureAwait(false); var complexTypeSystem = new ComplexTypeSystem(testSession); NodeId dataType = variableNode.DataType; Type nullType = testSession.Factory.GetSystemType(dataType); var valueBefore = testSession.ReadValue(variableNode.NodeId); Console.WriteLine($" -- {valueBefore}"); Type systemType = await complexTypeSystem.LoadType(dataType).ConfigureAwait(false); var valueAfter = testSession.ReadValue(variableNode.NodeId); Console.WriteLine($" -- {variableNode}: {systemType} {dataType}"); Console.WriteLine($" -- {valueAfter}"); Console.WriteLine($"Custom types defined for {variableNode}:"); foreach (var type in complexTypeSystem.GetDefinedTypes()) { Console.WriteLine($" -- {type.Namespace}.{type.Name}"); } } catch (ServiceResultException sre) { if (sre.StatusCode == StatusCodes.BadUserAccessDenied) { Console.WriteLine($" -- {variableNode}: Access denied!"); } } finally { testSession?.Close(); } } } else { Console.WriteLine("6 - Not testing to load individual types."); } Console.WriteLine("7 - Create a subscription with publishing interval of 1 second."); ExitCode = ExitCode.ErrorCreateSubscription; var subscription = new Subscription(m_session.DefaultSubscription) { PublishingInterval = 1000 }; Console.WriteLine("8 - Add all custom values and the server time to the subscription."); ExitCode = ExitCode.ErrorMonitoredItem; var list = new List <MonitoredItem> { new MonitoredItem(subscription.DefaultItem) { DisplayName = "ServerStatusCurrentTime", StartNodeId = "i=" + Variables.Server_ServerStatus_CurrentTime.ToString() } }; list.ForEach(i => i.Notification += OnNotification); foreach (var customVariable in allCustomTypeVariables) { var newItem = new MonitoredItem(subscription.DefaultItem) { DisplayName = customVariable.DisplayName.Text, StartNodeId = ExpandedNodeId.ToNodeId(customVariable.NodeId, m_session.NamespaceUris) }; newItem.Notification += OnComplexTypeNotification; list.Add(newItem); } subscription.AddItems(list); Console.WriteLine("9 - Add the subscription to the session."); ExitCode = ExitCode.ErrorAddSubscription; m_session.AddSubscription(subscription); subscription.Create(); Console.WriteLine("10 - Running...Press Ctrl-C to exit..."); ExitCode = ExitCode.ErrorRunning; return(m_session); }
/// <summary> /// Initializes a secure channel with the endpoint identified by the URL. /// </summary> /// <param name="connection">The connection to use.</param> /// <param name="settings">The settings to use when creating the channel.</param> /// <exception cref="ServiceResultException">Thrown if any communication error occurs.</exception> public void Initialize( ITransportWaitingConnection connection, TransportChannelSettings settings) { throw new NotSupportedException("WCF channels must be configured when they are constructed."); }
/// <summary> /// Closes any existing secure channel and opens a new one using an existing channel. /// </summary> /// <param name="connection">The reverse transport connection for the Reconnect.</param> /// <exception cref="ServiceResultException">Thrown if any communication error occurs.</exception> /// <remarks> /// Calling this method will cause outstanding requests over the current secure channel to fail. /// </remarks> public abstract void Reconnect(ITransportWaitingConnection connection);
/// <summary> /// Updates an endpoint with information from the server's discovery endpoint. /// </summary> public void UpdateFromServer( Uri endpointUrl, ITransportWaitingConnection connection, MessageSecurityMode securityMode, string securityPolicyUri) { // get the a discovery url. Uri discoveryUrl = GetDiscoveryUrl(endpointUrl); // create the discovery client. DiscoveryClient client; if (connection != null) { client = DiscoveryClient.Create(connection, m_configuration); } else { client = DiscoveryClient.Create(discoveryUrl, m_configuration); } try { // get the endpoints. EndpointDescriptionCollection collection = client.GetEndpoints(null); if (collection == null || collection.Count == 0) { throw ServiceResultException.Create( StatusCodes.BadUnknownResponse, "Server does not have any endpoints defined."); } // find list of matching endpoints. EndpointDescriptionCollection matches = new EndpointDescriptionCollection(); // first pass - match on the requested security parameters. foreach (EndpointDescription description in collection) { // check for match on security policy. if (!String.IsNullOrEmpty(securityPolicyUri)) { if (securityPolicyUri != description.SecurityPolicyUri) { continue; } } // check for match on security mode. if (securityMode != MessageSecurityMode.Invalid) { if (securityMode != description.SecurityMode) { continue; } } // add to list of matches. matches.Add(description); } // no matches (security parameters may have changed). if (matches.Count == 0) { matches = collection; } // check if list has to be narrowed down further. if (matches.Count > 1) { collection = matches; matches = new EndpointDescriptionCollection(); // second pass - match on the url scheme. foreach (EndpointDescription description in collection) { // parse the endpoint url. Uri sessionUrl = Utils.ParseUri(description.EndpointUrl); if (sessionUrl == null) { continue; } // check for matching protocol. if (sessionUrl.Scheme != endpointUrl.Scheme) { continue; } matches.Add(description); } } // no matches (protocol may not be supported). if (matches.Count == 0) { matches = collection; } // choose first in list by default. EndpointDescription match = matches[0]; // check if list has to be narrowed down further. if (matches.Count > 1) { // third pass - match based on security level. foreach (EndpointDescription description in matches) { if (description.SecurityLevel > match.SecurityLevel) { match = description; } } } // check if the endpoint url matches the endpoint used in the request. if (discoveryUrl != null) { Uri matchUrl = Utils.ParseUri(match.EndpointUrl); if (matchUrl == null || String.Compare(discoveryUrl.DnsSafeHost, matchUrl.DnsSafeHost, StringComparison.OrdinalIgnoreCase) != 0) { UriBuilder uri = new UriBuilder(matchUrl); uri.Host = discoveryUrl.DnsSafeHost; uri.Port = discoveryUrl.Port; match.EndpointUrl = uri.ToString(); // need to update the discovery urls. match.Server.DiscoveryUrls.Clear(); match.Server.DiscoveryUrls.Add(discoveryUrl.ToString()); } } // update the endpoint. Update(match); } finally { client.Close(); } }