module import { EventEmitter } from "events" ; import * as net from "net" ; import { Server , Socket } from "net" ; import * as chalk from "chalk" ; import * as async from "async" ; import { assert } from "node-opcua-assert" ; import { ICertificateManager , OPCUACertificateManager } from "node-opcua-certificate-manager" ; import { Certificate , convertPEMtoDER , makeSHA1Thumbprint , PrivateKeyPEM , split_der } from "node-opcua-crypto" ; import { checkDebugFlag , make_debugLog , make_errorLog } from "node-opcua-debug" ; import { getFullyQualifiedDomainName , resolveFullyQualifiedDomainName } from "node-opcua-hostname" ; import { fromURI , MessageSecurityMode , SecurityPolicy , ServerSecureChannelLayer , ServerSecureChannelParent , toURI , AsymmetricAlgorithmSecurityHeader , IServerSessionBase , Message } from "node-opcua-secure-channel" ; import { UserTokenType } from "node-opcua-service-endpoints" ; import { EndpointDescription } from "node-opcua-service-endpoints" ; import { ApplicationDescription } from "node-opcua-service-endpoints" ; import { IChannelData } from "./i_channel_data" ; import { ISocketData } from "./i_socket_data" ; const debugLog = make_debugLog ( __filename ) ; const errorLog = make_errorLog ( __filename ) ; const doDebug = checkDebugFlag ( __filename ) ; const default_transportProfileUri = "http://opcfoundation.org/UA-Profile/Transport/uatcp-uasc-uabinary" ; function extractSocketData ( socket , reason ) { const { bytesRead , bytesWritten , remoteAddress , remoteFamily , remotePort , localAddress , localPort } = socket ; const data = { bytesRead , bytesWritten , localAddress , localPort , remoteAddress , remoteFamily , remotePort , timestamp : new Date ( ) , reason } ; return data ; } function extractChannelData ( channel ) { const { channelId , clientCertificate , clientNonce , clientSecurityHeader , securityHeader , securityMode , securityPolicy , timeout , transactionsCount } = channel ; const channelData = { channelId , clientCertificate , clientNonce , clientSecurityHeader , securityHeader , securityMode , securityPolicy , timeout , transactionsCount } ; return channelData ; } function dumpChannelInfo ( channels ) { function d ( s ) { return `[ status=${ s . status } lastSeen=${ s . clientLastContactTime . toFixed ( 0 ) }ms sessionName=${ s . sessionName } timeout=${ s . sessionTimeout } ]` ; } function dumpChannel ( channel ) { console . log ( "------------------------------------------------------" ) ; console . log ( "            channelId = " , channel . channelId ) ; console . log ( "             timeout  = " , channel . timeout ) ; console . log ( "        remoteAddress = " , channel . remoteAddress ) ; console . log ( "        remotePort    = " , channel . remotePort ) ; console . log ( "" ) ; console . log ( "        bytesWritten  = " , channel . bytesWritten ) ; console . log ( "        bytesRead     = " , channel . bytesRead ) ; console . log ( "        sessions      = " , Object . keys ( channel . sessionTokens ) . length ) ; console . log ( Object . values ( channel . sessionTokens ) . map ( d ) . join ( "\n" ) ) ; const socket = ( channel as any ) . transport ?. _socket ; if ( ! socket ) { console . log ( " SOCKET IS CLOSED" ) ; } } for ( const channel of channels ) { dumpChannel ( channel ) ; } console . log ( "------------------------------------------------------" ) ; } const emptyCertificate = Buffer . alloc ( 0 ) ; const emptyPrivateKeyPEM = "" ; let OPCUAServerEndPointCounter = 0 ; export interface OPCUAServerEndPointOptions { /**
     * the tcp port
     */ port : number ; /**
     * the DER certificate chain
     */ certificateChain : Certificate ; /**
     * privateKey
     */ privateKey : PrivateKeyPEM ; certificateManager : OPCUACertificateManager ; /**
     *  the default secureToken lifetime @default=60000
     */ defaultSecureTokenLifetime ? : number ; default maxConnections ? : number ; default timeout ? : number ; serverInfo : ApplicationDescription ; objectFactory ? : any ; } export interface EndpointDescriptionParams { allowAnonymous ? : boolean ; restricted ? : boolean ; allowUnsecurePassword ? : boolean ; resourcePath ? : string ; alternateHostname ? : string [ ] ; hostname : string ; securityPolicies : SecurityPolicy [ ] ; } export interface AddStandardEndpointDescriptionsParam { securityModes ? : MessageSecurityMode [ ] ; securityPolicies ? : SecurityPolicy [ ] ; disableDiscovery ? : boolean ; allowAnonymous ? : boolean ; restricted ? : boolean ; hostname ? : string ; alternateHostname ? : string [ ] ; allowUnsecurePassword ? : boolean ; resourcePath ? : string ; } function getUniqueName ( name , collection ) { if ( collection [ name ] ) { let counter = 0 ; while ( collection [ name + "_" + counter . toString ( ) ] ) { counter ++ ; } name = name + "_" + counter . toString ( ) ; collection [ name ] = 1 ; return name ; } else { collection [ name ] = 1 ; return name ; } } interface ServerSecureChannelLayerPriv extends ServerSecureChannelLayer { _unpreregisterChannelEvent ? : ( ) => void ; } /**
 * OPCUAServerEndPoint a Server EndPoint.
 * A sever end point is listening to one port
 * note:
 *   see OPCUA Release 1.03 part 4 page 108 7.1 ApplicationDescription
 */ export class OPCUAServerEndPoint extends EventEmitter implements ServerSecureChannelParent { /**
     * the tcp port
     */ public port : number ; public certificateManager : OPCUACertificateManager ; public defaultSecureTokenLifetime : number ; public maxConnections : number ; public timeout : number ; public bytesWrittenInOldChannels : number ; public bytesReadInOldChannels : number ; public transactionsCountOldChannels : number ; public securityTokenCountOldChannels : number ; public serverInfo : ApplicationDescription ; public objectFactory : any ; public _on_new_channel ? : ( channel : ServerSecureChannelLayer ) => void ; public _on_close_channel ? : ( channel : ServerSecureChannelLayer ) => void ; public _on_connectionRefused ? : ( socketData : any ) => void ; public _on_openSecureChannelFailure ? : ( socketData : any , channelData : any ) => void ; private _certificateChain : Certificate ; private _privateKey : PrivateKeyPEM ; private _channels : { [ key ] : ServerSecureChannelLayer ; } ; private _server ? : Server ; private _endpoints : EndpointDescription [ ] ; private _listen_callback ? : ( err ? : Error ) => void ; private _started = false ; private _counter = OPCUAServerEndPointCounter ++ ; private _policy_deduplicator : { [ key ] : number ; } = { } ; constructor ( options ) { super ( ) ; assert ( ! Object . prototype . hasOwnProperty . call ( options , "certificate" ) , "expecting a certificateChain instead" ) ; assert ( Object . prototype . hasOwnProperty . call ( options , "certificateChain" ) , "expecting a certificateChain" ) ; assert ( Object . prototype . hasOwnProperty . call ( options , "privateKey" ) ) ; this . certificateManager = options . certificateManager ; options . port = options . port || 0 ; this . port = parseInt ( options . port . toString ( ) , 10 ) ; assert ( typeof this . port === "number" ) ; this . _certificateChain = options . certificateChain ; this . _privateKey = options . privateKey ; this . _channels = { } ; this . defaultSecureTokenLifetime = options . defaultSecureTokenLifetime || 600000 ; this . maxConnections = options . maxConnections || 20 ; this . timeout = options . timeout || 30000 ; this . _server = undefined ; this . _setup_server ( ) ; this . _endpoints = [ ] ; this . objectFactory = options . objectFactory ; this . bytesWrittenInOldChannels = 0 ; this . bytesReadInOldChannels = 0 ; this . transactionsCountOldChannels = 0 ; this . securityTokenCountOldChannels = 0 ; this . serverInfo = options . serverInfo ; assert ( this . serverInfo !== null && typeof this . serverInfo === "object" ) ; } public dispose ( ) { this . _certificateChain = emptyCertificate ; this . _privateKey = emptyPrivateKeyPEM ; assert ( Object . keys ( this . _channels ) . length === 0 , "OPCUAServerEndPoint channels must have been deleted" ) ; this . _channels = { } ; this . serverInfo = new ApplicationDescription ( { } ) ; this . _endpoints = [ ] ; assert ( this . _endpoints . length === 0 , "endpoints must have been deleted" ) ; this . _endpoints = [ ] ; this . _server = undefined ; this . _listen_callback = undefined ; this . removeAllListeners ( ) ; } public toString ( ) { const privateKey1 = convertPEMtoDER ( this . getPrivateKey ( ) ) ; const txt = " end point" + this . _counter + " port = " + this . port + " l = " + this . _endpoints . length + " " + makeSHA1Thumbprint ( this . getCertificateChain ( ) ) . toString ( "hex" ) + " " + makeSHA1Thumbprint ( privateKey1 ) . toString ( "hex" ) ; return txt ; } public getChannels ( ) { return Object . values ( this . _channels ) ; } /**
     * Returns the X509 DER form of the server certificate
     */ public getCertificate ( ) { return split_der ( this . getCertificateChain ( ) ) [ 0 ] ; } /**
     * Returns the X509 DER form of the server certificate
     */ public getCertificateChain ( ) { return this . _certificateChain ; } /**
     * the private key
     */ public getPrivateKey ( ) { return this . _privateKey ; } /**
     * The number of active channel on this end point.
     */ public get currentChannelCount ( ) : number { return Object . keys ( this . _channels ) . length ; } method param securityMode param securityPolicy return public getEndpointDescription ( securityMode , securityPolicy , endpointUrl ) { const endpoints = this . endpointDescriptions ( ) ; const arr = endpoints . filter ( matching_endpoint . bind ( this , securityMode , securityPolicy , endpointUrl ) ) ; if ( endpointUrl && endpointUrl . length > 0 && ! ( arr . length === 0 || arr . length === 1 ) ) { errorLog ( "Several matching endpoints have been found : " ) ; for ( const a of arr ) { errorLog ( "   " , a . endpointUrl , MessageSecurityMode [ securityMode ] , securityPolicy ) ; } } return arr . length === 0 ? null : arr [ 0 ] ; } public addEndpointDescription ( securityMode , securityPolicy , options ? ) { if ( ! options ) { options = { hostname : getFullyQualifiedDomainName ( ) , securityPolicies : [ SecurityPolicy . Basic256Sha256 ] } ; } options . allowAnonymous = options . allowAnonymous === undefined ? true : options . allowAnonymous ; if ( securityMode === MessageSecurityMode . None && securityPolicy !== SecurityPolicy . None ) { throw new Error ( " invalid security " ) ; } if ( securityMode !== MessageSecurityMode . None && securityPolicy === SecurityPolicy . None ) { throw new Error ( " invalid security " ) ; } const port = this . port ; const resourcePath = ( options . resourcePath || "" ) . replace ( /\\/g , "/" ) ; assert ( resourcePath . length === 0 || resourcePath . charAt ( 0 ) === "/" , "resourcePath should start with /" ) ; const hostname = options . hostname || getFullyQualifiedDomainName ( ) ; const endpointUrl = `opc.tcp://${ hostname }:${ port }${ resourcePath }` ; const endpoint_desc = this . getEndpointDescription ( securityMode , securityPolicy , endpointUrl ) ; if ( endpoint_desc ) { throw new Error ( " endpoint already exist" ) ; } this . _endpoints . push ( _makeEndpointDescription ( { collection : this . _policy_deduplicator , endpointUrl , hostname , port , server : this . serverInfo , serverCertificateChain : this . getCertificateChain ( ) , securityMode , securityPolicy , allowAnonymous : options . allowAnonymous , allowUnsecurePassword : options . allowUnsecurePassword , resourcePath : options . resourcePath , restricted : ! ! options . restricted , securityPolicies : options ?. securityPolicies || [ ] } ) ) ; } public addRestrictedEndpointDescription ( options ) { options = { ... options } ; options . restricted = true ; return this . addEndpointDescription ( MessageSecurityMode . None , SecurityPolicy . None , options ) ; } public addStandardEndpointDescriptions ( options ? ) { options = options || { } ; options . securityModes = options . securityModes || defaultSecurityModes ; options . securityPolicies = options . securityPolicies || defaultSecurityPolicies ; const defaultHostname = options . hostname || getFullyQualifiedDomainName ( ) ; let hostnames = [ defaultHostname ] ; options . alternateHostname = options . alternateHostname || [ ] ; if ( typeof options . alternateHostname === "string" ) { options . alternateHostname = [ options . alternateHostname ] ; } hostnames = [ ... new Set ( hostnames . concat ( options . alternateHostname as string [ ] ) ) ] ; for ( const alternateHostname of hostnames ) { const optionsE = options as EndpointDescriptionParams ; optionsE . hostname = alternateHostname ; if ( options . securityModes . indexOf ( MessageSecurityMode . None ) >= 0 ) { this . addEndpointDescription ( MessageSecurityMode . None , SecurityPolicy . None , optionsE ) ; } else { if ( ! options . disableDiscovery ) { this . addRestrictedEndpointDescription ( optionsE ) ; } } for ( const securityMode of options . securityModes ) { if ( securityMode === MessageSecurityMode . None ) { continue ; } for ( const securityPolicy of options . securityPolicies ) { if ( securityPolicy === SecurityPolicy . None ) { continue ; } this . addEndpointDescription ( securityMode , securityPolicy , optionsE ) ; } } } } /**
     * returns the list of end point descriptions.
     */ public endpointDescriptions ( ) { return this . _endpoints ; } method async public listen ( callback ) { assert ( typeof callback === "function" ) ; assert ( ! this . _started , "OPCUAServerEndPoint is already listening" ) ; this . _listen_callback = callback ; this . _server ! . on ( "error" , ( err ) => { debugLog ( chalk . red . bold ( " error" ) + " port = " + this . port , err ) ; this . _started = false ; this . _end_listen ( err ) ; } ) ; this . _server ! . on ( "listening" , ( ) => { debugLog ( "server is listening" ) ; } ) ; this . _server ! . listen ( this . port , ( err ? ) => { debugLog ( chalk . green . bold ( "LISTENING TO PORT " ) , this . port , "err  " , err ) ; assert ( ! err , " cannot listen to port " ) ; this . _started = true ; this . _end_listen ( ) ; } ) ; } public killClientSockets ( callback ) { for ( const channel of this . getChannels ( ) ) { const hacked_channel = channel as any ; if ( hacked_channel . transport && hacked_channel . transport . _socket ) { hacked_channel . transport . _socket . destroy ( ) ; hacked_channel . transport . _socket . emit ( "error" , new Error ( "EPIPE" ) ) ; } } callback ( ) ; } public suspendConnection ( callback ) { if ( ! this . _started ) { return callback ( new Error ( "Connection already suspended !!" ) ) ; } this . _server ! . close ( ( ) => { this . _started = false ; debugLog ( "Connection has been closed !" + this . port ) ; } ) ; this . _started = false ; callback ( ) ; } public restoreConnection ( callback ) { this . listen ( callback ) ; } public abruptlyInterruptChannels ( ) { for ( const channel of Object . values ( this . _channels ) ) { channel . abruptlyInterrupt ( ) ; } } method async public shutdown ( callback ) { debugLog ( "OPCUAServerEndPoint#shutdown " ) ; if ( this . _started ) { this . suspendConnection ( ( ) => { const _channels = Object . values ( this . _channels ) ; async . each ( _channels , ( channel , callback1 ) => { this . shutdown_channel ( channel , callback1 ) ; } , ( err ? ) => { if ( ! ( Object . keys ( this . _channels ) . length === 0 ) ) { errorLog ( " Bad !" ) ; } assert ( Object . keys ( this . _channels ) . length === 0 , "channel must have unregistered themselves" ) ; callback ( err || undefined ) ; } ) ; } ) ; } else { callback ( ) ; } } method async param callback public start ( callback ) { assert ( typeof callback === "function" ) ; this . listen ( callback ) ; } public get bytesWritten ( ) : number { const channels = Object . values ( this . _channels ) ; return ( this . bytesWrittenInOldChannels + channels . reduce ( ( accumulated , channel ) => { return accumulated + channel . bytesWritten ; } , 0 ) ) ; } public get bytesRead ( ) : number { const channels = Object . values ( this . _channels ) ; return ( this . bytesReadInOldChannels + channels . reduce ( ( accumulated , channel ) => { return accumulated + channel . bytesRead ; } , 0 ) ) ; } public get transactionsCount ( ) : number { const channels = Object . values ( this . _channels ) ; return ( this . transactionsCountOldChannels + channels . reduce ( ( accumulated , channel ) => { return accumulated + channel . transactionsCount ; } , 0 ) ) ; } public get securityTokenCount ( ) : number { const channels = Object . values ( this . _channels ) ; return ( this . securityTokenCountOldChannels + channels . reduce ( ( accumulated , channel ) => { return accumulated + channel . securityTokenCount ; } , 0 ) ) ; } public get activeChannelCount ( ) : number { return Object . keys ( this . _channels ) . length ; } private _dump_statistics ( ) { this . _server ! . getConnections ( ( err , count ) => { debugLog ( chalk . cyan ( "CONCURRENT CONNECTION = " ) , count ) ; } ) ; debugLog ( chalk . cyan ( "MAX CONNECTIONS = " ) , this . _server ! . maxConnections ) ; } private _setup_server ( ) { assert ( ! this . _server ) ; this . _server = net . createServer ( { pauseOnConnect : true } , this . _on_client_connection . bind ( this ) ) ; this . _server . maxConnections = this . maxConnections + 1 ; this . _listen_callback = undefined ; this . _server . on ( "connection" , ( socket ) => { if ( doDebug ) { this . _dump_statistics ( ) ; debugLog ( "server connected  with : " + ( socket as any ) . remoteAddress + ":" + ( socket as any ) . remotePort ) ; } } ) . on ( "close" , ( ) => { debugLog ( "server closed : all connections have ended" ) ; } ) . on ( "error" , ( err ) => { debugLog ( chalk . red . bold ( "server error: " ) , err . message ) ; } ) ; } private _on_client_connection ( socket ) { socket . setNoDelay ( true ) ; debugLog ( "OPCUAServerEndPoint#_on_client_connection" , this . _started ) ; if ( ! this . _started ) { debugLog ( chalk . bgWhite . cyan ( "OPCUAServerEndPoint#_on_client_connection " + "SERVER END POINT IS PROBABLY SHUTTING DOWN !!! - Connection is refused" ) ) ; socket . end ( ) ; return ; } const deny_connection = ( ) => { console . log ( chalk . bgWhite . cyan ( "OPCUAServerEndPoint#_on_client_connection " + "The maximum number of connection has been reached - Connection is refused" ) ) ; const reason = "maxConnections reached (" + this . maxConnections + ")" ; const socketData = extractSocketData ( socket , reason ) ; this . emit ( "connectionRefused" , socketData ) ; socket . end ( ) ; socket . destroy ( ) ; } ; const establish_connection = ( ) => { const nbConnections = Object . keys ( this . _channels ) . length ; debugLog ( " nbConnections " , nbConnections , " self._server.maxConnections" , this . _server ! . maxConnections , this . maxConnections ) ; if ( nbConnections >= this . maxConnections ) { deny_connection ( ) ; return ; } debugLog ( "OPCUAServerEndPoint._on_client_connection successful => New Channel" ) ; const channel = new ServerSecureChannelLayer ( { defaultSecureTokenLifetime : this . defaultSecureTokenLifetime , parent : this , timeout : this . timeout } ) ; debugLog ( "channel Timeout = >" , channel . timeout ) ; socket . resume ( ) ; this . _preregisterChannel ( channel ) ; channel . init ( socket , ( err ? ) => { this . _un_pre_registerChannel ( channel ) ; debugLog ( chalk . yellow . bold ( "Channel#init done" ) , err ) ; if ( err ) { const reason = "openSecureChannel has Failed " + err . message ; const socketData = extractSocketData ( socket , reason ) ; const channelData = extractChannelData ( channel ) ; this . emit ( "openSecureChannelFailure" , socketData , channelData ) ; socket . end ( ) ; socket . destroy ( ) ; } else { debugLog ( "server receiving a client connection" ) ; this . _registerChannel ( channel ) ; } } ) ; channel . on ( "message" , ( message ) => { this . emit ( "message" , message , channel , this ) ; } ) ; } ; this . _prevent_DDOS_Attack ( establish_connection , deny_connection ) ; } private _preregisterChannel ( channel ) { assert ( this . _started , "OPCUAServerEndPoint must be started" ) ; assert ( ! Object . prototype . hasOwnProperty . call ( this . _channels , channel . hashKey ) , " channel already preregistered!" ) ; const channelPriv = < ServerSecureChannelLayerPriv > channel ; this . _channels [ channel . hashKey ] = channelPriv ; channelPriv . _unpreregisterChannelEvent = ( ) => { debugLog ( "Channel received an abort event during the preregistration phase" ) ; this . _un_pre_registerChannel ( channel ) ; channel . dispose ( ) ; } ; channel . on ( "abort" , channelPriv . _unpreregisterChannelEvent ) ; } private _un_pre_registerChannel ( channel ) { if ( ! this . _channels [ channel . hashKey ] ) { debugLog ( "Already un preregistered ?" , channel . hashKey ) ; return ; } delete this . _channels [ channel . hashKey ] ; const channelPriv = < ServerSecureChannelLayerPriv > channel ; if ( typeof channelPriv . _unpreregisterChannelEvent === "function" ) { channel . removeListener ( "abort" , channelPriv . _unpreregisterChannelEvent ! ) ; channelPriv . _unpreregisterChannelEvent = undefined ; } } method param channel private private _registerChannel ( channel ) { if ( this . _started ) { debugLog ( chalk . red ( "_registerChannel = " ) , "channel.hashKey = " , channel . hashKey ) ; assert ( ! this . _channels [ channel . hashKey ] ) ; this . _channels [ channel . hashKey ] = channel ; event param channel this . emit ( "newChannel" , channel ) ; channel . on ( "abort" , ( ) => { this . _unregisterChannel ( channel ) ; } ) ; } else { debugLog ( "OPCUAServerEndPoint#_registerChannel called when end point is shutdown !" ) ; debugLog ( "  -> channel will be forcefully terminated" ) ; channel . close ( ) ; channel . dispose ( ) ; } } method param channel private private _unregisterChannel ( channel ) { debugLog ( "_un-registerChannel channel.hashKey" , channel . hashKey ) ; if ( ! Object . prototype . hasOwnProperty . call ( this . _channels , channel . hashKey ) ) { return ; } assert ( Object . prototype . hasOwnProperty . call ( this . _channels , channel . hashKey ) , "channel is not registered" ) ; event param channel this . emit ( "closeChannel" , channel ) ; this . bytesWrittenInOldChannels += channel . bytesWritten ; this . bytesReadInOldChannels += channel . bytesRead ; this . transactionsCountOldChannels += channel . transactionsCount ; delete this . _channels [ channel . hashKey ] ; if ( doDebug ) { this . _dump_statistics ( ) ; debugLog ( "un-registering channel  - Count = " , this . currentChannelCount ) ; } } private _end_listen ( err ? ) { assert ( typeof this . _listen_callback === "function" ) ; this . _listen_callback ! ( err ) ; this . _listen_callback = undefined ; } param channel param inner_callback private shutdown_channel ( channel , inner_callback ) { assert ( typeof inner_callback === "function" ) ; channel . once ( "close" , ( ) => { } ) ; channel . close ( ( ) => { this . _unregisterChannel ( channel ) ; setImmediate ( inner_callback ) ; } ) ; } private private _prevent_DDOS_Attack ( establish_connection , deny_connection ) { const nbConnections = this . activeChannelCount ; if ( nbConnections >= this . maxConnections ) { errorLog ( chalk . bgRed . white ( "PREVENTING DDOS ATTACK => maxConnection =" + this . maxConnections ) ) ; const unused_channels = this . getChannels ( ) . filter ( ( channel1 ) => { return ! channel1 . isOpened && ! channel1 . hasSession ; } ) ; if ( unused_channels . length === 0 ) { errorLog ( "All channels are in used ! let cancel some" ) ; if ( doDebug ) { console . log ( "  - all channels are used !!!!" ) ; dumpChannelInfo ( this . getChannels ( ) ) ; } setTimeout ( deny_connection , 10 ) ; return ; } if ( doDebug ) { console . log ( "   - Unused channels that can be clobbered" , unused_channels . map ( ( channel1 ) => channel1 . hashKey ) . join ( " " ) ) ; } const channel = unused_channels [ 0 ] ; errorLog ( "Closing channel " , channel . hashKey ) ; channel . close ( ( ) => { if ( doDebug ) { console . log ( "   _ Unused channel has been closed " , channel . hashKey ) ; } this . _unregisterChannel ( channel ) ; establish_connection ( ) ; } ) ; } else { setImmediate ( establish_connection ) ; } } } interface MakeEndpointDescriptionOptions { /**
     * port number s
     */ port : number ; default hostname : string ; /**
     *
     */ endpointUrl : string ; serverCertificateChain : Certificate ; /**
     *
     */ securityMode : MessageSecurityMode ; /**
     *
     */ securityPolicy : SecurityPolicy ; securityLevel ? : number ; server : ApplicationDescription ; resourcePath ? : string ; allowAnonymous ? : boolean ; allowUnsecurePassword ? : boolean ; /**
     * onlyCertificateLessConnection
     */ onlyCertificateLessConnection ? : boolean ; restricted : boolean ; collection : { [ key ] : number ; } ; securityPolicies : SecurityPolicy [ ] ; } interface EndpointDescriptionEx extends EndpointDescription { restricted : boolean ; } function estimateSecurityLevel ( securityMode , securityPolicy ) { if ( securityMode === MessageSecurityMode . None ) { return 1 ; } let offset = 100 ; if ( securityMode === MessageSecurityMode . SignAndEncrypt ) { offset = 200 ; } switch ( securityPolicy ) { case SecurityPolicy . Basic128 : case SecurityPolicy . Basic128Rsa15 : case SecurityPolicy . Basic192 : return 2 ; case SecurityPolicy . Basic192Rsa15 : return 3 ; case SecurityPolicy . Basic256 : return 4 ; case SecurityPolicy . Basic256Rsa15 : return 4 + offset ; case SecurityPolicy . Aes128_Sha256_RsaOaep : return 5 + offset ; case SecurityPolicy . Basic256Sha256 : return 6 + offset ; case SecurityPolicy . Aes256_Sha256_RsaPss : return 7 + offset ; default : case SecurityPolicy . None : return 1 ; } } private function _makeEndpointDescription ( options ) { assert ( isFinite ( options . port ) , "expecting a valid port number" ) ; assert ( Object . prototype . hasOwnProperty . call ( options , "serverCertificateChain" ) ) ; assert ( ! Object . prototype . hasOwnProperty . call ( options , "serverCertificate" ) ) ; assert ( ! ! options . securityMode ) ; assert ( ! ! options . securityPolicy ) ; assert ( options . server !== null && typeof options . server === "object" ) ; assert ( ! ! options . hostname && typeof options . hostname === "string" ) ; assert ( typeof options . restricted === "boolean" ) ; const u = ( n ) => getUniqueName ( n , options . collection ) ; options . securityLevel = options . securityLevel === undefined ? estimateSecurityLevel ( options . securityMode , options . securityPolicy ) : options . securityLevel ; assert ( isFinite ( options . securityLevel ) , "expecting a valid securityLevel" ) ; const securityPolicyUri = toURI ( options . securityPolicy ) ; const userIdentityTokens = [ ] ; if ( options . securityPolicy === SecurityPolicy . None ) { if ( options . allowUnsecurePassword ) { userIdentityTokens . push ( { policyId : u ( "username_unsecure" ) , tokenType : UserTokenType . UserName , issuedTokenType : null , issuerEndpointUrl : null , securityPolicyUri : null } ) ; } const a = ( tokenType , securityPolicy , name ) => { if ( options . securityPolicies . indexOf ( securityPolicy ) >= 0 ) { userIdentityTokens . push ( { policyId : u ( name ) , tokenType , issuedTokenType : null , issuerEndpointUrl : null , securityPolicyUri : securityPolicy } ) ; } } ; const onlyCertificateLessConnection = options . onlyCertificateLessConnection === undefined ? false : options . onlyCertificateLessConnection ; if ( ! onlyCertificateLessConnection ) { a ( UserTokenType . UserName , SecurityPolicy . Basic256 , "username_basic256" ) ; a ( UserTokenType . UserName , SecurityPolicy . Basic128Rsa15 , "username_basic128Rsa15" ) ; a ( UserTokenType . UserName , SecurityPolicy . Basic256Sha256 , "username_basic256Sha256" ) ; a ( UserTokenType . UserName , SecurityPolicy . Aes128_Sha256_RsaOaep , "username_aes128Sha256RsaOaep" ) ; a ( UserTokenType . Certificate , SecurityPolicy . Basic256 , "certificate_basic256" ) ; a ( UserTokenType . Certificate , SecurityPolicy . Basic128Rsa15 , "certificate_basic128Rsa15" ) ; a ( UserTokenType . Certificate , SecurityPolicy . Basic256Sha256 , "certificate_basic256Sha256" ) ; a ( UserTokenType . Certificate , SecurityPolicy . Aes128_Sha256_RsaOaep , "certificate_aes128Sha256RsaOaep" ) ; } } else { userIdentityTokens . push ( { policyId : u ( "usernamePassword" ) , tokenType : UserTokenType . UserName , issuedTokenType : null , issuerEndpointUrl : null , securityPolicyUri : null } ) ; userIdentityTokens . push ( { policyId : u ( "certificateX509" ) , tokenType : UserTokenType . Certificate , issuedTokenType : null , issuerEndpointUrl : null , securityPolicyUri : null } ) ; } if ( options . allowAnonymous ) { userIdentityTokens . push ( { policyId : u ( "anonymous" ) , tokenType : UserTokenType . Anonymous , issuedTokenType : null , issuerEndpointUrl : null , securityPolicyUri : null } ) ; } const endpoint = new EndpointDescription ( { endpointUrl : options . endpointUrl , server : undefined , serverCertificate : options . serverCertificateChain , securityMode : options . securityMode , securityPolicyUri , userIdentityTokens , securityLevel : options . securityLevel , transportProfileUri : default_transportProfileUri } ) as EndpointDescriptionEx ; ( endpoint as any ) . __defineGetter__ ( "endpointUrl" , ( ) => { return resolveFullyQualifiedDomainName ( options . endpointUrl ) ; } ) ; endpoint . server = options . server ; endpoint . restricted = options . restricted ; return endpoint ; } param endpoint param securityMode param securityPolicy internal function matching_endpoint ( securityMode , securityPolicy , endpointUrl , endpoint ) { assert ( endpoint instanceof EndpointDescription ) ; const endpoint_securityPolicy = fromURI ( endpoint . securityPolicyUri ) ; if ( endpointUrl && endpoint . endpointUrl ! !== endpointUrl ) { return false ; } return endpoint . securityMode === securityMode && endpoint_securityPolicy === securityPolicy ; } const defaultSecurityModes = [ MessageSecurityMode . None , MessageSecurityMode . Sign , MessageSecurityMode . SignAndEncrypt ] ; const defaultSecurityPolicies = [ SecurityPolicy . Basic128Rsa15 , SecurityPolicy . Basic256 , SecurityPolicy . Basic256Sha256 , SecurityPolicy . Aes128_Sha256_RsaOaep ] ;