module import * as crypto from "crypto" ; import { EventEmitter } from "events" ; import { callbackify } from "util" ; import * as async from "async" ; import * as chalk from "chalk" ; import { extractFullyQualifiedDomainName , getFullyQualifiedDomainName } from "node-opcua-hostname" ; import { assert } from "node-opcua-assert" ; import * as utils from "node-opcua-utils" ; import { AddressSpace , callMethodHelper , ContinuationPoint , IUserManager , PseudoVariantBoolean , PseudoVariantByteString , PseudoVariantDateTime , PseudoVariantDuration , PseudoVariantExtensionObject , PseudoVariantExtensionObjectArray , PseudoVariantLocalizedText , PseudoVariantNodeId , PseudoVariantString , RaiseEventData , SessionContext , UAObject , UAVariable , ISessionContext , UAView , EventTypeLike , UAObjectType } from "node-opcua-address-space" ; import { getDefaultCertificateManager , OPCUACertificateManager } from "node-opcua-certificate-manager" ; import { ServerState } from "node-opcua-common" ; import { Certificate , exploreCertificate , makeSHA1Thumbprint , Nonce , toPem } from "node-opcua-crypto" ; import { AttributeIds , LocalizedText , NodeClass } from "node-opcua-data-model" ; import { DataValue } from "node-opcua-data-value" ; import { dump , make_debugLog , make_errorLog , make_warningLog } from "node-opcua-debug" ; import { NodeId } from "node-opcua-nodeid" ; import { ObjectRegistry } from "node-opcua-object-registry" ; import { AsymmetricAlgorithmSecurityHeader , computeSignature , fromURI , getCryptoFactory , Message , MessageSecurityMode , nonceAlreadyBeenUsed , Request , Response , SecurityPolicy , ServerSecureChannelLayer , SignatureData , verifySignature } from "node-opcua-secure-channel" ; import { BrowseNextRequest , BrowseNextResponse , BrowseRequest , BrowseResponse } from "node-opcua-service-browse" ; import { CallRequest , CallResponse } from "node-opcua-service-call" ; import { ApplicationType , UserTokenType } from "node-opcua-service-endpoints" ; import { HistoryReadRequest , HistoryReadResponse , HistoryReadResult , HistoryUpdateResponse } from "node-opcua-service-history" ; import { AddNodesResponse , AddReferencesResponse , DeleteNodesResponse , DeleteReferencesResponse } from "node-opcua-service-node-management" ; import { QueryFirstResponse , QueryNextResponse } from "node-opcua-service-query" ; import { ReadRequest , ReadResponse , ReadValueId , TimestampsToReturn } from "node-opcua-service-read" ; import { RegisterNodesRequest , RegisterNodesResponse , UnregisterNodesRequest , UnregisterNodesResponse } from "node-opcua-service-register-node" ; import { ActivateSessionRequest , ActivateSessionResponse , AnonymousIdentityToken , CloseSessionRequest , CloseSessionResponse , CreateSessionRequest , CreateSessionResponse , UserNameIdentityToken , X509IdentityToken } from "node-opcua-service-session" ; import { CreateMonitoredItemsRequest , CreateMonitoredItemsResponse , CreateSubscriptionRequest , CreateSubscriptionResponse , DeleteMonitoredItemsRequest , DeleteMonitoredItemsResponse , DeleteSubscriptionsRequest , DeleteSubscriptionsResponse , ModifyMonitoredItemsRequest , ModifyMonitoredItemsResponse , ModifySubscriptionRequest , ModifySubscriptionResponse , MonitoredItemModifyResult , PublishRequest , PublishResponse , RepublishRequest , RepublishResponse , SetMonitoringModeRequest , SetMonitoringModeResponse , SetPublishingModeRequest , SetPublishingModeResponse , SetTriggeringRequest , SetTriggeringResponse , TransferSubscriptionsRequest , TransferSubscriptionsResponse } from "node-opcua-service-subscription" ; import { TranslateBrowsePathsToNodeIdsRequest , TranslateBrowsePathsToNodeIdsResponse } from "node-opcua-service-translate-browse-path" ; import { WriteRequest , WriteResponse } from "node-opcua-service-write" ; import { ErrorCallback , StatusCode , StatusCodes } from "node-opcua-status-code" ; import { ApplicationDescriptionOptions , BrowseResult , BuildInfo , CallMethodResultOptions , CancelResponse , EndpointDescription , MonitoredItemModifyRequest , MonitoringMode , UserIdentityToken , UserTokenPolicy , BuildInfoOptions , MonitoredItemCreateResult , IssuedIdentityToken , BrowseResultOptions } from "node-opcua-types" ; import { DataType } from "node-opcua-variant" ; import { VariantArrayType } from "node-opcua-variant" ; import { matchUri } from "node-opcua-utils" ; import { UAString } from "node-opcua-basic-types" ; import { OPCUABaseServer , OPCUABaseServerOptions } from "./base_server" ; import { Factory } from "./factory" ; import { IRegisterServerManager } from "./i_register_server_manager" ; import { MonitoredItem } from "./monitored_item" ; import { RegisterServerManager } from "./register_server_manager" ; import { RegisterServerManagerHidden } from "./register_server_manager_hidden" ; import { RegisterServerManagerMDNSONLY } from "./register_server_manager_mdns_only" ; import { ServerCapabilitiesOptions } from "./server_capabilities" ; import { OPCUAServerEndPoint } from "./server_end_point" ; import { ClosingReason , CreateSessionOption , ServerEngine } from "./server_engine" ; import { ServerSession } from "./server_session" ; import { CreateMonitoredItemHook , DeleteMonitoredItemHook , Subscription } from "./server_subscription" ; import { ISocketData } from "./i_socket_data" ; import { IChannelData } from "./i_channel_data" ; function isSubscriptionIdInvalid ( subscriptionId ) { return subscriptionId < 0 || subscriptionId >= 0xffffffff ; } export type ValidUserFunc = ( this : ServerSession , username : string , password : string ) => boolean ; export type ValidUserAsyncFunc = ( this : ServerSession , username : string , password : string , callback : ( err : Error | null , isAuthorized ? : boolean ) => void ) => void ; export interface UserManagerOptions extends IUserManager { /** synchronous function to check the credentials - can be overruled by isValidUserAsync */ isValidUser ? : ValidUserFunc ; /** asynchronous function to check if the credentials - overrules isValidUser */ isValidUserAsync ? : ValidUserAsyncFunc ; } const thenify = require ( "thenify" ) ; const package_info = require ( "../package.json" ) ; const debugLog = make_debugLog ( __filename ) ; const errorLog = make_errorLog ( __filename ) ; const warningLog = make_warningLog ( __filename ) ; const default_maxAllowedSessionNumber = 10 ; const default_maxConnectionsPerEndpoint = 10 ; function g_sendError ( channel , message , ResponseClass , statusCode ) { const response = new ResponseClass ( { responseHeader : { serviceResult : statusCode } } ) ; return channel . send_response ( "MSG" , response , message ) ; } const default_build_info = { manufacturerName : "NodeOPCUA : MIT Licence ( see http://node-opcua.github.io/)" , productName : "NodeOPCUA-Server" , productUri : null , softwareVersion : package_info . version , buildNumber : "0" , buildDate : new Date ( 2020 , 1 , 1 ) } ; const minSessionTimeout = 100 ; const defaultSessionTimeout = 1000 * 30 ; const maxSessionTimeout = 1000 * 60 * 50 ; type ResponseClassType = typeof BrowseResponse | typeof BrowseNextResponse | typeof CallResponse | typeof CreateMonitoredItemsResponse | typeof CreateSubscriptionResponse | typeof DeleteSubscriptionsResponse | typeof HistoryReadResponse | typeof ModifyMonitoredItemsResponse | typeof ModifySubscriptionResponse | typeof ReadResponse | typeof RegisterNodesResponse | typeof RepublishResponse | typeof SetPublishingModeResponse | typeof SetTriggeringResponse | typeof TransferSubscriptionsResponse | typeof TranslateBrowsePathsToNodeIdsResponse | typeof UnregisterNodesResponse | typeof WriteResponse ; function _adjust_session_timeout ( sessionTimeout ) { let revisedSessionTimeout = sessionTimeout || defaultSessionTimeout ; revisedSessionTimeout = Math . min ( revisedSessionTimeout , maxSessionTimeout ) ; revisedSessionTimeout = Math . max ( revisedSessionTimeout , minSessionTimeout ) ; return revisedSessionTimeout ; } function channel_has_session ( channel , session ) { if ( session . channel === channel ) { assert ( Object . prototype . hasOwnProperty . call ( channel . sessionTokens , session . authenticationToken . toString ( ) ) ) ; return true ; } return false ; } function moveSessionToChannel ( session , channel ) { debugLog ( "moveSessionToChannel sessionId" , session . nodeId , " channelId=" , channel . channelId ) ; if ( session . publishEngine ) { session . publishEngine . cancelPendingPublishRequestBeforeChannelChange ( ) ; } session . _detach_channel ( ) ; session . _attach_channel ( channel ) ; assert ( session . channel ! . channelId === channel . channelId ) ; } async function _attempt_to_close_some_old_unactivated_session ( server ) { const session = server . engine ! . getOldestUnactivatedSession ( ) ; if ( session ) { await server . engine ! . closeSession ( session . authenticationToken , false , "Forcing" ) ; } } function getRequiredEndpointInfo ( endpoint ) { assert ( endpoint instanceof EndpointDescription ) ; const e = new EndpointDescription ( { endpointUrl : endpoint . endpointUrl , securityLevel : endpoint . securityLevel , securityMode : endpoint . securityMode , securityPolicyUri : endpoint . securityPolicyUri , server : { applicationUri : endpoint . server . applicationUri , applicationType : endpoint . server . applicationType , applicationName : endpoint . server . applicationName } , transportProfileUri : endpoint . transportProfileUri , userIdentityTokens : endpoint . userIdentityTokens } ) ; e . server . productUri = null ; e . server . applicationName = null as any ; e . server . gatewayServerUri = null ; e . server . discoveryProfileUri = null ; e . server . discoveryUrls = null ; e . serverCertificate = null as any ; return e ; } function _serverEndpointsForCreateSessionResponse ( server , endpointUrl , serverUri ) { serverUri = null ; return server . _get_endpoints ( endpointUrl ) . filter ( ( e ) => ! ( e as any ) . restricted ) . filter ( ( e ) => matchUri ( e . endpointUrl , endpointUrl ) ) . map ( getRequiredEndpointInfo ) ; } function adjustSecurityPolicy ( channel , userTokenPolicy_securityPolicyUri ) { let securityPolicy = fromURI ( userTokenPolicy_securityPolicyUri ) ; if ( securityPolicy === SecurityPolicy . Invalid ) { securityPolicy = fromURI ( ( channel . clientSecurityHeader ! as AsymmetricAlgorithmSecurityHeader ) . securityPolicyUri ) ; assert ( securityPolicy !== SecurityPolicy . Invalid ) ; } return securityPolicy ; } function findUserTokenByPolicy ( endpoint_description , userTokenType , policyId ) { assert ( endpoint_description instanceof EndpointDescription ) ; const r = endpoint_description . userIdentityTokens ! . filter ( ( userIdentity ) => userIdentity . tokenType === userTokenType && ( ! policyId || userIdentity . policyId === policyId ) ) ; return r . length === 0 ? null : r [ 0 ] ; } function findUserTokenPolicy ( endpoint_description , userTokenType ) { assert ( endpoint_description instanceof EndpointDescription ) ; const r = endpoint_description . userIdentityTokens ! . filter ( ( userIdentity ) => { assert ( userIdentity . tokenType !== undefined ) ; return userIdentity . tokenType === userTokenType ; } ) ; return r . length === 0 ? null : r [ 0 ] ; } function createAnonymousIdentityToken ( endpoint_desc ) { assert ( endpoint_desc instanceof EndpointDescription ) ; const userTokenPolicy = findUserTokenPolicy ( endpoint_desc , UserTokenType . Anonymous ) ; if ( ! userTokenPolicy ) { throw new Error ( "Cannot find ANONYMOUS user token policy in end point description" ) ; } return new AnonymousIdentityToken ( { policyId : userTokenPolicy . policyId } ) ; } function sameIdentityToken ( token1 , token2 ) { if ( token1 instanceof UserNameIdentityToken ) { if ( ! ( token2 instanceof UserNameIdentityToken ) ) { return false ; } if ( token1 . userName !== token2 . userName ) { return false ; } if ( token1 . password . toString ( "hex" ) !== token2 . password . toString ( "hex" ) ) { return false ; } } else if ( token1 instanceof AnonymousIdentityToken ) { if ( ! ( token2 instanceof AnonymousIdentityToken ) ) { return false ; } if ( token1 . policyId !== token2 . policyId ) { return false ; } return true ; } assert ( false , " Not implemented yet" ) ; return false ; } function getTokenType ( userIdentityToken ) { if ( userIdentityToken instanceof AnonymousIdentityToken ) { return UserTokenType . Anonymous ; } else if ( userIdentityToken instanceof UserNameIdentityToken ) { return UserTokenType . UserName ; } else if ( userIdentityToken instanceof IssuedIdentityToken ) { return UserTokenType . IssuedToken ; } else if ( userIdentityToken instanceof X509IdentityToken ) { return UserTokenType . Certificate ; } return UserTokenType . Invalid ; } function thumbprint ( certificate ? ) { return certificate ? certificate . toString ( "base64" ) : "" ; } function monitoredItem_read_and_record_value ( self , context , oldValue , node , itemToMonitor , callback ) { assert ( self instanceof MonitoredItem ) ; assert ( oldValue instanceof DataValue ) ; assert ( itemToMonitor . attributeId === AttributeIds . Value ) ; const dataValue = node . readAttribute ( context , itemToMonitor . attributeId , itemToMonitor . indexRange , itemToMonitor . dataEncoding ) ; callback ( null , dataValue ) ; } function monitoredItem_read_and_record_value_async ( self , context , oldValue , node , itemToMonitor , callback ) { assert ( context instanceof SessionContext ) ; assert ( itemToMonitor . attributeId === AttributeIds . Value ) ; assert ( self instanceof MonitoredItem ) ; assert ( oldValue instanceof DataValue ) ; assert ( itemToMonitor . attributeId === AttributeIds . Value ) ; node . readValueAsync ( context , ( err , dataValue ? ) => { callback ( err , dataValue ) ; } ) ; } function build_scanning_node_function ( context , addressSpace , monitoredItem , itemToMonitor ) { assert ( context instanceof SessionContext ) ; assert ( itemToMonitor instanceof ReadValueId ) ; const node = addressSpace . findNode ( itemToMonitor . nodeId ) as UAVariable ; if ( ! node ) { errorLog ( " INVALID NODE ID  , " , itemToMonitor . nodeId . toString ( ) ) ; dump ( itemToMonitor ) ; return ( oldData , callback ) => { callback ( null , new DataValue ( { statusCode : StatusCodes . BadNodeIdUnknown , value : { dataType : DataType . Null , value : 0 } } ) ) ; } ; } if ( itemToMonitor . attributeId === AttributeIds . Value ) { const monitoredItem_read_and_record_value_func = itemToMonitor . attributeId === AttributeIds . Value && typeof node . readValueAsync === "function" ? monitoredItem_read_and_record_value_async : monitoredItem_read_and_record_value ; return function func ( this , oldDataValue , callback ) { assert ( this instanceof MonitoredItem ) ; assert ( oldDataValue instanceof DataValue ) ; assert ( typeof callback === "function" ) ; monitoredItem_read_and_record_value_func ( this , context , oldDataValue , node , itemToMonitor , callback ) ; } ; } else { return function func ( this , oldDataValue , callback ) { assert ( this instanceof MonitoredItem ) ; assert ( oldDataValue instanceof DataValue ) ; assert ( typeof callback === "function" ) ; const newDataValue = node . readAttribute ( null , itemToMonitor . attributeId ) ; callback ( null , newDataValue ) ; } ; } } function prepareMonitoredItem ( context , addressSpace , monitoredItem ) { const itemToMonitor = monitoredItem . itemToMonitor ; const readNodeFunc = build_scanning_node_function ( context , addressSpace , monitoredItem , itemToMonitor ) ; monitoredItem . samplingFunc = readNodeFunc ; } function isMonitoringModeValid ( monitoringMode ) { assert ( MonitoringMode . Invalid !== undefined ) ; return monitoringMode !== MonitoringMode . Invalid && monitoringMode <= MonitoringMode . Reporting ; } function _installRegisterServerManager ( self ) { assert ( self instanceof OPCUAServer ) ; assert ( ! self . registerServerManager ) ; if ( ! self . registerServerMethod ) { throw new Error ( "Internal Error" ) ; } switch ( self . registerServerMethod ) { case RegisterServerMethod . HIDDEN : self . registerServerManager = new RegisterServerManagerHidden ( { server : self } ) ; break ; case RegisterServerMethod . MDNS : self . registerServerManager = new RegisterServerManagerMDNSONLY ( { server : self } ) ; break ; case RegisterServerMethod . LDS : self . registerServerManager = new RegisterServerManager ( { discoveryServerEndpointUrl : self . discoveryServerEndpointUrl , server : self } ) ; break ; default : throw new Error ( "Invalid switch" ) ; } self . registerServerManager . on ( "serverRegistrationPending" , ( ) => { event debugLog ( "serverRegistrationPending" ) ; self . emit ( "serverRegistrationPending" ) ; } ) ; self . registerServerManager . on ( "serverRegistered" , ( ) => { event debugLog ( "serverRegistered" ) ; self . emit ( "serverRegistered" ) ; } ) ; self . registerServerManager . on ( "serverRegistrationRenewed" , ( ) => { event debugLog ( "serverRegistrationRenewed" ) ; self . emit ( "serverRegistrationRenewed" ) ; } ) ; self . registerServerManager . on ( "serverUnregistered" , ( ) => { debugLog ( "serverUnregistered" ) ; event self . emit ( "serverUnregistered" ) ; } ) ; } function validate_applicationUri ( channel , request ) { const applicationUri = request . clientDescription . applicationUri ! ; const clientCertificate = request . clientCertificate ; if ( channel . securityMode === MessageSecurityMode . None ) { return true ; } if ( ! clientCertificate || clientCertificate . length === 0 ) { return true ; } const e = exploreCertificate ( clientCertificate ) ; const applicationUriFromCert = e . tbsCertificate . extensions ! . subjectAltName . uniformResourceIdentifier [ 0 ] ; if ( applicationUriFromCert !== applicationUri ) { errorLog ( "BadCertificateUriInvalid!" ) ; errorLog ( "applicationUri           = " , applicationUri ) ; errorLog ( "applicationUriFromCert   = " , applicationUriFromCert ) ; } return applicationUriFromCert === applicationUri ; } function validate_security_endpoint ( server , request , channel ) { debugLog ( "validate_security_endpoint = " , request . endpointUrl ) ; let endpoints = server . _get_endpoints ( request . endpointUrl ) ; if ( endpoints . length === 0 ) { const ua_server = server . engine . addressSpace ! . rootFolder . objects . server ; ua_server . raiseEvent ( "AuditUrlMismatchEventType" , { endpointUrl : { dataType : DataType . String , value : request . endpointUrl } } ) ; debugLog ( "Cannot find endpoint  in available endpoints with endpointUri" , request . endpointUrl ) ; if ( OPCUAServer . requestExactEndpointUrl ) { return { errCode : StatusCodes . BadServiceUnsupported } ; } else { endpoints = server . _get_endpoints ( null ) ; } } endpoints = endpoints . filter ( ( e ) => ! ( e as any ) . restricted ) ; const endpoints_matching_security_mode = endpoints . filter ( ( e ) => { return e . securityMode === channel . securityMode ; } ) ; if ( endpoints_matching_security_mode . length === 0 ) { return { errCode : StatusCodes . BadSecurityModeRejected } ; } const endpoints_matching_security_policy = endpoints_matching_security_mode . filter ( ( e ) => { return e . securityPolicyUri === channel . securityHeader ! . securityPolicyUri ; } ) ; if ( endpoints_matching_security_policy . length === 0 ) { return { errCode : StatusCodes . BadSecurityPolicyRejected } ; } if ( endpoints_matching_security_policy . length !== 1 ) { debugLog ( "endpoints_matching_security_policy= " , endpoints_matching_security_policy . length ) ; } return { errCode : StatusCodes . Good , endpoint : endpoints_matching_security_policy [ 0 ] } ; } export enum RegisterServerMethod { HIDDEN = 1 , MDNS = 2 , LDS = 3 } export interface OPCUAServerEndpointOptions { default hostname ? : string ; default port ? : number ; default securityPolicies ? : SecurityPolicy [ ] ; default securityModes ? : MessageSecurityMode [ ] ; default allowAnonymous ? : boolean ; /** alternate hostname  or IP to use */ alternateHostname ? : string | string [ ] ; /**
     *  true, if discovery service on secure channel shall be disabled
     */ disableDiscovery ? : boolean ; } export interface OPCUAServerOptions extends OPCUABaseServerOptions , OPCUAServerEndpointOptions { alternateEndpoints ? : OPCUAServerEndpointOptions [ ] ; /**
     * the server certificate full path filename
     *
     * the certificate should be in PEM format
     */ certificateFile ? : string ; /**
     * the server private key full path filename
     *
     * This file should contains the private key that has been used to generate
     * the server certificate file.
     *
     * the private key should be in PEM format
     *
     */ privateKeyFile ? : string ; /**
     * the default secure token life time in ms.
     */ defaultSecureTokenLifetime ? : number ; default timeout ? : number ; default maxAllowedSessionNumber ? : number ; default maxConnectionsPerEndpoint ? : number ; /**
     * the nodeset.xml file(s) to load
     *
     * node-opcua comes with pre-installed node-set files that can be used
     *
     * example:
     *
     * ``` javascript
     *
     * ```
     */ nodeset_filename ? : string [ ] | string ; /**
     * the server Info
     *
     * this object contains the value that will populate the
     * Root/ObjectS/Server/ServerInfo OPCUA object in the address space.
     */ serverInfo ? : ApplicationDescriptionOptions ; buildInfo ? : { productName ? : string ; productUri ? : string | null ; manufacturerName ? : string ; softwareVersion ? : string ; buildNumber ? : string ; buildDate ? : Date ; } ; /**
     *  an object that implements user authentication methods
     */ userManager ? : UserManagerOptions ; /** resource Path is a string added at the end of the url such as "/UA/Server" */ resourcePath ? : string ; /**
     *
     */ serverCapabilities ? : ServerCapabilitiesOptions ; default isAuditing ? : boolean ; default registerServerMethod ? : RegisterServerMethod ; default discoveryServerEndpointUrl ? : string ; default capabilitiesForMDNS ? : string [ ] ; /**
     * user Certificate Manager
     * this certificate manager holds the X509 certificates used
     * by client that uses X509 certificate token to impersonate a user
     */ userCertificateManager ? : OPCUACertificateManager ; /**
     * Server Certificate Manager
     *
     * this certificate manager will be used by the server to access
     * and store certificates from the connecting clients
     */ serverCertificateManager ? : OPCUACertificateManager ; /**
     *
     */ onCreateMonitoredItem ? : CreateMonitoredItemHook ; onDeleteMonitoredItem ? : DeleteMonitoredItemHook ; } export interface OPCUAServer { /**
     *
     */ engine : ServerEngine ; /**
     *
     */ registerServerMethod : RegisterServerMethod ; /**
     *
     */ discoveryServerEndpointUrl : string ; /**
     *
     */ registerServerManager ? : IRegisterServerManager ; /**
     *
     */ capabilitiesForMDNS : string [ ] ; /**
     *
     */ userCertificateManager : OPCUACertificateManager ; } const g_requestExactEndpointUrl = ! ! process . env . NODEOPCUA_SERVER_REQUEST_EXACT_ENDPOINT_URL ; /**
 *
 */ export class OPCUAServer extends OPCUABaseServer { static defaultShutdownTimeout = 100 ; /**
     * if requestExactEndpointUrl is set to true the server will only accept createSession that have a endpointUrl that strictly matches
     * one of the provided endpoint.
     * This mean that if the server expose a endpoint with url such as opc.tcp://MYHOSTNAME:1234, client will not be able to reach the server
     * with the ip address of the server.
     * requestExactEndpointUrl = true => emulates the Prosys Server behavior
     * requestExactEndpointUrl = false => emulates the Unified Automation behavior.
     */ static requestExactEndpointUrl : boolean = g_requestExactEndpointUrl ; /**
     * total number of bytes written  by the server since startup
     */ public get bytesWritten ( ) : number { return this . endpoints . reduce ( ( accumulated , endpoint ) => { return accumulated + endpoint . bytesWritten ; } , 0 ) ; } /**
     * total number of bytes read  by the server since startup
     */ public get bytesRead ( ) : number { return this . endpoints . reduce ( ( accumulated , endpoint ) => { return accumulated + endpoint . bytesRead ; } , 0 ) ; } /**
     * Number of transactions processed by the server since startup
     */ public get transactionsCount ( ) : number { return this . endpoints . reduce ( ( accumulated , endpoint ) => { return accumulated + endpoint . transactionsCount ; } , 0 ) ; } /**
     * The server build info
     */ public get buildInfo ( ) : BuildInfo { return this . engine . buildInfo ; } /**
     * the number of connected channel on all existing end points
     */ public get currentChannelCount ( ) : number { return this . endpoints . reduce ( ( currentValue , endPoint ) => { return currentValue + endPoint . currentChannelCount ; } , 0 ) ; } /**
     * The number of active subscriptions from all sessions
     */ public get currentSubscriptionCount ( ) : number { return this . engine ? this . engine . currentSubscriptionCount : 0 ; } /**
     * the number of session activation requests that have been rejected
     */ public get rejectedSessionCount ( ) : number { return this . engine ? this . engine . rejectedSessionCount : 0 ; } /**
     * the number of request that have been rejected
     */ public get rejectedRequestsCount ( ) : number { return this . engine ? this . engine . rejectedRequestsCount : 0 ; } /**
     * the number of sessions that have been aborted
     */ public get sessionAbortCount ( ) : number { return this . engine ? this . engine . sessionAbortCount : 0 ; } /**
     * the publishing interval count
     */ public get publishingIntervalCount ( ) : number { return this . engine ? this . engine . publishingIntervalCount : 0 ; } /**
     * the number of sessions currently active
     */ public get currentSessionCount ( ) : number { return this . engine ? this . engine . currentSessionCount : 0 ; } /**
     * true if the server has been initialized
     *
     */ public get initialized ( ) : boolean { return this . engine && this . engine . addressSpace !== null ; } /**
     * is the server auditing ?
     */ public get isAuditing ( ) : boolean { return this . engine ? this . engine . isAuditing : false ; } public static registry = new ObjectRegistry ( ) ; public static fallbackSessionName = "Client didn't provide a meaningful sessionName ..." ; /**
     * the maximum number of subscription that can be created per server
     */ public static MAX_SUBSCRIPTION = 50 ; /**
     * the maximum number of concurrent sessions allowed on the server
     */ public maxAllowedSessionNumber : number ; /**
     * the maximum number for concurrent connection per end point
     */ public maxConnectionsPerEndpoint : number ; /**
     * false if anonymous connection are not allowed
     */ public allowAnonymous = false ; /**
     * the user manager
     */ public userManager : UserManagerOptions ; public readonly options : OPCUAServerOptions ; private objectFactory ? : Factory ; private _delayInit ? : ( ) => Promise < void > ; constructor ( options ? ) { super ( options ) ; this . allowAnonymous = false ; options = options || { } ; this . options = options ; property this . maxAllowedSessionNumber = options . maxAllowedSessionNumber || default_maxAllowedSessionNumber ; property this . maxConnectionsPerEndpoint = options . maxConnectionsPerEndpoint || default_maxConnectionsPerEndpoint ; const buildInfo = { ... default_build_info , ... options . buildInfo } ; buildInfo . productUri = buildInfo . productUri || this . serverInfo . productUri ; this . serverInfo . productUri = this . serverInfo . productUri || buildInfo . productUri ; this . userManager = options . userManager || { } ; if ( typeof this . userManager . isValidUser !== "function" ) { this . userManager . isValidUser = ( ) => { return false ; } ; } options . allowAnonymous = options . allowAnonymous === undefined ? true : ! ! options . allowAnonymous ; property this . allowAnonymous = options . allowAnonymous ; this . discoveryServerEndpointUrl = options . discoveryServerEndpointUrl || "opc.tcp://%FQDN%:4840" ; assert ( typeof this . discoveryServerEndpointUrl === "string" ) ; this . serverInfo . applicationType = ApplicationType . Server ; this . capabilitiesForMDNS = options . capabilitiesForMDNS || [ "NA" ] ; this . registerServerMethod = options . registerServerMethod || RegisterServerMethod . HIDDEN ; _installRegisterServerManager ( this ) ; if ( ! options . userCertificateManager ) { this . userCertificateManager = getDefaultCertificateManager ( "UserPKI" ) ; } else { this . userCertificateManager = options . userCertificateManager ; } this . _delayInit = async ( ) => { if ( ! options ) { throw new Error ( "Internal Error" ) ; } this . engine = new ServerEngine ( { applicationUri : ( ) => this . serverInfo . applicationUri ! , buildInfo , isAuditing : options . isAuditing , serverCapabilities : options . serverCapabilities } ) ; this . objectFactory = new Factory ( this . engine ) ; const endpointDefinitions = options . alternateEndpoints || [ ] ; const hostname = getFullyQualifiedDomainName ( ) ; endpointDefinitions . push ( { port : options . port || 26543 , allowAnonymous : options . allowAnonymous , alternateHostname : options . alternateHostname , disableDiscovery : options . disableDiscovery , hostname : options . hostname || hostname , securityModes : options . securityModes , securityPolicies : options . securityPolicies } ) ; for ( const endpointOptions of endpointDefinitions ) { const endPoint = this . createEndpointDescriptions ( options ! , endpointOptions ) ; this . endpoints . push ( endPoint ) ; endPoint . on ( "message" , ( message , channel ) => { this . on_request ( message , channel ) ; } ) ; endPoint . on ( "error" , ( err ) => { errorLog ( "OPCUAServer endpoint error" , err ) ; this . engine . setServerState ( ServerState . Failed ) ; this . shutdown ( ( ) => { } ) ; } ) ; } } ; } /**
     * Initialize the server by installing default node set.
     *
     * and instruct the server to listen to its endpoints.
     *
     * ```javascript
     * const server = new OPCUAServer();
     * await server.initialize();
     *
     * // default server namespace is now initialized
     * // it is a good time to create life instance objects
     * const namespace = server.engine.addressSpace.getOwnNamespace();
     * namespace.addObject({
     *     browseName: "SomeObject",
     *     organizedBy: server.engine.addressSpace.rootFolder.objects
     * });
     *
     * // the addressSpace is now complete
     * // let's now start listening to clients
     * await server.start();
     * ```
     */ public initialize ( ) ; public initialize ( done ) ; public initialize ( ... args ) { const done = args [ 0 ] as ( err ? : Error ) => void ; assert ( ! this . initialized , "server is already initialized" ) ; this . _preInitTask . push ( async ( ) => { if ( this . _delayInit ) { await this . _delayInit ( ) ; this . _delayInit = undefined ; } } ) ; this . performPreInitialization ( ) . then ( ( ) => { OPCUAServer . registry . register ( this ) ; this . engine . initialize ( this . options , ( ) => { setImmediate ( ( ) => { this . emit ( "post_initialize" ) ; done ( ) ; } ) ; } ) ; } ) . catch ( ( err ) => { done ( err ) ; } ) ; } async public start ( ) ; public start ( done ) ; public start ( ... args ) { const done = args [ 0 ] as ( ) => void ; const tasks = [ ] ; tasks . push ( callbackify ( extractFullyQualifiedDomainName ) ) ; if ( ! this . initialized ) { tasks . push ( ( callback ) => { this . initialize ( callback ) ; } ) ; } tasks . push ( ( callback ) => { super . start ( ( err ? ) => { if ( err ) { this . shutdown ( ( err2 ? ) => { callback ( err ) ; } ) ; } else { this . registerServerManager ! . start ( ( ) => { } ) ; setImmediate ( callback ) ; } } ) ; } ) ; async . series ( tasks , done ) ; } method async param timeout example public shutdown ( timeout ? ) ; public shutdown ( callback ) ; public shutdown ( timeout , callback ) ; public shutdown ( ... args ) { const timeout = args . length === 1 ? OPCUAServer . defaultShutdownTimeout : ( args [ 0 ] as number ) ; const callback = ( args . length === 1 ? args [ 0 ] : args [ 1 ] ) as ( err ? : Error ) => void ; assert ( typeof callback === "function" ) ; debugLog ( "OPCUAServer#shutdown (timeout = " , timeout , ")" ) ; if ( ! this . engine ) { return callback ( ) ; } assert ( this . engine ) ; if ( ! this . engine . isStarted ( ) ) { const err = new Error ( "OPCUAServer#shutdown failure ! server doesn't seems to be started yet" ) ; return callback ( err ) ; } this . userCertificateManager . dispose ( ) ; this . engine . setServerState ( ServerState . Shutdown ) ; const shutdownTime = new Date ( Date . now ( ) + timeout ) ; this . engine . setShutdownTime ( shutdownTime ) ; debugLog ( "OPCUAServer is now unregistering itself from  the discovery server " + this . buildInfo ) ; this . registerServerManager ! . stop ( ( err ? ) => { debugLog ( "OPCUAServer unregistered from discovery server" , err ) ; setTimeout ( ( ) => { this . engine . shutdown ( ) ; debugLog ( "OPCUAServer#shutdown: started" ) ; OPCUABaseServer . prototype . shutdown . call ( this , ( err1 ? ) => { debugLog ( "OPCUAServer#shutdown: completed" ) ; this . dispose ( ) ; callback ( err1 ) ; } ) ; } , timeout ) ; } ) ; } public dispose ( ) { for ( const endpoint of this . endpoints ) { endpoint . dispose ( ) ; } this . endpoints = [ ] ; this . removeAllListeners ( ) ; if ( this . registerServerManager ) { this . registerServerManager . dispose ( ) ; this . registerServerManager = undefined ; } OPCUAServer . registry . unregister ( this ) ; if ( this . engine ) { this . engine . dispose ( ) ; } } public raiseEvent ( eventType , options ) { if ( ! this . engine . addressSpace ) { errorLog ( "addressSpace missing" ) ; return ; } const server = this . engine . addressSpace . findNode ( "Server" ) as UAObject ; if ( ! server ) { return ; } let eventTypeNode = eventType ; if ( typeof eventType === "string" ) { eventTypeNode = this . engine . addressSpace . findEventType ( eventType ) ; if ( eventTypeNode ) { return server . raiseEvent ( eventTypeNode , options ) ; } else { console . warn ( " cannot find event type " , eventType ) ; } } else { return server . raiseEvent ( eventTypeNode , options ) ; } } internal protected createSession ( options ) { if ( ! this . engine ) { throw new Error ( "Internal Error" ) ; } return this . engine . createSession ( options ) ; } internal protected getSession ( authenticationToken , activeOnly ? ) { return this . engine ? this . engine . getSession ( authenticationToken , activeOnly ) : null ; } param channel param clientCertificate param clientNonce internal protected computeServerSignature ( channel , clientCertificate , clientNonce ) { return computeSignature ( clientCertificate , clientNonce , this . getPrivateKey ( ) , channel . messageBuilder . securityPolicy ) ; } param session param channel param clientSignature internal protected verifyClientSignature ( session , channel , clientSignature ) { const clientCertificate = channel . receiverCertificate ! ; const securityPolicy = channel . messageBuilder . securityPolicy ; const serverCertificate = this . getCertificate ( ) ; const result = verifySignature ( serverCertificate , session . nonce ! , clientSignature , clientCertificate , securityPolicy ) ; return result ; } protected isValidUserNameIdentityToken ( channel , session , userTokenPolicy , userIdentityToken , userTokenSignature , callback ) { assert ( userIdentityToken instanceof UserNameIdentityToken ) ; const securityPolicy = adjustSecurityPolicy ( channel , userTokenPolicy . securityPolicyUri ) ; if ( securityPolicy === SecurityPolicy . None ) { return callback ( null , StatusCodes . Good ) ; } const cryptoFactory = getCryptoFactory ( securityPolicy ) ; if ( ! cryptoFactory ) { return callback ( null , StatusCodes . BadSecurityPolicyRejected ) ; } if ( userIdentityToken . encryptionAlgorithm !== cryptoFactory . asymmetricEncryptionAlgorithm ) { errorLog ( "invalid encryptionAlgorithm" ) ; errorLog ( "userTokenPolicy" , userTokenPolicy . toString ( ) ) ; errorLog ( "userTokenPolicy" , userIdentityToken . toString ( ) ) ; return callback ( null , StatusCodes . BadIdentityTokenInvalid ) ; } const userName = userIdentityToken . userName ; const password = userIdentityToken . password ; if ( ! userName || ! password ) { return callback ( null , StatusCodes . BadIdentityTokenInvalid ) ; } return callback ( null , StatusCodes . Good ) ; } protected isValidX509IdentityToken ( channel , session , userTokenPolicy , userIdentityToken , userTokenSignature , callback ) { assert ( userIdentityToken instanceof X509IdentityToken ) ; assert ( callback instanceof Function ) ; const securityPolicy = adjustSecurityPolicy ( channel , userTokenPolicy . securityPolicyUri ) ; const cryptoFactory = getCryptoFactory ( securityPolicy ) ; if ( ! cryptoFactory ) { return callback ( null , StatusCodes . BadSecurityPolicyRejected ) ; } if ( ! userTokenSignature || ! userTokenSignature . signature ) { return callback ( null , StatusCodes . BadUserSignatureInvalid ) ; } if ( userIdentityToken . policyId !== userTokenPolicy . policyId ) { errorLog ( "invalid encryptionAlgorithm" ) ; errorLog ( "userTokenPolicy" , userTokenPolicy . toString ( ) ) ; errorLog ( "userTokenPolicy" , userIdentityToken . toString ( ) ) ; return callback ( null , StatusCodes . BadSecurityPolicyRejected ) ; } const certificate = userIdentityToken . certificateData ; const nonce = session . nonce ! ; const serverCertificate = this . getCertificate ( ) ; assert ( serverCertificate instanceof Buffer ) ; assert ( certificate instanceof Buffer , "expecting certificate to be a Buffer" ) ; assert ( nonce instanceof Buffer , "expecting nonce to be a Buffer" ) ; assert ( userTokenSignature . signature instanceof Buffer , "expecting userTokenSignature to be a Buffer" ) ; if ( ! verifySignature ( serverCertificate , nonce , userTokenSignature , certificate , securityPolicy ) ) { return callback ( null , StatusCodes . BadUserSignatureInvalid ) ; } this . userCertificateManager ! . checkCertificate ( certificate , ( err , certificateStatus ) => { if ( err ) { return callback ( err ) ; } if ( StatusCodes . BadCertificateUntrusted === certificateStatus || StatusCodes . BadCertificateTimeInvalid === certificateStatus || StatusCodes . BadCertificateIssuerTimeInvalid === certificateStatus || StatusCodes . BadCertificateIssuerUseNotAllowed === certificateStatus || StatusCodes . BadCertificateIssuerRevocationUnknown === certificateStatus || StatusCodes . BadCertificateRevocationUnknown === certificateStatus || StatusCodes . BadCertificateRevoked === certificateStatus || StatusCodes . BadCertificateUseNotAllowed === certificateStatus || StatusCodes . BadSecurityChecksFailed === certificateStatus || StatusCodes . Good !== certificateStatus ) { debugLog ( "isValidX509IdentityToken => certificateStatus = " , certificateStatus ?. toString ( ) ) ; return callback ( null , StatusCodes . BadIdentityTokenRejected ) ; } if ( StatusCodes . Good !== certificateStatus ) { assert ( certificateStatus instanceof StatusCode ) ; return callback ( null , certificateStatus ) ; } return callback ( null , StatusCodes . Good ) ; } ) ; } internal protected userNameIdentityTokenAuthenticateUser ( channel , session , userTokenPolicy , userIdentityToken , callback ) { assert ( userIdentityToken instanceof UserNameIdentityToken ) ; const securityPolicy = adjustSecurityPolicy ( channel , userTokenPolicy . securityPolicyUri ) ; const userName = userIdentityToken . userName ! ; let password = userIdentityToken . password ; if ( securityPolicy === SecurityPolicy . None ) { password = password . toString ( ) ; } else { const serverPrivateKey = this . getPrivateKey ( ) ; const serverNonce = session . nonce ! ; assert ( serverNonce instanceof Buffer ) ; const cryptoFactory = getCryptoFactory ( securityPolicy ) ; if ( ! cryptoFactory ) { return callback ( new Error ( " Unsupported security Policy" ) ) ; } const buff = cryptoFactory . asymmetricDecrypt ( password , serverPrivateKey ) ; if ( ! buff || buff . length < 4 ) { async . setImmediate ( ( ) => callback ( null , false ) ) ; return ; } const length = buff . readUInt32LE ( 0 ) - serverNonce . length ; password = buff . slice ( 4 , 4 + length ) . toString ( "utf-8" ) ; } if ( typeof this . userManager . isValidUserAsync === "function" ) { this . userManager . isValidUserAsync . call ( session , userName , password , callback ) ; } else { const authorized = this . userManager . isValidUser ! . call ( session , userName , password ) ; async . setImmediate ( ( ) => callback ( null , authorized ) ) ; } } internal protected isValidUserIdentityToken ( channel , session , userIdentityToken , userTokenSignature , endpointDescription , callback ) { assert ( callback instanceof Function ) ; if ( ! userIdentityToken ) { throw new Error ( "Invalid token" ) ; } const userTokenType = getTokenType ( userIdentityToken ) ; const userTokenPolicy = findUserTokenByPolicy ( endpointDescription , userTokenType , userIdentityToken . policyId ! ) ; if ( ! userTokenPolicy ) { return callback ( null , StatusCodes . BadIdentityTokenInvalid ) ; } if ( userIdentityToken instanceof UserNameIdentityToken ) { return this . isValidUserNameIdentityToken ( channel , session , userTokenPolicy , userIdentityToken , userTokenSignature , callback ) ; } if ( userIdentityToken instanceof X509IdentityToken ) { return this . isValidX509IdentityToken ( channel , session , userTokenPolicy , userIdentityToken , userTokenSignature , callback ) ; } return callback ( null , StatusCodes . Good ) ; } internal param channel param session param userIdentityToken param callback returns { * } protected isUserAuthorized ( channel , session , userIdentityToken , callback ) { assert ( userIdentityToken ) ; assert ( typeof callback === "function" ) ; const userTokenType = getTokenType ( userIdentityToken ) ; const userTokenPolicy = findUserTokenByPolicy ( session . getEndpointDescription ( ) , userTokenType , userIdentityToken . policyId ! ) ; /** istanbul ignore next */ if ( ! userTokenPolicy ) { return callback ( null , false ) ; } if ( userIdentityToken instanceof UserNameIdentityToken ) { return this . userNameIdentityTokenAuthenticateUser ( channel , session , userTokenPolicy , userIdentityToken , callback ) ; } async . setImmediate ( callback . bind ( null , null , true ) ) ; } protected makeServerNonce ( ) { return crypto . randomBytes ( 32 ) ; } protected async _on_CreateSessionRequest ( message , channel ) { const request = message . request as CreateSessionRequest ; assert ( request instanceof CreateSessionRequest ) ; function rejectConnection ( server , statusCode ) { server . engine . incrementSecurityRejectedSessionCount ( ) ; const response1 = new CreateSessionResponse ( { responseHeader : { serviceResult : statusCode } } ) ; channel . send_response ( "MSG" , response1 , message ) ; } if ( this . currentSessionCount >= this . maxAllowedSessionNumber ) { await _attempt_to_close_some_old_unactivated_session ( this ) ; } if ( this . currentSessionCount >= this . maxAllowedSessionNumber ) { return rejectConnection ( this , StatusCodes . BadTooManySessions ) ; } if ( utils . isNullOrUndefined ( request . sessionName ) ) { debugLog ( "assigning OPCUAServer.fallbackSessionName because client's sessionName is null " , OPCUAServer . fallbackSessionName ) ; request . sessionName = OPCUAServer . fallbackSessionName ; } const revisedSessionTimeout = _adjust_session_timeout ( request . requestedSessionTimeout ) ; if ( ! request . clientNonce || request . clientNonce . length < 32 ) { if ( channel . securityMode !== MessageSecurityMode . None ) { errorLog ( chalk . red ( "SERVER with secure connection: Missing or invalid client Nonce " ) , request . clientNonce && request . clientNonce . toString ( "hex" ) ) ; return rejectConnection ( this , StatusCodes . BadNonceInvalid ) ; } } if ( nonceAlreadyBeenUsed ( request . clientNonce ) ) { errorLog ( chalk . red ( "SERVER with secure connection: None has already been used" ) , request . clientNonce && request . clientNonce . toString ( "hex" ) ) ; return rejectConnection ( this , StatusCodes . BadNonceInvalid ) ; } if ( ! validate_applicationUri ( channel , request ) ) { return rejectConnection ( this , StatusCodes . BadCertificateUriInvalid ) ; } const { errCode , endpoint } = validate_security_endpoint ( this , request , channel ) ; if ( errCode !== StatusCodes . Good ) { return rejectConnection ( this , errCode ) ; } const session = this . createSession ( { clientDescription : request . clientDescription , sessionTimeout : revisedSessionTimeout } ) ; session . endpoint = endpoint ; assert ( session ) ; assert ( session . sessionTimeout === revisedSessionTimeout ) ; session . clientDescription = request . clientDescription ; session . sessionName = request . sessionName || "<unknown session name>" ; session . nonce = this . makeServerNonce ( ) ; session . channelId = channel . channelId ; session . _attach_channel ( channel ) ; const serverCertificateChain = this . getCertificateChain ( ) ; const hasEncryption = true ; if ( session . channel ! . securityMode === MessageSecurityMode . None ) { } const response = new CreateSessionResponse ( { sessionId : session . nodeId , authenticationToken : session . authenticationToken , revisedSessionTimeout , serverNonce : session . nonce , serverCertificate : hasEncryption ? serverCertificateChain : undefined , serverEndpoints : _serverEndpointsForCreateSessionResponse ( this , session . endpoint ! . endpointUrl , request . serverUri ) , serverSoftwareCertificates : null , serverSignature : this . computeServerSignature ( channel , request . clientCertificate , request . clientNonce ) , maxRequestMessageSize : 0x4000000 } ) ; this . emit ( "create_session" , session ) ; session . on ( "session_closed" , ( session1 , deleteSubscriptions , reason ) => { assert ( typeof reason === "string" ) ; if ( this . isAuditing ) { assert ( reason === "Timeout" || reason === "Terminated" || reason === "CloseSession" || reason === "Forcing" ) ; const sourceName = "Session/" + reason ; this . raiseEvent ( "AuditSessionEventType" , { actionTimeStamp : { dataType : "DateTime" , value : new Date ( ) } , status : { dataType : "Boolean" , value : true } , serverId : { dataType : "String" , value : "" } , clientAuditEntryId : { dataType : "String" , value : "" } , clientUserId : { dataType : "String" , value : "" } , sourceName : { dataType : "String" , value : sourceName } , sessionId : { dataType : "NodeId" , value : session1 . nodeId } } ) ; } this . emit ( "session_closed" , session1 , deleteSubscriptions ) ; } ) ; if ( this . isAuditing ) { this . raiseEvent ( "AuditCreateSessionEventType" , { actionTimeStamp : { dataType : "DateTime" , value : new Date ( ) } , status : { dataType : "Boolean" , value : true } , serverId : { dataType : "String" , value : "" } , clientAuditEntryId : { dataType : "String" , value : "" } , clientUserId : { dataType : "String" , value : "" } , sourceName : { dataType : "String" , value : "Session/CreateSession" } , sessionId : { dataType : "NodeId" , value : session . nodeId } , secureChannelId : { dataType : "String" , value : session . channel ! . channelId ! . toString ( ) } , revisedSessionTimeout : { dataType : "Duration" , value : session . sessionTimeout } , clientCertificate : { dataType : "ByteString" , value : session . channel ! . clientCertificate } , clientCertificateThumbprint : { dataType : "String" , value : thumbprint ( session . channel ! . clientCertificate ! ) } } ) ; } assert ( response . authenticationToken ) ; channel . send_response ( "MSG" , response , message ) ; } method private protected _on_ActivateSessionRequest ( message , channel ) { const request = message . request as ActivateSessionRequest ; assert ( request instanceof ActivateSessionRequest ) ; const authenticationToken = request . requestHeader . authenticationToken ; const session = this . getSession ( authenticationToken ) ; function rejectConnection ( server , statusCode ) { if ( statusCode === StatusCodes . BadSessionIdInvalid ) { server . engine . incrementRejectedSessionCount ( ) ; } else { server . engine . incrementRejectedSessionCount ( ) ; server . engine . incrementSecurityRejectedSessionCount ( ) ; } const response1 = new ActivateSessionResponse ( { responseHeader : { serviceResult : statusCode } } ) ; channel . send_response ( "MSG" , response1 , message ) ; } let response ; if ( ! session ) { debugLog ( chalk . yellow . bold ( " Bad Session in  _on_ActivateSessionRequest" ) , authenticationToken . toString ( ) ) ; return rejectConnection ( this , StatusCodes . BadSessionIdInvalid ) ; } session . keepAlive ? session . keepAlive ( ) : void 0 ; if ( session . status === "new" ) { if ( ! channel_has_session ( channel , session ) ) { errorLog ( " channel.sessionTokens === " + Object . keys ( channel . sessionTokens ) . join ( " " ) ) ; return rejectConnection ( this , StatusCodes . BadSessionNotActivated ) ; } } if ( session . status === "active" ) { if ( session . channel ! . channelId !== channel . channelId ) { warningLog ( " Session " , session . sessionName , " is being transferred from channel" , chalk . cyan ( session . channel ! . channelId ! . toString ( ) ) , " to channel " , chalk . cyan ( channel . channelId ! . toString ( ) ) ) ; const old_channel_cert_thumbprint = thumbprint ( session . channel ! . clientCertificate ! ) ; const new_channel_cert_thumbprint = thumbprint ( channel . clientCertificate ! ) ; if ( old_channel_cert_thumbprint !== new_channel_cert_thumbprint ) { return rejectConnection ( this , StatusCodes . BadNoValidCertificates ) ; } if ( ! sameIdentityToken ( session . userIdentityToken ! , request . userIdentityToken as UserIdentityToken ) ) { return rejectConnection ( this , StatusCodes . BadIdentityChangeNotSupported ) ; } } moveSessionToChannel ( session , channel ) ; } else if ( session . status === "screwed" ) { return rejectConnection ( this , StatusCodes . BadSessionClosed ) ; } else if ( session . status === "closed" ) { warningLog ( chalk . yellow . bold ( " Bad Session Closed in  _on_ActivateSessionRequest" ) , authenticationToken . toString ( ) ) ; return rejectConnection ( this , StatusCodes . BadSessionClosed ) ; } if ( ! this . verifyClientSignature ( session , channel , request . clientSignature ) ) { return rejectConnection ( this , StatusCodes . BadApplicationSignatureInvalid ) ; } request . userIdentityToken = request . userIdentityToken || createAnonymousIdentityToken ( session . endpoint ! ) ; this . isValidUserIdentityToken ( channel , session , request . userIdentityToken as UserIdentityToken , request . userTokenSignature , session . endpoint ! , ( err , statusCode ? ) => { if ( statusCode !== StatusCodes . Good ) { if ( ! ( statusCode && statusCode instanceof StatusCode ) ) { const a = 23 ; } assert ( statusCode && statusCode instanceof StatusCode , "expecting statusCode" ) ; return rejectConnection ( this , statusCode ! ) ; } session . userIdentityToken = request . userIdentityToken as UserIdentityToken ; this . isUserAuthorized ( channel , session , request . userIdentityToken as UserIdentityToken , ( err1 , authorized ? ) => { if ( err1 ) { return rejectConnection ( this , StatusCodes . BadInternalError ) ; } if ( ! authorized ) { return rejectConnection ( this , StatusCodes . BadUserAccessDenied ) ; } else { session . nonce = this . makeServerNonce ( ) ; session . status = "active" ; response = new ActivateSessionResponse ( { serverNonce : session . nonce } ) ; channel . send_response ( "MSG" , response , message ) ; const userIdentityTokenPasswordRemoved = ( userIdentityToken ) => { const a = userIdentityToken . clone ( ) ; a . password = "*************" ; return a ; } ; assert ( session . nodeId ) ; assert ( session . sessionTimeout > 0 ) ; if ( this . isAuditing ) { this . raiseEvent ( "AuditActivateSessionEventType" , { actionTimeStamp : { dataType : "DateTime" , value : new Date ( ) } , status : { dataType : "Boolean" , value : true } , serverId : { dataType : "String" , value : "" } , clientAuditEntryId : { dataType : "String" , value : "" } , clientUserId : { dataType : "String" , value : "cc" } , sourceName : { dataType : "String" , value : "Session/ActivateSession" } , sessionId : { dataType : "NodeId" , value : session . nodeId } , clientSoftwareCertificates : { arrayType : VariantArrayType . Array , dataType : "ExtensionObject" , value : [ ] } , userIdentityToken : { dataType : "ExtensionObject" , value : userIdentityTokenPasswordRemoved ( session . userIdentityToken ) } , secureChannelId : { dataType : "String" , value : session . channel ! . channelId ! . toString ( ) } } ) ; } this . emit ( "session_activated" , session , userIdentityTokenPasswordRemoved ) ; } } ) ; } ) ; } protected prepare ( message , channel ) { const request = message . request ; const authenticationToken = request . requestHeader . authenticationToken ; const session = this . getSession ( authenticationToken , true ) ; if ( ! session ) { message . session_statusCode = StatusCodes . BadSessionIdInvalid ; return ; } message . session = session ; if ( channel . channelId !== session . channelId ) { if ( ! ( request instanceof ActivateSessionRequest ) ) { errorLog ( chalk . red . bgWhite ( "ERROR: channel.channelId !== session.channelId" ) , channel . channelId , session . channelId ) ; } message . session_statusCode = StatusCodes . BadSecureChannelIdInvalid ; } else if ( channel_has_session ( channel , session ) ) { message . session_statusCode = StatusCodes . Good ; } else { message . session_statusCode = StatusCodes . BadSecureChannelIdInvalid ; } } method param ResponseClass param message param channel param actionToPerform param actionToPerform . session { ServerSession } param actionToPerform . sendResponse param actionToPerform . sendResponse . response param actionToPerform . sendError param actionToPerform . sendError . statusCode param actionToPerform . sendError . diagnostics private protected async _apply_on_SessionObject ( ResponseClass , message , channel , actionToPerform ) { assert ( typeof actionToPerform === "function" ) ; function sendResponse ( response1 ) { try { assert ( response1 instanceof ResponseClass ) ; if ( message . session ) { const counterName = ResponseClass . name . replace ( "Response" , "" ) ; message . session . incrementRequestTotalCounter ( counterName ) ; } return channel . send_response ( "MSG" , response1 , message ) ; } catch ( err ) { if ( err instanceof Error ) { errorLog ( "Internal error in issuing response\nplease contact support@sterfive.com" , message . request . toString ( ) , "\n" , response1 . toString ( ) ) ; } throw err ; } } function sendError ( statusCode ) { if ( message . session ) { message . session . incrementRequestErrorCounter ( ResponseClass . name . replace ( "Response" , "" ) ) ; } return g_sendError ( channel , message , ResponseClass , statusCode ) ; } let response ; if ( ! message . session || message . session_statusCode !== StatusCodes . Good ) { const errMessage = "INVALID SESSION  !! " ; response = new ResponseClass ( { responseHeader : { serviceResult : message . session_statusCode } } ) ; debugLog ( chalk . red . bold ( errMessage ) , chalk . yellow ( message . session_statusCode ! . toString ( ) ) , response . constructor . name ) ; return sendResponse ( response ) ; } assert ( message . session_statusCode === StatusCodes . Good ) ; if ( message . session . status === "closed" ) { return sendError ( StatusCodes . BadSessionIdInvalid ) ; } if ( message . session . status === "new" ) { message . session . status = "screwed" ; return sendError ( StatusCodes . BadSessionNotActivated ) ; } if ( message . session . status !== "active" ) { message . session . status = "screwed" ; return sendError ( StatusCodes . BadSessionIdInvalid ) ; } if ( message . session . keepAlive ) { assert ( typeof message . session . keepAlive === "function" ) ; message . session . keepAlive ( ) ; } message . session . incrementTotalRequestCount ( ) ; await actionToPerform ( message . session as ServerSession , sendResponse , sendError ) ; } protected async _apply_on_Subscription ( ResponseClass , message , channel , actionToPerform ) { assert ( typeof actionToPerform === "function" ) ; const request = message . request as unknown as { subscriptionId : number ; } ; assert ( Object . prototype . hasOwnProperty . call ( request , "subscriptionId" ) ) ; this . _apply_on_SessionObject ( ResponseClass , message , channel , async ( session , sendResponse , sendError ) => { const subscription = session . getSubscription ( request . subscriptionId ) ; if ( ! subscription ) { return sendError ( StatusCodes . BadSubscriptionIdInvalid ) ; } subscription . resetLifeTimeAndKeepAliveCounters ( ) ; await actionToPerform ( session , subscription , sendResponse , sendError ) ; } ) ; } protected _apply_on_SubscriptionIds < T > ( ResponseClass , message , channel , actionToPerform ) { assert ( typeof actionToPerform === "function" ) ; const request = message . request as unknown as { subscriptionIds : number [ ] ; } ; assert ( Object . prototype . hasOwnProperty . call ( request , "subscriptionIds" ) ) ; this . _apply_on_SessionObject ( ResponseClass , message , channel , async ( session , sendResponse , sendError ) => { const subscriptionIds = request . subscriptionIds ; if ( ! request . subscriptionIds || request . subscriptionIds . length === 0 ) { return sendError ( StatusCodes . BadNothingToDo ) ; } const results = subscriptionIds . map ( ( subscriptionId ) => actionToPerform ( session , subscriptionId ) ) ; for ( let i = 0 ; i < results . length ; i ++ ) { if ( results [ i ] . then ) { results [ i ] = await results [ i ] ; } } const response = new ResponseClass ( { responseHeader : { serviceResult : request . subscriptionIds . length > OPCUAServer . MAX_SUBSCRIPTION ? StatusCodes . BadTooManyOperations : StatusCodes . Good } , results } ) ; sendResponse ( response ) ; } ) ; } protected _apply_on_Subscriptions ( ResponseClass , message , channel , actionToPerform ) { this . _apply_on_SubscriptionIds < StatusCode > ( ResponseClass , message , channel , async ( session , subscriptionId ) => { if ( isSubscriptionIdInvalid ( subscriptionId ) ) { return StatusCodes . BadSubscriptionIdInvalid ; } const subscription = session . getSubscription ( subscriptionId ) ; if ( ! subscription ) { return StatusCodes . BadSubscriptionIdInvalid ; } return actionToPerform ( session , subscription ) ; } ) ; } private async _closeSession ( authenticationToken , deleteSubscriptions , reason ) { if ( deleteSubscriptions && this . options . onDeleteMonitoredItem ) { const session = this . getSession ( authenticationToken ) ; if ( session ) { const subscriptions = session . publishEngine . subscriptions ; for ( const subscription of subscriptions ) { await subscription . applyOnMonitoredItem ( this . options . onDeleteMonitoredItem . bind ( null , subscription ) as any ) ; } } } await this . engine . closeSession ( authenticationToken , deleteSubscriptions , reason ) ; } method param message param channel private protected _on_CloseSessionRequest ( message , channel ) { const request = message . request as CloseSessionRequest ; assert ( request instanceof CloseSessionRequest ) ; let response ; message . session_statusCode = StatusCodes . Good ; function sendError ( statusCode ) { return g_sendError ( channel , message , CloseSessionResponse , statusCode ) ; } function sendResponse ( response1 ) { channel . send_response ( "MSG" , response1 , message ) ; } const session = message . session ; if ( ! session ) { return sendError ( StatusCodes . BadSessionIdInvalid ) ; } const wasNotActivated = session . status === "new" ; ( async ( ) => { await this . _closeSession ( request . requestHeader . authenticationToken , request . deleteSubscriptions , "CloseSession" ) ; response = new CloseSessionResponse ( { } ) ; sendResponse ( response ) ; } ) ( ) ; } method param message param channel private protected _on_BrowseRequest ( message , channel ) { const request = message . request as BrowseRequest ; assert ( request instanceof BrowseRequest ) ; const diagnostic = { } ; this . _apply_on_SessionObject ( BrowseResponse , message , channel , ( session , sendResponse , sendError ) => { let response ; if ( request . view && ! request . view . viewId . isEmpty ( ) ) { let theView = this . engine ! . addressSpace ! . findNode ( request . view . viewId ) as UAView ; if ( theView && theView . nodeClass !== NodeClass . View ) { diagnostic . localizedText = { text : "Expecting a view here" } ; theView = null ; } if ( ! theView ) { return sendError ( StatusCodes . BadViewIdUnknown ) ; } } if ( ! request . nodesToBrowse || request . nodesToBrowse . length === 0 ) { return sendError ( StatusCodes . BadNothingToDo ) ; } if ( this . engine . serverCapabilities . operationLimits . maxNodesPerBrowse > 0 ) { if ( request . nodesToBrowse . length > this . engine . serverCapabilities . operationLimits . maxNodesPerBrowse ) { return sendError ( StatusCodes . BadTooManyOperations ) ; } } const requestedMaxReferencesPerNode = Math . min ( 9876 , request . requestedMaxReferencesPerNode ) ; assert ( request . nodesToBrowse [ 0 ] . schema . name === "BrowseDescription" ) ; const context = new SessionContext ( { session , server : this } ) ; const f = callbackify ( this . engine . browseWithAutomaticExpansion ) . bind ( this . engine ) ; ( f as any ) ( request . nodesToBrowse , context , ( err ? , results ? ) => { if ( ! results ) { throw new Error ( "internal error : " + err ?. message ) ; } assert ( results [ 0 ] . schema . name === "BrowseResult" ) ; const maxBrowseContinuationPoints = this . engine . serverCapabilities . maxBrowseContinuationPoints ; results = results . map ( ( result ) => { assert ( ! result . continuationPoint ) ; if ( ! session . continuationPointManager ) { return new BrowseResult ( { statusCode : StatusCodes . BadNoContinuationPoints } ) ; } if ( session . continuationPointManager . hasReachedMaximum ( maxBrowseContinuationPoints ) ) { return new BrowseResult ( { statusCode : StatusCodes . BadNoContinuationPoints } ) ; } const truncatedResult = session . continuationPointManager . registerReferences ( requestedMaxReferencesPerNode , result . references || [ ] , { continuationPoint : null } ) ; let { statusCode } = truncatedResult ; const { continuationPoint , values } = truncatedResult ; statusCode = result . statusCode ; return new BrowseResult ( { statusCode , continuationPoint , references : values } ) ; } ) ; response = new BrowseResponse ( { diagnosticInfos : undefined , results } ) ; sendResponse ( response ) ; } ) ; } ) ; } method param message param channel private protected _on_BrowseNextRequest ( message , channel ) { const request = message . request as BrowseNextRequest ; assert ( request instanceof BrowseNextRequest ) ; this . _apply_on_SessionObject ( BrowseNextResponse , message , channel , ( session , sendResponse , sendError ) => { if ( ! request . continuationPoints || request . continuationPoints . length === 0 ) { return sendError ( StatusCodes . BadNothingToDo ) ; } const results = request . continuationPoints . map ( ( continuationPoint , index ) => session . continuationPointManager . getNextReferences ( 0 , { continuationPoint , index , releaseContinuationPoints : request . releaseContinuationPoints } ) ) . map ( ( r ) => < BrowseResultOptions > { continuationPoint : r . continuationPoint , references : r . values , statusCode : r . statusCode } ) ; const response = new BrowseNextResponse ( { diagnosticInfos : undefined , results } ) ; sendResponse ( response ) ; } ) ; } protected _on_ReadRequest ( message , channel ) { const request = message . request as ReadRequest ; assert ( request instanceof ReadRequest ) ; this . _apply_on_SessionObject ( ReadResponse , message , channel , ( session , sendResponse , sendError ) => { const context = new SessionContext ( { session , server : this } ) ; let response ; let results = [ ] ; const timestampsToReturn = request . timestampsToReturn ; if ( timestampsToReturn === TimestampsToReturn . Invalid ) { return sendError ( StatusCodes . BadTimestampsToReturnInvalid ) ; } if ( request . maxAge < 0 ) { return sendError ( StatusCodes . BadMaxAgeInvalid ) ; } request . nodesToRead = request . nodesToRead || [ ] ; if ( ! request . nodesToRead || request . nodesToRead . length <= 0 ) { return sendError ( StatusCodes . BadNothingToDo ) ; } assert ( request . nodesToRead [ 0 ] . schema . name === "ReadValueId" ) ; if ( this . engine . serverCapabilities . operationLimits . maxNodesPerRead > 0 ) { if ( request . nodesToRead . length > this . engine . serverCapabilities . operationLimits . maxNodesPerRead ) { return sendError ( StatusCodes . BadTooManyOperations ) ; } } for ( const nodeToRead of request . nodesToRead ) { nodeToRead . nodeId = session . resolveRegisteredNode ( nodeToRead . nodeId ) ; } this . engine . refreshValues ( request . nodesToRead , request . maxAge , ( err ? ) => { assert ( ! err , " error not handled here , fix me" ) ; results = this . engine . read ( context , request ) ; assert ( results [ 0 ] . schema . name === "DataValue" ) ; assert ( results . length === request . nodesToRead ! . length ) ; response = new ReadResponse ( { diagnosticInfos : undefined , results : undefined } ) ; response . results = results ; assert ( response . diagnosticInfos ! . length === 0 ) ; sendResponse ( response ) ; } ) ; } ) ; } protected _on_HistoryReadRequest ( message , channel ) { const request = message . request as HistoryReadRequest ; assert ( request instanceof HistoryReadRequest ) ; this . _apply_on_SessionObject ( HistoryReadResponse , message , channel , ( session , sendResponse , sendError ) => { let response ; const timestampsToReturn = request . timestampsToReturn ; if ( timestampsToReturn === TimestampsToReturn . Invalid ) { return sendError ( StatusCodes . BadTimestampsToReturnInvalid ) ; } request . nodesToRead = request . nodesToRead || [ ] ; if ( ! request . nodesToRead || request . nodesToRead . length <= 0 ) { return sendError ( StatusCodes . BadNothingToDo ) ; } assert ( request . nodesToRead [ 0 ] . schema . name === "HistoryReadValueId" ) ; if ( this . engine . serverCapabilities . operationLimits . maxNodesPerRead > 0 ) { if ( request . nodesToRead . length > this . engine . serverCapabilities . operationLimits . maxNodesPerRead ) { return sendError ( StatusCodes . BadTooManyOperations ) ; } } if ( this . engine . serverCapabilities . operationLimits . maxNodesPerHistoryReadData > 0 ) { if ( request . nodesToRead . length > this . engine . serverCapabilities . operationLimits . maxNodesPerHistoryReadData ) { return sendError ( StatusCodes . BadTooManyOperations ) ; } } if ( this . engine . serverCapabilities . operationLimits . maxNodesPerHistoryReadEvents > 0 ) { if ( request . nodesToRead . length > this . engine . serverCapabilities . operationLimits . maxNodesPerHistoryReadEvents ) { return sendError ( StatusCodes . BadTooManyOperations ) ; } } const context = new SessionContext ( { session , server : this } ) ; this . engine . refreshValues ( request . nodesToRead , 0 , ( err ? ) => { assert ( ! err , " error not handled here , fix me" ) ; this . engine . historyRead ( context , request , ( err1 , results ? ) => { if ( err1 ) { return sendError ( StatusCodes . BadHistoryOperationInvalid ) ; } if ( ! results ) { return sendError ( StatusCodes . BadHistoryOperationInvalid ) ; } assert ( results [ 0 ] . schema . name === "HistoryReadResult" ) ; assert ( results . length === request . nodesToRead ! . length ) ; response = new HistoryReadResponse ( { diagnosticInfos : undefined , results } ) ; assert ( response . diagnosticInfos ! . length === 0 ) ; sendResponse ( response ) ; } ) ; } ) ; } ) ; } protected _on_WriteRequest ( message , channel ) { const request = message . request as WriteRequest ; assert ( request instanceof WriteRequest ) ; assert ( ! request . nodesToWrite || Array . isArray ( request . nodesToWrite ) ) ; this . _apply_on_SessionObject ( WriteResponse , message , channel , ( session , sendResponse , sendError ) => { let response ; if ( ! request . nodesToWrite || request . nodesToWrite . length === 0 ) { return sendError ( StatusCodes . BadNothingToDo ) ; } if ( this . engine . serverCapabilities . operationLimits . maxNodesPerWrite > 0 ) { if ( request . nodesToWrite . length > this . engine . serverCapabilities . operationLimits . maxNodesPerWrite ) { return sendError ( StatusCodes . BadTooManyOperations ) ; } } for ( const nodeToWrite of request . nodesToWrite ) { nodeToWrite . nodeId = session . resolveRegisteredNode ( nodeToWrite . nodeId ) ; } const context = new SessionContext ( { session , server : this } ) ; assert ( request . nodesToWrite [ 0 ] . schema . name === "WriteValue" ) ; this . engine . write ( context , request . nodesToWrite , ( err , results ? ) => { if ( err ) { errorLog ( err ) ; return sendError ( StatusCodes . BadInternalError ) ; } assert ( Array . isArray ( results ) ) ; assert ( results ! . length === request . nodesToWrite ! . length ) ; response = new WriteResponse ( { diagnosticInfos : undefined , results } ) ; sendResponse ( response ) ; } ) ; } ) ; } protected _on_CreateSubscriptionRequest ( message , channel ) { const engine = this . engine ; const addressSpace = engine . addressSpace ! ; const request = message . request as CreateSubscriptionRequest ; assert ( request instanceof CreateSubscriptionRequest ) ; this . _apply_on_SessionObject ( CreateSubscriptionResponse , message , channel , ( session , sendResponse , sendError ) => { const context = new SessionContext ( { session , server : this } ) ; if ( session . currentSubscriptionCount >= OPCUAServer . MAX_SUBSCRIPTION ) { return sendError ( StatusCodes . BadTooManySubscriptions ) ; } const subscription = session . createSubscription ( request ) ; subscription . on ( "monitoredItem" , ( monitoredItem ) => { prepareMonitoredItem ( context , addressSpace , monitoredItem ) ; } ) ; const response = new CreateSubscriptionResponse ( { revisedLifetimeCount : subscription . lifeTimeCount , revisedMaxKeepAliveCount : subscription . maxKeepAliveCount , revisedPublishingInterval : subscription . publishingInterval , subscriptionId : subscription . id } ) ; sendResponse ( response ) ; } ) ; } protected _on_DeleteSubscriptionsRequest ( message , channel ) { const request = message . request as DeleteSubscriptionsRequest ; assert ( request instanceof DeleteSubscriptionsRequest ) ; this . _apply_on_SubscriptionIds ( DeleteSubscriptionsResponse , message , channel , async ( session , subscriptionId ) => { let subscription = this . engine . findOrphanSubscription ( subscriptionId ) ; if ( subscription ) { warningLog ( "Deleting an orphan subscription" , subscriptionId ) ; await this . _beforeDeleteSubscription ( subscription ) ; return this . engine . deleteOrphanSubscription ( subscription ) ; } subscription = session . getSubscription ( subscriptionId ) ; if ( subscription ) { await this . _beforeDeleteSubscription ( subscription ) ; } return session . deleteSubscription ( subscriptionId ) ; } ) ; } protected _on_TransferSubscriptionsRequest ( message , channel ) { const engine = this . engine ; const request = message . request as TransferSubscriptionsRequest ; assert ( request instanceof TransferSubscriptionsRequest ) ; this . _apply_on_SubscriptionIds ( TransferSubscriptionsResponse , message , channel , async ( session , subscriptionId ) => await engine . transferSubscription ( session , subscriptionId , request . sendInitialValues ) ) ; } protected _on_CreateMonitoredItemsRequest ( message , channel ) { const engine = this . engine ; const addressSpace = engine . addressSpace ! ; const request = message . request as CreateMonitoredItemsRequest ; assert ( request instanceof CreateMonitoredItemsRequest ) ; this . _apply_on_Subscription ( CreateMonitoredItemsResponse , message , channel , async ( session , subscription , sendResponse , sendError ) : Promise < void > => { const timestampsToReturn = request . timestampsToReturn ; if ( timestampsToReturn === TimestampsToReturn . Invalid ) { return sendError ( StatusCodes . BadTimestampsToReturnInvalid ) ; } if ( ! request . itemsToCreate || request . itemsToCreate . length === 0 ) { return sendError ( StatusCodes . BadNothingToDo ) ; } if ( this . engine . serverCapabilities . operationLimits . maxMonitoredItemsPerCall > 0 ) { if ( request . itemsToCreate . length > this . engine . serverCapabilities . operationLimits . maxMonitoredItemsPerCall ) { return sendError ( StatusCodes . BadTooManyOperations ) ; } } const options = this . options as OPCUAServerOptions ; let results = [ ] ; if ( options . onCreateMonitoredItem ) { const resultsPromise = request . itemsToCreate . map ( async ( monitoredItemCreateRequest ) => { const { monitoredItem , createResult } = subscription . preCreateMonitoredItem ( addressSpace , timestampsToReturn , monitoredItemCreateRequest ) ; if ( monitoredItem ) { await options . onCreateMonitoredItem ! ( subscription , monitoredItem ) ; subscription . postCreateMonitoredItem ( monitoredItem , monitoredItemCreateRequest , createResult ) ; } return createResult ; } ) ; results = await Promise . all ( resultsPromise ) ; } else { results = request . itemsToCreate . map ( ( monitoredItemCreateRequest ) => { const { monitoredItem , createResult } = subscription . preCreateMonitoredItem ( addressSpace , timestampsToReturn , monitoredItemCreateRequest ) ; if ( monitoredItem ) { subscription . postCreateMonitoredItem ( monitoredItem , monitoredItemCreateRequest , createResult ) ; } return createResult ; } ) ; } const response = new CreateMonitoredItemsResponse ( { responseHeader : { serviceResult : StatusCodes . Good } , results } ) ; sendResponse ( response ) ; } ) ; } protected _on_ModifySubscriptionRequest ( message , channel ) { const request = message . request as ModifySubscriptionRequest ; assert ( request instanceof ModifySubscriptionRequest ) ; this . _apply_on_Subscription ( ModifySubscriptionResponse , message , channel , async ( session , subscription , sendResponse , sendError ) => { subscription . modify ( request ) ; const response = new ModifySubscriptionResponse ( { revisedLifetimeCount : subscription . lifeTimeCount , revisedMaxKeepAliveCount : subscription . maxKeepAliveCount , revisedPublishingInterval : subscription . publishingInterval } ) ; sendResponse ( response ) ; } ) ; } protected _on_ModifyMonitoredItemsRequest ( message , channel ) { const request = message . request as ModifyMonitoredItemsRequest ; assert ( request instanceof ModifyMonitoredItemsRequest ) ; this . _apply_on_Subscription ( ModifyMonitoredItemsResponse , message , channel , async ( session , subscription , sendResponse , sendError ) => { const timestampsToReturn = request . timestampsToReturn ; if ( timestampsToReturn === TimestampsToReturn . Invalid ) { return sendError ( StatusCodes . BadTimestampsToReturnInvalid ) ; } if ( ! request . itemsToModify || request . itemsToModify . length === 0 ) { return sendError ( StatusCodes . BadNothingToDo ) ; } if ( this . engine . serverCapabilities . operationLimits . maxMonitoredItemsPerCall > 0 ) { if ( request . itemsToModify . length > this . engine . serverCapabilities . operationLimits . maxMonitoredItemsPerCall ) { return sendError ( StatusCodes . BadTooManyOperations ) ; } } const itemsToModify = request . itemsToModify ; function modifyMonitoredItem ( item ) { const monitoredItemId = item . monitoredItemId ; const monitoredItem = subscription . getMonitoredItem ( monitoredItemId ) ; if ( ! monitoredItem ) { return new MonitoredItemModifyResult ( { statusCode : StatusCodes . BadMonitoredItemIdInvalid } ) ; } if ( item . requestedParameters . samplingInterval === - 1 ) { item . requestedParameters . samplingInterval = subscription . publishingInterval ; } return monitoredItem . modify ( timestampsToReturn , item . requestedParameters ) ; } const results = itemsToModify . map ( modifyMonitoredItem ) ; const response = new ModifyMonitoredItemsResponse ( { results } ) ; sendResponse ( response ) ; } ) ; } protected _on_PublishRequest ( message , channel ) { const request = message . request as PublishRequest ; assert ( request instanceof PublishRequest ) ; this . _apply_on_SessionObject ( PublishResponse , message , channel , ( session , sendResponse , sendError ) => { assert ( session ) ; assert ( session . publishEngine ) ; session . publishEngine . _on_PublishRequest ( request , ( request1 , response ) => { sendResponse ( response ) ; } ) ; } ) ; } protected _on_SetPublishingModeRequest ( message , channel ) { const request = message . request as SetPublishingModeRequest ; assert ( request instanceof SetPublishingModeRequest ) ; const publishingEnabled = request . publishingEnabled ; this . _apply_on_Subscriptions ( SetPublishingModeResponse , message , channel , async ( session , subscription ) => { return subscription . setPublishingMode ( publishingEnabled ) ; } ) ; } protected _on_DeleteMonitoredItemsRequest ( message , channel ) { const request = message . request as DeleteMonitoredItemsRequest ; assert ( request instanceof DeleteMonitoredItemsRequest ) ; this . _apply_on_Subscription ( DeleteMonitoredItemsResponse , message , channel , async ( session , subscription , sendResponse , sendError ) => { if ( ! request . monitoredItemIds || request . monitoredItemIds . length === 0 ) { return sendError ( StatusCodes . BadNothingToDo ) ; } if ( this . engine . serverCapabilities . operationLimits . maxMonitoredItemsPerCall > 0 ) { if ( request . monitoredItemIds . length > this . engine . serverCapabilities . operationLimits . maxMonitoredItemsPerCall ) { return sendError ( StatusCodes . BadTooManyOperations ) ; } } const resultsPromises = request . monitoredItemIds . map ( async ( monitoredItemId ) => { if ( this . options . onDeleteMonitoredItem ) { const monitoredItem = subscription . getMonitoredItem ( monitoredItemId ) ; if ( monitoredItem ) { await this . options . onDeleteMonitoredItem ( subscription , monitoredItem ) ; } } return subscription . removeMonitoredItem ( monitoredItemId ) ; } ) ; try { const results = await Promise . all ( resultsPromises ) ; const response = new DeleteMonitoredItemsResponse ( { diagnosticInfos : undefined , results } ) ; sendResponse ( response ) ; } catch ( err ) { console . log ( err ) ; return sendError ( StatusCodes . BadInternalError ) ; } } ) ; } protected _on_SetTriggeringRequest ( message , channel ) { const request = message . request as SetTriggeringRequest ; assert ( request instanceof SetTriggeringRequest ) ; this . _apply_on_Subscription ( SetTriggeringResponse , message , channel , async ( session , subscription , sendResponse , sendError ) => { const { triggeringItemId , linksToAdd , linksToRemove } = request ; /**
             * The MaxMonitoredItemsPerCall Property indicates
             * [...]
             *  • the maximum size of the sum of the linksToAdd and linksToRemove arrays when a
             *    Client calls the SetTriggering Service.
             *
             */ const maxElements = ( linksToAdd ? linksToAdd . length : 0 ) + ( linksToRemove ? linksToRemove . length : 0 ) ; if ( this . engine . serverCapabilities . operationLimits . maxMonitoredItemsPerCall > 0 ) { if ( maxElements > this . engine . serverCapabilities . operationLimits . maxMonitoredItemsPerCall ) { return sendError ( StatusCodes . BadTooManyOperations ) ; } } const { addResults , removeResults , statusCode } = subscription . setTriggering ( triggeringItemId , linksToAdd , linksToRemove ) ; const response = new SetTriggeringResponse ( { responseHeader : { serviceResult : statusCode } , addResults , removeResults , addDiagnosticInfos : null , removeDiagnosticInfos : null } ) ; sendResponse ( response ) ; } ) ; } protected async _beforeDeleteSubscription ( subscription ) { if ( ! this . options . onDeleteMonitoredItem ) { return ; } await subscription . applyOnMonitoredItem ( this . options . onDeleteMonitoredItem . bind ( null , subscription ) as any ) ; } protected _on_RepublishRequest ( message , channel ) { const request = message . request as RepublishRequest ; assert ( request instanceof RepublishRequest ) ; this . _apply_on_Subscription ( RepublishResponse , message , channel , async ( session , subscription , sendResponse , sendError ) => { subscription . subscriptionDiagnostics . republishRequestCount += 1 ; subscription . subscriptionDiagnostics . republishMessageRequestCount += 1 ; const retransmitSequenceNumber = request . retransmitSequenceNumber ; const notificationMessage = subscription . getMessageForSequenceNumber ( retransmitSequenceNumber ) ; if ( ! notificationMessage ) { return sendError ( StatusCodes . BadMessageNotAvailable ) ; } const response = new RepublishResponse ( { notificationMessage , responseHeader : { serviceResult : StatusCodes . Good } } ) ; subscription . subscriptionDiagnostics . republishMessageCount += 1 ; sendResponse ( response ) ; } ) ; } protected _on_SetMonitoringModeRequest ( message , channel ) { const request = message . request as SetMonitoringModeRequest ; assert ( request instanceof SetMonitoringModeRequest ) ; this . _apply_on_Subscription ( SetMonitoringModeResponse , message , channel , async ( session , subscription , sendResponse , sendError ) => { if ( ! request . monitoredItemIds || request . monitoredItemIds . length === 0 ) { return sendError ( StatusCodes . BadNothingToDo ) ; } if ( this . engine . serverCapabilities . operationLimits . maxMonitoredItemsPerCall > 0 ) { if ( request . monitoredItemIds . length > this . engine . serverCapabilities . operationLimits . maxMonitoredItemsPerCall ) { return sendError ( StatusCodes . BadTooManyOperations ) ; } } const monitoringMode = request . monitoringMode ; if ( ! isMonitoringModeValid ( monitoringMode ) ) { return sendError ( StatusCodes . BadMonitoringModeInvalid ) ; } const results = request . monitoredItemIds . map ( ( monitoredItemId ) => { const monitoredItem = subscription . getMonitoredItem ( monitoredItemId ) ; if ( ! monitoredItem ) { return StatusCodes . BadMonitoredItemIdInvalid ; } monitoredItem . setMonitoringMode ( monitoringMode ) ; return StatusCodes . Good ; } ) ; const response = new SetMonitoringModeResponse ( { results } ) ; sendResponse ( response ) ; } ) ; } protected _on_TranslateBrowsePathsToNodeIdsRequest ( message , channel ) { const request = message . request as TranslateBrowsePathsToNodeIdsRequest ; assert ( request instanceof TranslateBrowsePathsToNodeIdsRequest ) ; this . _apply_on_SessionObject ( TranslateBrowsePathsToNodeIdsResponse , message , channel , async ( session , sendResponse , sendError ) => { if ( ! request . browsePaths || request . browsePaths . length === 0 ) { return sendError ( StatusCodes . BadNothingToDo ) ; } if ( this . engine . serverCapabilities . operationLimits . maxNodesPerTranslateBrowsePathsToNodeIds > 0 ) { if ( request . browsePaths . length > this . engine . serverCapabilities . operationLimits . maxNodesPerTranslateBrowsePathsToNodeIds ) { return sendError ( StatusCodes . BadTooManyOperations ) ; } } const browsePathsResults = request . browsePaths . map ( ( browsePath ) => this . engine . browsePath ( browsePath ) ) ; const response = new TranslateBrowsePathsToNodeIdsResponse ( { diagnosticInfos : null , results : browsePathsResults } ) ; sendResponse ( response ) ; } ) ; } protected _on_CallRequest ( message , channel ) { const request = message . request as CallRequest ; assert ( request instanceof CallRequest ) ; this . _apply_on_SessionObject ( CallResponse , message , channel , ( session , sendResponse , sendError ) => { let response ; if ( ! request . methodsToCall || request . methodsToCall . length === 0 ) { return sendError ( StatusCodes . BadNothingToDo ) ; } let maxNodesPerMethodCall = this . engine . serverCapabilities . operationLimits . maxNodesPerMethodCall ; maxNodesPerMethodCall = maxNodesPerMethodCall <= 0 ? 1000 : maxNodesPerMethodCall ; if ( request . methodsToCall . length > maxNodesPerMethodCall ) { return sendError ( StatusCodes . BadTooManyOperations ) ; } const addressSpace = this . engine . addressSpace ! ; const context = new SessionContext ( { session , server : this } ) ; async . map ( request . methodsToCall , callMethodHelper . bind ( null , context , addressSpace ) , ( err ? , results ? ) => { if ( err ) { errorLog ( "ERROR in method Call !! " , err ) ; } assert ( Array . isArray ( results ) ) ; response = new CallResponse ( { results : results as CallMethodResultOptions [ ] } ) ; sendResponse ( response ) ; } ) ; } ) ; } protected _on_RegisterNodesRequest ( message , channel ) { const request = message . request as RegisterNodesRequest ; assert ( request instanceof RegisterNodesRequest ) ; this . _apply_on_SessionObject ( RegisterNodesResponse , message , channel , ( session , sendResponse , sendError ) => { let response ; if ( ! request . nodesToRegister || request . nodesToRegister . length === 0 ) { response = new RegisterNodesResponse ( { responseHeader : { serviceResult : StatusCodes . BadNothingToDo } } ) ; return sendResponse ( response ) ; } if ( this . engine . serverCapabilities . operationLimits . maxNodesPerRegisterNodes > 0 ) { if ( request . nodesToRegister . length > this . engine . serverCapabilities . operationLimits . maxNodesPerRegisterNodes ) { return sendError ( StatusCodes . BadTooManyOperations ) ; } } const registeredNodeIds = request . nodesToRegister . map ( ( nodeId ) => session . registerNode ( nodeId ) ) ; response = new RegisterNodesResponse ( { registeredNodeIds } ) ; sendResponse ( response ) ; } ) ; } protected _on_UnregisterNodesRequest ( message , channel ) { const request = message . request as UnregisterNodesRequest ; assert ( request instanceof UnregisterNodesRequest ) ; this . _apply_on_SessionObject ( UnregisterNodesResponse , message , channel , ( session , sendResponse , sendError ) => { let response ; request . nodesToUnregister = request . nodesToUnregister || [ ] ; if ( ! request . nodesToUnregister || request . nodesToUnregister . length === 0 ) { response = new UnregisterNodesResponse ( { responseHeader : { serviceResult : StatusCodes . BadNothingToDo } } ) ; return sendResponse ( response ) ; } if ( this . engine . serverCapabilities . operationLimits . maxNodesPerRegisterNodes > 0 ) { if ( request . nodesToUnregister . length > this . engine . serverCapabilities . operationLimits . maxNodesPerRegisterNodes ) { return sendError ( StatusCodes . BadTooManyOperations ) ; } } request . nodesToUnregister . map ( ( nodeId ) => session . unRegisterNode ( nodeId ) ) ; response = new UnregisterNodesResponse ( { } ) ; sendResponse ( response ) ; } ) ; } protected _on_Cancel ( message , channel ) { return g_sendError ( channel , message , CancelResponse , StatusCodes . BadServiceUnsupported ) ; } protected _on_AddNodes ( message , channel ) { return g_sendError ( channel , message , AddNodesResponse , StatusCodes . BadServiceUnsupported ) ; } protected _on_AddReferences ( message , channel ) { return g_sendError ( channel , message , AddReferencesResponse , StatusCodes . BadServiceUnsupported ) ; } protected _on_DeleteNodes ( message , channel ) { return g_sendError ( channel , message , DeleteNodesResponse , StatusCodes . BadServiceUnsupported ) ; } protected _on_DeleteReferences ( message , channel ) { return g_sendError ( channel , message , DeleteReferencesResponse , StatusCodes . BadServiceUnsupported ) ; } protected _on_QueryFirst ( message , channel ) { return g_sendError ( channel , message , QueryFirstResponse , StatusCodes . BadServiceUnsupported ) ; } protected _on_QueryNext ( message , channel ) { return g_sendError ( channel , message , QueryNextResponse , StatusCodes . BadServiceUnsupported ) ; } protected _on_HistoryUpdate ( message , channel ) { return g_sendError ( channel , message , HistoryUpdateResponse , StatusCodes . BadServiceUnsupported ) ; } private createEndpoint ( port1 , serverOptions ) { const endPoint = new OPCUAServerEndPoint ( { port : port1 , certificateManager : this . serverCertificateManager , certificateChain : this . getCertificateChain ( ) , privateKey : this . getPrivateKey ( ) , defaultSecureTokenLifetime : serverOptions . defaultSecureTokenLifetime || 600000 , timeout : serverOptions . timeout || 3 * 60 * 1000 , maxConnections : this . maxConnectionsPerEndpoint , objectFactory : this . objectFactory , serverInfo : this . serverInfo } ) ; return endPoint ; } private createEndpointDescriptions ( serverOption , endpointOptions ) { if ( ! endpointOptions ) { throw new Error ( "internal error" ) ; } const hostname = getFullyQualifiedDomainName ( ) ; endpointOptions . hostname = endpointOptions . hostname || hostname ; endpointOptions . port = endpointOptions . port || 26543 ; if ( ! Object . prototype . hasOwnProperty . call ( endpointOptions , "port" ) || ! isFinite ( endpointOptions . port ! ) || typeof endpointOptions . port !== "number" ) { throw new Error ( "expecting a valid port (number)" ) ; } const port = Number ( endpointOptions . port || 0 ) ; const endPoint = this . createEndpoint ( port , serverOption ) ; endpointOptions . alternateHostname = endpointOptions . alternateHostname || [ ] ; const alternateHostname = endpointOptions . alternateHostname instanceof Array ? endpointOptions . alternateHostname : [ endpointOptions . alternateHostname ] ; const allowAnonymous = endpointOptions . allowAnonymous === undefined ? true : ! ! endpointOptions . allowAnonymous ; endPoint . addStandardEndpointDescriptions ( { allowAnonymous , securityModes : endpointOptions . securityModes , securityPolicies : endpointOptions . securityPolicies , hostname : endpointOptions . hostname , alternateHostname , disableDiscovery : ! ! endpointOptions . disableDiscovery , resourcePath : serverOption . resourcePath || "" } ) ; return endPoint ; } public async initializeCM ( ) { await super . initializeCM ( ) ; await this . userCertificateManager . initialize ( ) ; } } export interface RaiseEventAuditEventData extends RaiseEventData { actionTimeStamp : PseudoVariantDateTime ; status : PseudoVariantBoolean ; serverId : PseudoVariantString ; /**
     * ClientAuditEntryId contains the human-readable AuditEntryId defined in Part 3.
     */ clientAuditEntryId : PseudoVariantString ; /**
     * The ClientUserId identifies the user of the client requesting an action. The ClientUserId can be
     * obtained from the UserIdentityToken passed in the ActivateSession call.
     */ clientUserId : PseudoVariantString ; sourceName : PseudoVariantString ; } export interface RaiseEventAuditUpdateMethodEventData extends RaiseEventAuditEventData { methodId : PseudoVariantNodeId ; inputArguments : any ; } export interface RaiseEventAuditConditionCommentEventData extends RaiseEventAuditUpdateMethodEventData { eventId : PseudoVariantByteString ; comment : PseudoVariantLocalizedText ; } export interface RaiseEventAuditSessionEventData extends RaiseEventAuditEventData { /**
     *  part 5 - 6.4.7 AuditSessionEventType
     */ sessionId : PseudoVariantNodeId ; } export interface RaiseEventAuditCreateSessionEventData extends RaiseEventAuditSessionEventData { /**
     *  part 5 - 6.4.8 AuditCreateSessionEventType
     *  SecureChannelId shall uniquely identify the SecureChannel.
     *  The application shall use the same identifier in
     *  all AuditEvents related to the Session Service Set (AuditCreateSessionEventType, AuditActivateSessionEventType
     *  and their subtypes) and the SecureChannel Service Set (AuditChannelEventType and its subtype
     */ secureChannelId : PseudoVariantString ; revisedSessionTimeout : PseudoVariantDuration ; clientCertificate : PseudoVariantByteString ; clientCertificateThumbprint : PseudoVariantString ; } export interface RaiseEventAuditActivateSessionEventData extends RaiseEventAuditSessionEventData { /**
     * part 5 - 6.4.10 AuditActivateSessionEventType
     */ clientSoftwareCertificates : PseudoVariantExtensionObjectArray ; /**
     * UserIdentityToken reflects the userIdentityToken parameter of the ActivateSession Service call.
     * For Username/Password tokens the password should NOT be included.
     */ userIdentityToken : PseudoVariantExtensionObject ; /**
     * SecureChannelId shall uniquely identify the SecureChannel. The application shall use the same identifier
     * in all AuditEvents related to the Session Service Set (AuditCreateSessionEventType,
     * AuditActivateSessionEventType and their subtypes) and the SecureChannel Service Set
     * (AuditChannelEventType and its subtypes).
     */ secureChannelId : PseudoVariantString ; } export interface RaiseEventTransitionEventData extends RaiseEventData { } export interface RaiseEventAuditUrlMismatchEventTypeData extends RaiseEventData { endpointUrl : PseudoVariantString ; } export interface OPCUAServer { internal param eventType param options raiseEvent ( eventType , options ) : void ; raiseEvent ( eventType , options ) : void ; raiseEvent ( eventType , options ) : void ; raiseEvent ( eventType , options ) : void ; raiseEvent ( eventType , options ) : void ; raiseEvent ( eventType , options ) : void ; raiseEvent ( eventType , options ) : void ; } export interface OPCUAServer extends EventEmitter { on ( event , eventHandler ) : this ; on ( event , eventHandler ) : this ; on ( event , eventHandler ) : this ; on ( event , eventHandler ) : this ; event on ( event , eventHandler ) : this ; event on ( event , eventHandler ) : this ; event on ( event , eventHandler ) : this ; event on ( event , eventHandler ) : this ; /**
     * event raised after the server has raised an OPCUA event toward a client
     */ on ( event , eventHandler ) : this ; /**
     * event raised when the server received a request from one of its connected client.
     * useful for trace purpose.
     */ on ( event , eventHandler ) : this ; /**
     * event raised when the server send an response to a request to one of its connected client.
     * useful for trace purpose.
     */ on ( event , eventHandler ) : this ; /**
     * event raised when a new secure channel is opened
     */ on ( event , eventHandler ) : this ; /**
     * event raised when a new secure channel is closed
     */ on ( event , eventHandler ) : this ; /**
     * event raised when the server refused a tcp connection from a client. ( for instance because too any connections)
     */ on ( event , eventHandler ) : this ; /**
     * event raised when a OpenSecureChannel has failed, it could be a invalid certificate or malformed message
     */ on ( event , eventHandler ) : this ; on ( event , eventHandler ) : this ; } const opts = { multiArgs : false } ; OPCUAServer . prototype . start = thenify . withCallback ( OPCUAServer . prototype . start , opts ) ; OPCUAServer . prototype . initialize = thenify . withCallback ( OPCUAServer . prototype . initialize , opts ) ; OPCUAServer . prototype . shutdown = thenify . withCallback ( OPCUAServer . prototype . shutdown , opts ) ;