CVE-2022-21208 (TypeScript, published 2022-08-23T05:15Z)
Description: value: The package node-opcua before 2.74.0 are vulnerable to Denial of Service (DoS) due to a missing limitation on the number of received chunks - per single session or in total for all concurrent sessions. An attacker can exploit this vulnerability by sending an unlimited number of huge chunks (e.g. 2GB each) without sending the Final closing chunk.
Severity: nan (CVSS v3 base score: 7.5)
Weakness: CWE-1284 \u2014 Improper Validation of Specified Quantity in Input
The product receives input that is expected to specify a quantity (such as size or length), but it does not validate or incorrectly validates that the quantity has the required properties.
Repository: https://github.com/node-opcua/node-opcua
Files changed:
packages/node-opcua-client/source/client_base.ts
packages/node-opcua-client/source/private/client_base_impl.ts
packages/node-opcua-client/test/test_x509_signature.ts
packages/node-opcua-end2end-test/test/end_to_end/test_e2e_issue896_execute_method_access_control.ts
packages/node-opcua-end2end-test/test/end_to_end/u_test_e2e_SubscriptionDiagnostics.js
packages/node-opcua-end2end-test/test/end_to_end/u_test_e2e_call_service.js
packages/node-opcua-end2end-test/test/end_to_end/u_test_e2e_issue445_currentSessionCount.js
packages/node-opcua-end2end-test/test/end_to_end/u_test_e2e_monitoring_larger_number2.ts
packages/node-opcua-packet-assembler/source/packet_assembler.ts
packages/node-opcua-samples/bin/opcua_interceptor.js
packages/node-opcua-secure-channel/source/client/client_secure_channel_layer.ts
packages/node-opcua-secure-channel/source/message_builder.ts
packages/node-opcua-secure-channel/source/message_chunker.ts
packages/node-opcua-secure-channel/source/server/server_secure_channel_layer.ts
packages/node-opcua-secure-channel/test/test_decrypt_unsign.js
packages/node-opcua-secure-channel/test/test_message_builder.js
packages/node-opcua-secure-channel/test/test_message_builder_with_sign_chunks.js
packages/node-opcua-secure-channel/test/test_secure_channel_layer.js
packages/node-opcua-secure-channel/test/test_secure_channel_service.js
packages/node-opcua-secure-channel/test/test_security.ts
packages/node-opcua-secure-channel/test/test_server_secure_channel_layer.js
packages/node-opcua-secure-channel/test/test_various_packets.js
packages/node-opcua-secure-channel/test_fixtures/fixture_problematic_ReadResponse.js
packages/node-opcua-secure-channel/test_helpers/fake_message_chunk_factory.ts
packages/node-opcua-secure-channel/test_helpers/verify_message_chunk.ts
packages/node-opcua-server/source/opcua_server.ts
packages/node-opcua-transport/source/client_tcp_transport.ts
packages/node-opcua-transport/source/index.ts
packages/node-opcua-transport/source/message_builder_base.ts
packages/node-opcua-transport/source/server_tcp_transport.ts
packages/node-opcua-transport/source/status_codes.ts
packages/node-opcua-transport/source/tcp_transport.ts
packages/node-opcua-transport/source/utils.ts
packages/node-opcua-transport/test/test_client_tcp_transport.js
packages/node-opcua-transport/test/test_message_builder_base.js
packages/node-opcua-transport/test/test_server_tcp_transport.js
packages/node-opcua-transport/test_helpers/direct_transport.ts
packages/node-opcua-transport/test_helpers/half_com_channel.ts
Commit message: fix message chunk overflow detection


Vulnerable code:
import {
    ClientSecureChannelLayer,
    ConnectionStrategy,
    ConnectionStrategyOptions,
    SecurityPolicy} from "node-opcua-secure-channel";
import {
    FindServersOnNetworkRequestOptions,
    FindServersRequestOptions,
    ServerOnNetwork
} from "node-opcua-service-discovery";
import {
    ApplicationDescription,
    EndpointDescription} from "node-opcua-service-endpoints";
            tokenRenewalInterval: this.tokenRenewalInterval
import { constructObject } from "node-opcua-factory";
    /*
    const offset = 16 * 3 + 6;
    buffer = buffer.slice(offset);
    */
    const messageBuilder = new MessageBuilder({});
    let objMessage: any = null;
import {allPermissions, OPCUAClient, OPCUAServer, StatusCodes, UserTokenType, WellKnownRoles, makeRoles} from "node-opcua";
        {username: "Gandalf", password: "g", roles: makeRoles([WellKnownRoles.AuthenticatedUser,WellKnownRoles.ConfigureAdmin])},
        {username: "Frodo", password: "f", roles:  makeRoles([WellKnownRoles.AuthenticatedUser])},
            getUserRoles: username => users.find(user => user.username === username)!.roles,
            
            isValidUser(username, password) {
                const user = users.find(user => user.username === username);
    })
        endpointMustExist: false,
        namespace.addMethod(folder, {
            browseName: "doIt",
            nodeId: "ns=1;s=doIt",
            rolePermissions: [
                { 
                    roleId:  WellKnownRoles.ConfigureAdmin,
                    permissions: allPermissions
                }
            ]
            /*
        }).bindMethod((inputArguments, context, callback) => {
            wasExecuted = true;
            callback(null, {statusCode: StatusCodes.Good})
        });
    beforeEach(() => wasExecuted = false);
            inputArguments: [],
            inputArguments: [],
});
/*global xit,it,describe,before,after,beforeEach,afterEach*/



const { assert } = require("node-opcua-assert");
                requestedSessionTimeout: 600 * 1000 // use long session time out
            const too_many = 50000;
const async = require("async");
    ClientMonitoredItem,
    StatusCodes,
const { make_debugLog, make_errorLog, checkDebugFlag } = require("node-opcua-debug");
        }, 5000);
    return await new Promise((resolve,reject) => {
        }, 5000);
            await perform_operation_on_subscription_async(client, endpointUrl, async function (session, subscription) {
import sinon = require("sinon");
import should = require("should");
import { clearTimeout } from "timers";
            while (itemToMonitors.length + itemsToMonitor1.length < 10000) {
                    const timerId = setTimeout(() => resolve(), 12000);
    on(eventName: "error", eventHandler: (err: Error) => void): this;
                this.emit("error", new Error("maximum message size exceeded"));
                this.emit("error", new Error(message));
const argv = require("yargs")
    .usage("Usage: $0 --portServer [num] --port [num]  --hostname <hostname> -block")
    .argv;
const chalk = require("chalk");

        var err = new s.TCPErrorMessage();
import { CallbackT, StatusCodes } from "node-opcua-status-code";
    private readonly messageBuilder: MessageBuilder;
            securityMode: this.securityMode
        this._requests = {};
            .on("message", (response: Response, msgType: string, requestId: number) => {
                this._on_message_received(response, msgType, requestId);
            .on("start_chunk", () => {
            .on("error", (err, requestId) => {
                //

        this.__in_normal_close_operation = false;

        this._timeout_request_count = 0;

        this._securityTokenTimeoutId = null;

        this.transportTimeout = options.transportTimeout || ClientSecureChannelLayer.defaultTransportTimeout;

        this.channelId = 0;

        this.connectionStrategy = coerceConnectionStrategy(options.connectionStrategy);

    public closeWithError(err: Error, callback: ErrorCallback): void {
            this._transport.prematureTerminate(err);
            requestData._tick2 = this.messageBuilder._tick1;
        requestData.bytesRead = this.messageBuilder.totalMessageSize;
                const cryptoFactory = this.messageBuilder.cryptoFactory;
                this.messageBuilder.pushNewToken(this.securityToken, derivedServerKeys);
        this._transport.on("message", (messageChunk: Buffer) => {
                this.closeWithError(new Error("Restarting because Request has timed out during OpenSecureChannel"), () => {
                    /* */
                });
        this.messageBuilder.feed(messageChunk);
import { decodeStatusCode } from "node-opcua-status-code";
import { MessageBuilderBase } from "node-opcua-transport";
import { chooseSecurityHeader, SymmetricAlgorithmSecurityHeader } from "./secure_channel_service";
export interface MessageBuilderOptions {
            return this._report_error("internal error");
            // invalid message type
            return this._report_error("ERROR RECEIVED");
            return this._report_error("Invalid message type ( HEL/ACK )");
            return this._report_error(err instanceof Error ? err.message : " err");
            return this._report_error("cannot construct object with nodeID " + id.toString());
            return this._report_error("cannot construct object with nodeID " + id);
import { make_errorLog, make_warningLog} from "node-opcua-debug";
        this.maxMessageSize = options.maxMessageSize || 16 *1024*1024;
                    errorLog(`[NODE-OPCUA-E55] message size ${totalSize} exceeds the negotiated message size ${this.maxMessageSize} nb chunks ${nbChunks}`);
    exploreCertificate
import { ServerTCP_transport } from "node-opcua-transport";
    public readonly messageBuilder: MessageBuilder;
        this.messageBuilder = new MessageBuilder({
            name: "server",
            objectFactory: options.objectFactory,
            privateKey: this.getPrivateKey()
        });

        this.messageBuilder.on("error", (err) => {
            // istanbul ignore next
            if (doDebug) {
                debugLog(chalk.red("xxxxx error "), err.message.yellow, err.stack);
                debugLog(chalk.red("xxxxx Server is now closing socket, without further notice"));
            }
            // close socket immediately
            this.close(undefined);
        });

        assert(this.messageBuilder, "dispose already called ?");
            // xx this.messageBuilder = null;
            (this as any).transport = null;
        this.messageBuilder.setSecurity(securityMode, securityPolicy);
                this.transport.on("message", (messageChunk: Buffer) => {
                    assert(this.messageBuilder);
                    this.messageBuilder.feed(messageChunk);
    public send_fatal_error_and_abort(
        statusCode: StatusCode,
        description: string,
        message: Message,
        callback: ErrorCallback
    ): void {
        /* istanbul ignore next */
        if (!(this.messageBuilder && this.messageBuilder.sequenceHeader && this.messageBuilder.securityHeader)) {
            return this._on_OpenSecureChannelRequestError(StatusCodes.BadCommunicationError, "internal error", message, callback);
        }

                this._on_common_message(request, msgType, requestId, channelId);
            .on("start_chunk", () => {
            this.messageBuilder.removeListener("message", messageHandler);
            this.messageBuilder.removeListener("error", errorHandler);
        this.messageBuilder.once("error", errorHandler);
        this.messageBuilder.once("message", messageHandler);
            const cryptoFactory = this.messageBuilder.cryptoFactory;
        const cryptoFactory = this.messageBuilder.cryptoFactory;
            return this.send_fatal_error_and_abort(StatusCodes.BadInternalError, "invalid request", message, callback);
        const cryptoFactory = this.messageBuilder.cryptoFactory;
        this.messageBuilder.pushNewToken(this.securityToken, derivedClientKeys);
        if (this.messageBuilder.sequenceHeader === null) {
        requestId = this.messageBuilder.sequenceHeader.requestId;
                    return this.send_fatal_error_and_abort(
        this.send_fatal_error_and_abort(serviceResult, description, message, callback);
    it("should decrypt an OPN packet and verify that the signature is correct", function() {
const { MessageBuilder } = require("..");
const { SecurityPolicy, MessageSecurityMode} = require("..");
            function () {}
            .on("message", (message) => {})
            .on("message", (message) => {})
const crypto_utils = require("node-opcua-crypto");
const fake_message_chunk_factory = require("../dist/test_helpers/fake_message_chunk_factory");
        const options = {};
        const messageBuilder = new MessageBuilder(options);
        messageBuilder.privateKey = crypto_utils.readPrivateRsaKey(private_key_filename);
            .on("message", (message) => {/** */})
        fake_message_chunk_factory.iterateOnSignedMessageChunks(someBuffer, function (err, chunk) {
        fake_message_chunk_factory.iterateOnSignedMessageChunks(someBuffer, function (err, chunk) {
        fake_message_chunk_factory.iterateOnSignedMessageChunks(someBuffer, function (err, chunk) {
        messageBuilder.privateKey = crypto_utils.readPrivateRsaKey(private_key_filename);
            .on("message", (message) => {/** */})
        fake_message_chunk_factory.iterateOnSignedAndEncryptedMessageChunks(lorem_ipsum_buffer, function (err, chunk) {
        const options = {};
        const messageBuilder = new MessageBuilder(options);
        messageBuilder.privateKey = crypto_utils.readPrivateRsaKey(private_key_filename);
        messageBuilder.pushNewToken({ tokenId: 10 }, fake_message_chunk_factory.derivedKeys);
            .on("message", (message) => {/** */})
        fake_message_chunk_factory.iterateOnSymmetricEncryptedChunk(lorem_ipsum_buffer, function (err, chunk) {
        serverChannel.init(socket, function () {});
                secureChannel.abortConnection(function () {});
        await closeChannel().should.be.rejectedWith(/Transport disconnected/);
            await promisify(secureChannel.closeWithError).call(secureChannel, new Error("Sabotage"));
                        .on("message", function(reconstructed_message) {
            .on("message", function(message) {
           
                
                doDebug &&  console.log("client received a response ", response?.constructor.name);
                doDebug &&  console.log(response?.toString());
    it("client & server channel  - with security ", (done) => {

const { ServerSecureChannelLayer, MessageSecurityMode, SecurityPolicy } = require("..");
        serverSecureChannel.on("message", function (message) {
        serverSecureChannel.on("message", function (message) {
            serverSecureChannel.send_response("MSG", new GetEndpointsResponse(), message, () => {});
        let isDone = false;
            isDone = true;
const MessageBuilder = require("..").MessageBuilder;
xdescribe("testing with problematic packet", function () {
        messageBuilder.setSecurity(MessageSecurityMode.None,SecurityPolicy.None);
        on("message", function (message) {
            on_message__received = true;
            message.schema.name.should.equal("GetEndpointsResponse");
            on_message__received.should.equal(true);
            full_message_body_event_received.should.equal(true);
            done();
        }).
        on("full_message_body", function (full_message_body) {
            full_message_body_event_received = true;
        }).
        on("error", function (err) {
            should.not.exist(err);
            throw new Error("should not get there");
        });
        on("message", function (message) {
            messageCount += 1;
            if (messageCount === 2) {
                done();
            }
        }).
        on("error", function (err) {
            console.log(err);
            throw new Error("should not get there");
        }).
        on("invalid_sequence_number", function (expected, found) {
            throw new Error("should not received a invalid_sequence_number here");
        });
        //
        const services = require("../src/services");

        const ec = require("node-opcua-basic-types");
        const full_message_body = require("../test_fixtures/fixture_problematic_ReadResponse.js").packet_ReadResponse;
        const id = ec.decodeExpandedNodeId(binaryStream);
        this.objectFactory = require("node-opcua-factory");
        // construct the object
        const objMessage = this.objectFactory.constructObject(id);
        if (!objMessage) {
            console.log(services);
            console.log("cannot construct object with id" + id.toString());
        }
        objMessage.constructor.name.should.eql("ReadResponse");
        const analyseExtensionObject = require("node-opcua-packet-analyzer").analyseExtensionObject;
const { makebuffer_from_trace  } = require("node-opcua-debug");
exports.packet_ReadResponse= makebuffer_from_trace(function () {
export function iterateOnSymmetricEncryptedChunk(buffer: Buffer, callback: ChunkVisitorFunc) {
    msgChunkManager.on("chunk", (chunk, final) => callback(null, chunk));
    const messageBuilder = new MessageBuilder({});
    messageBuilder.on("start_chunk", (info) => {
import { bindRoleSet} from "./user_manager_ua";

        return computeSignature(clientCertificate, clientNonce, this.getPrivateKey(), channel.messageBuilder.securityPolicy);
        const securityPolicy = channel.messageBuilder.securityPolicy;
        assert(typeof callback === 'function');
        assert(typeof callback === 'function');
    on(eventName: "message", eventHandler: (message: Buffer) => void): this;
    once(eventName: "message", eventHandler: (message: Buffer) => void): this;
 *    transport.on("message",function(message_chunk) {
 *        // do something with message from server...
            maxChunkCount: 0, // 0 - no limits
            maxMessageSize: 0, // 0 - no limits

            receiveBufferSize: 1024 * 64 * 10,
            sendBufferSize: 1024 * 64 * 10 // 8192 min,
export * from "./HelloMessage";
export * from "./TCPErrorMessage";
export * from "./message_builder_base";
import { make_errorLog, make_debugLog } from "node-opcua-debug";
    public readonly signatureLength: number;    
    public readonly options: { signatureLength?: number };
    constructor(options?: { signatureLength?: number }) {
        options = options || {};
            minimumSizeInBytes: 0,
            readMessageFunc: readRawMessageHeader
        this._packetAssembler.on("message", (messageChunk) => this._feed_messageChunk(messageChunk));
        this._packetAssembler.on("newMessage", (info, data) => {
            this.emit("start_chunk", info, data);
                return this._report_error("Invalid secure channel Id");
            return false;
    protected _report_error(errorMessage: string): false {
        this.emit("error", new Error(errorMessage), this.sequenceHeader ? this.sequenceHeader.requestId : null);
            return this._report_error(`Invalid message header detected`);
            return this._report_error("received and Abort Message");
    public receiveBufferSize: number;
    public sendBufferSize: number;
    public maxMessageSize: number;
    public maxChunkCount: number;
    public protocolVersion: number;

        this.receiveBufferSize = 0;
        this.sendBufferSize = 0;
        this.maxMessageSize = 0;
        this.maxChunkCount = 0;
        this.protocolVersion = 0;
        if (debugLog) {
            debugLog(chalk.cyan("_abortWithError"));
        assert(typeof callback === "function", "expecting a callback");
        /* istanbul ignore else */
        if (!this._aborted) {
            this._aborted = 1;
            setTimeout(() => {
                // send the error message and close the connection
                assert(Object.prototype.hasOwnProperty.call(StatusCodes, statusCode.name));
                /* istanbul ignore next*/
                if (doDebug) {
                    debugLog(chalk.red(" Server aborting because ") + chalk.cyan(statusCode.name));
                    debugLog(chalk.red(" extraErrorDescription   ") + chalk.cyan(extraErrorDescription));
                }
                const errorResponse = new TCPErrorMessage({
                    reason: statusCode.description,
                    statusCode
                });
                const messageChunk = packTcpMessage("ERR", errorResponse);
                this.write(messageChunk);
                this.disconnect(() => {
                    this._aborted = 2;
                    callback(new Error(extraErrorDescription + " StatusCode = " + statusCode.name));
                });
            }, ServerTCP_transport.throttleTime);
        } else {
            callback(new Error(statusCode.name));
        }
    }
    private _send_ACK_response(helloMessage: HelloMessage): void {
        assert(helloMessage.receiveBufferSize >= minimumBufferSize);
        assert(helloMessage.sendBufferSize >= minimumBufferSize);
        this.receiveBufferSize = clamp_value(helloMessage.receiveBufferSize, 8192, 512 * 1024);
        this.sendBufferSize = clamp_value(helloMessage.sendBufferSize, 8192, 512 * 1024);
        this.maxMessageSize = clamp_value(helloMessage.maxMessageSize, 100000, 64 * 1024 * 1024);
        this.maxChunkCount = clamp_value(helloMessage.maxChunkCount, 0, 65535);

import { createFastUninitializedBuffer } from "node-opcua-buffer-utils";
import * as debug from "node-opcua-debug";
import { PacketAssembler } from "node-opcua-packet-assembler";
import { ErrorCallback, CallbackWithData } from "node-opcua-status-code";
import { writeTCPMessageHeader } from "./tools";
const debugLog = debug.make_debugLog(__filename);
const doDebug = debug.checkDebugFlag(__filename);
const errorLog = debug.make_errorLog(__filename);
    
    on(eventName: "message", eventHandler: (message: Buffer) => void): this;
    once(eventName: "message", eventHandler: (message: Buffer) => void): this;
    private _pendingBuffer?: any;
        this._pendingBuffer = undefined;
    /**
     * ```createChunk``` is used to construct a pre-allocated chunk to store up to ```length``` bytes of data.
     * The created chunk includes a prepended header for ```chunk_type``` of size ```self.headerSize```.
     *
     * @method createChunk
     * @param msgType
     * @param chunkType {String} chunk type. should be 'F' 'C' or 'A'
     * @param length
     * @return a buffer object with the required length representing the chunk.
     *
     * Note:
     *  - only one chunk can be created at a time.
     *  - a created chunk should be committed using the ```write``` method before an other one is created.
     */
    public createChunk(msgType: string, chunkType: string, length: number): Buffer {
        assert(msgType === "MSG");
        assert(this._pendingBuffer === undefined, "createChunk has already been called ( use write first)");

        const totalLength = length + this.headerSize;
        const buffer = createFastUninitializedBuffer(totalLength);
        writeTCPMessageHeader("MSG", chunkType, totalLength, buffer);
        this._pendingBuffer = buffer;

        return buffer;
    }

     *
     * Notes:
     *  - the message chunk must have been created by ```createChunk```.
     *  - once a message chunk has been written, it is possible to call ```createChunk``` again.
     *
    public write(messageChunk: Buffer): void {
        assert(
            this._pendingBuffer === undefined || this._pendingBuffer === messageChunk,
            " write should be used with buffer created by createChunk"
        );
        assert(["F", "C", "A"].indexOf(header.messageHeader.isFinal) !== -1);
        this._write_chunk(messageChunk);
        this._pendingBuffer = undefined;
    protected _write_chunk(messageChunk: Buffer): void {
            this._socket.write(messageChunk);
        // install packet assembler ...
        this.packetAssembler = new PacketAssembler({
            readMessageFunc: readRawMessageHeader,

            minimumSizeInBytes: this.headerSize
        });

        /* istanbul ignore next */
        if (!this.packetAssembler) {
            throw new Error("Internal Error");
        }
        this.packetAssembler.on("message", (messageChunk: Buffer) => this._on_message_received(messageChunk));
            this.prematureTerminate(new Error("socket timeout : timeout=" + this.timeout));
    public prematureTerminate(err: Error): void {
        debugLog("prematureTerminate", err ? err.message : "");
            err.message = "socket has timeout: EPIPE: " + err.message;
    private _on_message_received(messageChunk: Buffer) {
            this.emit("message", messageChunk);
export const doTraceHelloAck = transportFlag.match(/HELACK/);
export const doTraceChunk = transportFlag.match(/CHUNK/);
const { assert } = require("node-opcua-assert");
const { StatusCodes, StatusCode } = require("node-opcua-status-code");


const { make_debugLog, make_errorLog } = require("node-opcua-debug");
        transport.on("message", function (message_chunk) {
        transport.connect(endpointUrl, function (err) {
            const buf = transport.createChunk("MSG", "F", message1.length);
        builder.on("chunk", function (message_chunk) {
        builder.on("chunk", function (message_chunk) {
        transport.on("message", (messageChunk) => {
        transport.on("message", (messageChunk) => {
        transport.on("message", (messageChunk) => {
        this.server.on("end", (err: Error) => {

Fixed code:
import { ClientSecureChannelLayer, ConnectionStrategy, ConnectionStrategyOptions, SecurityPolicy } from "node-opcua-secure-channel";
import { FindServersOnNetworkRequestOptions, FindServersRequestOptions, ServerOnNetwork } from "node-opcua-service-discovery";
import { ApplicationDescription, EndpointDescription } from "node-opcua-service-endpoints";
export interface TransportSettings {
    maxChunkCount?: number;
    /**
     * @advanced
     */
    maxMessageSize?: number;
    /**
     * @advanced
     */
    sendBufferSize?: number;
    /**
     * @advanced
     */
    receiveBufferSize?: number;
}

    /**
     * @advanced
     */
    transportSetting?: TransportSettings
            tokenRenewalInterval: this.tokenRenewalInterval,
            
            // transportTimeout:

import { BaseUAObject, constructObject } from "node-opcua-factory";
    const messageBuilder = new MessageBuilder({
        maxChunkCount: 1,
        maxChunkSize: buffer.length + 100,
        maxMessageSize: buffer.length + 100
    });

    let objMessage: BaseUAObject | null = null;
import { allPermissions, OPCUAClient, OPCUAServer, StatusCodes, UserTokenType, WellKnownRoles, makeRoles } from "node-opcua";
        { username: "Gandalf", password: "g", roles: makeRoles([WellKnownRoles.AuthenticatedUser, WellKnownRoles.ConfigureAdmin]) },
        { username: "Frodo", password: "f", roles: makeRoles([WellKnownRoles.AuthenticatedUser]) }
            getUserRoles: (username) => users.find((user) => user.username === username)!.roles,

            isValidUser: (username: string, password: string) => {
                const user = users.find((user) => user.username === username);
    });
        endpointMustExist: false
        namespace
            .addMethod(folder, {
                browseName: "doIt",
                nodeId: "ns=1;s=doIt",
                rolePermissions: [
                    {
                        roleId: WellKnownRoles.ConfigureAdmin,
                        permissions: allPermissions
                    }
                ]
                /*
            })
            .bindMethod((inputArguments, context, callback) => {
                wasExecuted = true;
                callback(null, { statusCode: StatusCodes.Good });
            });
    beforeEach(() => (wasExecuted = false));
            inputArguments: []
            inputArguments: []
});
                requestedSessionTimeout: 600 * 1000, // use long session time out
            const too_many = 5000;
const { make_debugLog, checkDebugFlag } = require("node-opcua-debug");
const { pause } = require("../discovery/_helper");

    await pause(100);

    await pause(100);
            console.log(monitoredItem);
        }, 15000);
    return await new Promise((resolve, reject) => {
            console.log(currentSessionCountMonitoredItem);
        }, 15000);

            await perform_operation_on_subscription_async(client, endpointUrl, async (session, subscription) => {

import { clearTimeout } from "timers";
import * as should from "should";
            session.on("session_closed", ()=>{ 
                console.log("session_closed");  
            });

            while (itemToMonitors.length + itemsToMonitor1.length < 2200) {


                    const timerId = setTimeout(() => resolve(), 5000);
                    
export enum PacketAssemblerErrorCode {
    ChunkSizeExceeded = 1,
    ChunkTooSmall = 2
}
    on(eventName: "error", eventHandler: (err: Error, errCode: PacketAssemblerErrorCode) => void): this;
                this.emit("error", new Error("chunk is too small "), PacketAssemblerErrorCode.ChunkTooSmall);
                this.emit("error", new Error(message), PacketAssemblerErrorCode.ChunkSizeExceeded);
const chalk = require("chalk");
const argv = require("yargs")
    .usage("Usage: $0 --portServer [num] --port [num]  --hostname <hostname> -block")
    .argv;
        const err = new s.TCPErrorMessage();
            /** */
import { CallbackT, StatusCode, StatusCodes } from "node-opcua-status-code";
import { StatusCodes2 } from "node-opcua-transport";

    private messageBuilder?: MessageBuilder;
        this._requests = {};

        this.__in_normal_close_operation = false;

        this._timeout_request_count = 0;

        this._securityTokenTimeoutId = null;

        this.transportTimeout = options.transportTimeout || ClientSecureChannelLayer.defaultTransportTimeout;

        this.channelId = 0;

        this.connectionStrategy = coerceConnectionStrategy(options.connectionStrategy);
    }

    private _install_message_builder() {
        // istanbul ignore next
        if (!this._transport || !this._transport.parameters) {
            throw new Error("internal error");
        }
            securityMode: this.securityMode,
            maxChunkSize: this._transport.receiveBufferSize || 0,
            maxChunkCount: this._transport.maxChunkCount || 0,
            maxMessageSize: this._transport.maxMessageSize || 0
            .on("message", (response: BaseUAObject, msgType: string, requestId: number, channelId: number) => {
                this._on_message_received(response as Response, msgType, requestId);
            .on("startChunk", () => {
            .on("error", (err: Error, statusCode: StatusCode, requestId: number | null) => {
                if (!requestId) {
                    return;
                }

    public closeWithError(err: Error, statusCode: StatusCode, callback: ErrorCallback): void {
            this._transport.prematureTerminate(err, statusCode);
            requestData._tick2 = this.messageBuilder!._tick1;
        requestData.bytesRead = this.messageBuilder!.totalMessageSize;
                const cryptoFactory = this.messageBuilder!.cryptoFactory;
                this.messageBuilder!.pushNewToken(this.securityToken, derivedServerKeys);
        this._install_message_builder();

        this._transport.on("chunk", (messageChunk: Buffer) => {
                this.closeWithError(
                    new Error("Restarting because Request has timed out during OpenSecureChannel"),
                    StatusCodes2.BadRequestTimeout,
                    () => {
                        /* */
                    }
                );
        this.messageBuilder!.feed(messageChunk);
import { decodeStatusCode, coerceStatusCode, StatusCodes, StatusCode } from "node-opcua-status-code";
import { MessageBuilderBase, MessageBuilderBaseOptions, StatusCodes2 } from "node-opcua-transport";
import { chooseSecurityHeader, MessageChunker, SymmetricAlgorithmSecurityHeader } from "./secure_channel_service";
export interface MessageBuilderOptions extends MessageBuilderBaseOptions {
type PacketInfo = any;

export interface MessageBuilder extends MessageBuilderBase {
    on(eventName: "startChunk", eventHandler: (info: PacketInfo, data: Buffer) => void): this;
    on(eventName: "chunk", eventHandler: (chunk: Buffer) => void): this;
    on(eventName: "error", eventHandler: (err: Error, statusCode: StatusCode, requestId: number | null) => void): this;
    on(eventName: "full_message_body", eventHandler: (fullMessageBody: Buffer) => void): this;
    on(
        eventName: "message",
        eventHandler: (obj: BaseUAObject, msgType: string, requestId: number, channelId: number) => void
    ): this;

    on(eventName: "invalid_message", eventHandler: (obj: BaseUAObject) => void): this;
    on(eventName: "invalid_sequence_number", eventHandler: (expectedSequenceNumber: number, sequenceNumber: number) => void): this;
    on(eventName: "new_token", eventHandler: (tokenId: number) => void): this;

    emit(eventName: "startChunk", info: PacketInfo, data: Buffer): boolean;
    emit(eventName: "chunk", chunk: Buffer): boolean;
    emit(eventName: "error", err: Error, statusCode: StatusCode, requestId: number | null): boolean;
    emit(eventName: "full_message_body", fullMessageBody: Buffer): boolean;
    emit(eventName: "message", obj: BaseUAObject, msgType: string, requestId: number, channelId: number): boolean;
    emit(eventName: "invalid_message", evobj: BaseUAObject): boolean;
    emit(eventName: "invalid_sequence_number", expectedSequenceNumber: number, sequenceNumber: number): boolean;
    emit(eventName: "new_token", tokenId: number): boolean;
}
    protected _reportErrMessage(message: Buffer) {
        try {
            const binaryStream = new BinaryStream(message);
            const msgType = binaryStream.readUInt32();
            const msgLength = binaryStream.readUInt32();
            if (message.length === msgLength) {
                const errorCode = binaryStream.readUInt32();
                const additionalInfo = decodeString(binaryStream);
                // invalid message type
                const m1 = `ERR: ${errorCode} ${additionalInfo}`;
                warningLog(m1);
                return this._report_error(coerceStatusCode(errorCode), m1);
            } else {
                return this._report_error(StatusCodes.BadTcpInternalError, message.toString("hex"));
            }
        } catch (err) {
            console.log(hexDump(message));
            return this._report_error(StatusCodes.BadTcpInternalError, message.toString("hex"));
        }
    }

            return this._report_error(StatusCodes2.BadTcpInternalError, "internal error");
            return this._reportErrMessage(fullMessageBody);
            return this._report_error(StatusCodes2.BadTcpMessageTypeInvalid, "Invalid message type ( HEL/ACK )");
            return this._report_error(StatusCodes2.BadTcpInternalError, err instanceof Error ? err.message : " err");
            return this._report_error(StatusCodes.BadNotSupported, "cannot construct object with nodeID " + id.toString());
            return this._report_error(StatusCodes.BadNotSupported, "cannot construct object with nodeID " + id);
import { make_errorLog, make_warningLog } from "node-opcua-debug";
    public static defaultMaxMessageSize: number = 16 * 1024 * 1024;

        this.maxMessageSize = options.maxMessageSize || MessageChunker.defaultMaxMessageSize;
                    errorLog(
                        `[NODE-OPCUA-E55] message size ${totalSize} exceeds the negotiated message size ${this.maxMessageSize} nb chunks ${nbChunks}`
                    );
/* eslint-disable max-statements */
    exploreCertificate,
    hexDump
import { ServerTCP_transport, StatusCodes2 } from "node-opcua-transport";
import { doTraceIncomingChunk } from "node-opcua-transport";


    public messageBuilder?: MessageBuilder;


        this.objectFactory = options.objectFactory;

    private _build_message_builder() {
        this.messageBuilder = new MessageBuilder({
            name: "server",
            objectFactory: this.objectFactory,
            privateKey: this.getPrivateKey(),
            maxChunkSize: this.transport.receiveBufferSize,
            maxChunkCount: this.transport.maxChunkCount,
            maxMessageSize: this.transport.maxMessageSize
        });
        debugLog(" this.transport.maxChunkCount", this.transport.maxChunkCount);
        debugLog(" this.transport.maxMessageSize", this.transport.maxMessageSize);

        this.messageBuilder.on("error", (err, statusCode) => {

            warningLog("ServerSecureChannel:MessageBuilder: ", err.message);
            
            // istanbul ignore next
            if (doDebug) {
                debugLog(chalk.red("Error "), err.message, err.stack);
                debugLog(chalk.red("Server is now closing socket, without further notice"));
            }

            this.transport.sendErrorMessage(statusCode, err.message);

            // close socket immediately
            this.close(undefined);
        });
    }

            this.messageBuilder = undefined;
            (this as any).transport = undefined;
        if (!this.messageBuilder) {
            this._build_message_builder();
        }
        assert(this.messageBuilder);
        this.messageBuilder!.setSecurity(securityMode, securityPolicy);
                this._build_message_builder();

                this.transport.on("chunk", (messageChunk: Buffer) => {
                    // istanbul ignore next
                    if (doTraceIncomingChunk) {
                        console.log(hexDump(messageChunk));
                    }
                    this.messageBuilder!.feed(messageChunk);
    private _sendFatalErrorAndAbort(statusCode: StatusCode, description: string, message: Message, callback: ErrorCallback): void {

        /* istanbul ignore next */
        if (!(this.messageBuilder && this.messageBuilder.sequenceHeader && this.messageBuilder.securityHeader)) {
            return this._on_OpenSecureChannelRequestError(
                StatusCodes.BadCommunicationError,
                "internal error",
                { request, requestId },
                callback
            );
        }

                this._on_common_message(request as Request, msgType, requestId, channelId);
            .on("error", (err: Error, statusCode: StatusCode, requestId: number | null) => {
                /** */
                this.transport.sendErrorMessage(statusCode, err.message);
                this.close();
            })
            .on("startChunk", () => {
            this.messageBuilder!.removeListener("message", messageHandler);
            this.messageBuilder!.removeListener("error", errorHandler);
        this.messageBuilder!.once("error", errorHandler);
        this.messageBuilder!.once("message", messageHandler);
            const cryptoFactory = this.messageBuilder!.cryptoFactory;
        const cryptoFactory = this.messageBuilder!.cryptoFactory;
            return this._sendFatalErrorAndAbort(StatusCodes2.BadSecurityChecksFailed, "invalid request", message, callback);
        const cryptoFactory = this.messageBuilder!.cryptoFactory;
        this.messageBuilder!.pushNewToken(this.securityToken, derivedClientKeys);
        if (this.messageBuilder!.sequenceHeader === null) {
        requestId = this.messageBuilder!.sequenceHeader.requestId;
                    return this._sendFatalErrorAndAbort(
        this._sendFatalErrorAndAbort(serviceResult, description, message, callback);
    xit("should decrypt an OPN packet and verify that the signature is correct", function() {
        // 1496

const { MessageBuilder } = require("..");
const { SecurityPolicy, MessageSecurityMode } = require("..");
            function () {/** */ }
            .on("message", (message) => { /** */})
            .on("message", (message) => { /** */})
const { readPrivateRsaKey } = require("node-opcua-crypto");
const {
    iterateOnSymmetricEncryptedChunk,
    iterateOnSignedAndEncryptedMessageChunks,
    iterateOnSignedMessageChunks,
    derivedKeys
} = require("../dist/test_helpers/fake_message_chunk_factory");
        const messageBuilder = new MessageBuilder({
            maxChunkCount: 10,
            maxMessageSize: someBuffer.length * 10,
            maxChunkSize: someBuffer.length  + 1000 ,
        });
        messageBuilder.privateKey = readPrivateRsaKey(private_key_filename);
            .on("message", (message) => {/** */ })
                console.log("ERROR", error);
        iterateOnSignedMessageChunks(someBuffer, (err, chunk) => {
        iterateOnSignedMessageChunks(someBuffer, function (err, chunk) {
        iterateOnSignedMessageChunks(someBuffer, function (err, chunk) {
        messageBuilder.privateKey = readPrivateRsaKey(private_key_filename);
            .on("message", (message) => {/** */ })
        iterateOnSignedAndEncryptedMessageChunks(lorem_ipsum_buffer, function (err, chunk) {
        const messageBuilder = new MessageBuilder({
            maxMessageSize: 1000000,
            maxChunkSize: 2048,
            maxChunkCount: 5
        });
        messageBuilder.privateKey = readPrivateRsaKey(private_key_filename);
        messageBuilder.pushNewToken({ tokenId: 10 }, derivedKeys);
        let _err;
            .on("message", (message) => {/** */ })
                console.log("err ", error.message);
        iterateOnSymmetricEncryptedChunk(lorem_ipsum_buffer, function (err, chunk) {
const { StatusCodes } = require("node-opcua-status-code");
        serverChannel.init(socket, function () {/** */ });
                secureChannel.abortConnection(function () {/** */ });
    // eslint-disable-next-line @typescript-eslint/no-this-alias


        await closeChannel(); // .should.be.rejectedWith(/Transport disconnected/);
            await promisify(secureChannel.closeWithError).call(secureChannel, StatusCodes.Bad, new Error("Sabotage"));
            console.log(err.message);
                        .on("message", (reconstructed_message) => {
            .on("message", (message) => {
                doDebug && console.log("client received a response ", response?.constructor.name);
                doDebug && console.log(response?.toString());

                    (serverSChannel as any)._build_message_builder();

    it("RR-client & server channel  - with security ", (done) => {

const { HelloMessage } = require("node-opcua-transport");
const { OpenSecureChannelRequest, SecurityTokenRequestType, ReadRequest } = require("node-opcua-types");
const { hexDump } = require("node-opcua-crypto");
const { BinaryStream } = require("node-opcua-binary-stream");

const { ServerSecureChannelLayer, MessageSecurityMode, SecurityPolicy, MessageChunker } = require("..");
const { pause } = require("../../node-opcua-end2end-test/test/discovery/_helper");
// eslint-disable-next-line import/order
        serverSecureChannel.on("message", (message) => {
        serverSecureChannel.on("message", (message) => {
            serverSecureChannel.send_response("MSG", new GetEndpointsResponse(), message, () => {/** */ });
            /** */





    it("KK8 should not accept message with too large chunk", async () => {
        const node = new DirectTransport();

        let serverSecureChannel = new ServerSecureChannelLayer({
        });

        serverSecureChannel.setSecurity(MessageSecurityMode.None, SecurityPolicy.None);
        serverSecureChannel.timeout = 100000;


        let initialized = false;
        serverSecureChannel.init(node.server, (err) => {
            initialized = true;
            should.not.exist(err);
        });

        async function send(chunk) {
            return await new Promise((resolve) => {
                node.client.once("data", (data) => {
                    resolve(data);
                });
                node.client.write(chunk);
            });
        }

        async function send1(msg, request) {

            const l = request.binaryStoreSize();

            // craft a HELLO Message        
            const b = new BinaryStream(l + 8);
            b.writeInt8(msg[0].charCodeAt(0));
            b.writeInt8(msg[1].charCodeAt(0));
            b.writeInt8(msg[2].charCodeAt(0));
            b.writeInt8("F".charCodeAt(0));
            b.writeUInt32(0);
            request.encode(b);
            b.buffer.writeInt32LE(b.length, 4);

            console.log(`sending\n${hexDump(b.buffer)}`)

            const rep = await send(b.buffer);
            console.log(`receiving\n${hexDump(rep)}`);
            return rep;
        }
        async function sendHello() {
            // eslint-disable-next-line no-undef
            const helloMessage = new HelloMessage({
                protocolVersion: 0,// UInt32;
                receiveBufferSize: 8 * 1024,// UInt32;
                sendBufferSize: 8 * 1024,
                maxMessageSize: 16 * 1024,
                maxChunkCount: 2,
                endpointUrl: "opc.tcp://localhost:1234/SomeEndpoint"
            });
            await send1("HEL", helloMessage);

        }

        let requestId = 1;
        async function send2(msg, request, tweakerFunc) {
            const messageChunker = new MessageChunker();

            return await new Promise((resolve,reject) => {
                node.client.once("data", (chunk) => {
                    requestId += 1;
                    console.log(`receiving\n${hexDump(chunk)}`);
                    resolve(chunk);
                    resolve();
                });
                node.client.once("error", (err) => {
                    reject(err);
                })
                messageChunker.chunkSecureMessage(msg, {
                    requestId,
                    securityMode: MessageSecurityMode.None,
                }, request, (chunk) => {
                    if (chunk) {
                        if (tweakerFunc) {
                            chunk = tweakerFunc(chunk);
                        }
                        console.log(`sending\n${hexDump(chunk)}`);
                        node.client.write(chunk)
                    } else {
                        console.log("done.");
                    }
                });
            });

        }

        async function sendOpenChannel() {

            const openChannelRequest = new OpenSecureChannelRequest({
                clientNonce: null,
                clientProtocolVersion: 0,
                requestHeader: {
                },
                requestType: SecurityTokenRequestType.Init,
                requestedLifetime: 100000,
                securityMode: MessageSecurityMode.None,
            });
            return await send2("OPN", openChannelRequest);

        }

        async function sendTooLargeChunkMessage() {
            return await send2("MSG", new ReadRequest({}), (chunk)=>{
                chunk.writeUInt32LE(0xFFFF,4);
                return chunk;
            });
        }

        await sendHello();
        await sendOpenChannel();
        await sendTooLargeChunkMessage();

        // console.log(serverSecureChannel.transport);

        await new Promise((resolve) => {
            serverSecureChannel.close(() => {
                serverSecureChannel.dispose();
                resolve();
            });
        });
        node.shutdown(() => {/** */ });

    });

const { decodeExpandedNodeId } = require("node-opcua-basic-types");
const { analyseExtensionObject } = require("node-opcua-packet-analyzer");
const { MessageBuilder } = require("..");
const { MessageSecurityMode, SecurityPolicy } = require("..");
const full_message_body = require("../test_fixtures/fixture_problematic_ReadResponse.js").packet_ReadResponse;

describe("testing with problematic packet", function () {
        messageBuilder.setSecurity(MessageSecurityMode.None, SecurityPolicy.None);
            on("message", function (message) {
                on_message__received = true;
                message.schema.name.should.equal("GetEndpointsResponse");
                on_message__received.should.equal(true);
                full_message_body_event_received.should.equal(true);
                done();
            }).
            on("full_message_body", function (full_message_body) {
                full_message_body_event_received = true;
            }).
            on("error", function (err) {
                should.not.exist(err);
                throw new Error("should not get there");
            });
            on("message", function (message) {
                messageCount += 1;
                if (messageCount === 2) {
                    done();
                }
            }).
            on("error", function (err) {
                console.log(err);
                throw new Error("should not get there");
            }).
            on("invalid_sequence_number", function (expected, found) {
                throw new Error("should not received a invalid_sequence_number here");
            });
        const id = decodeExpandedNodeId(binaryStream);
        // this.objectFactory = objectFactory;
        // // construct the object
        // const objMessage = this.objectFactory.constructObject(id);
        // if (!objMessage) {
        //     console.log("cannot construct object with id" + id.toString());
        // }
        // objMessage.constructor.name.should.eql("ReadResponse");
const { makeBufferFromTrace  } = require("node-opcua-debug");
exports.packet_ReadResponse= makeBufferFromTrace(function () {
export function iterateOnSymmetricEncryptedChunk(buffer: Buffer, onChunkFunc: ChunkVisitorFunc) {
    msgChunkManager.on("chunk", (chunk, final) => onChunkFunc(null, chunk));
    const maxChunkSize = packets.map((p) => p.length).reduce((a, b) => Math.max(a, b), 0);

    const messageBuilder = new MessageBuilder({
        maxChunkCount: packets.length + 1,
        maxMessageSize: 1000000,
        maxChunkSize
    });
    messageBuilder.on("startChunk", (info) => {
    messageBuilder.on("error", (err) => {
        console.log("verify_multi_chunk_message : err", err.message);
    });

import { bindRoleSet } from "./user_manager_ua";
        return computeSignature(clientCertificate, clientNonce, this.getPrivateKey(), channel.messageBuilder!.securityPolicy);
        const securityPolicy = channel.messageBuilder!.securityPolicy;
        assert(typeof callback === "function");
        assert(typeof callback === "function");
    on(eventName: "chunk", eventHandler: (messageChunk: Buffer) => void): this;
    once(eventName: "chunk", eventHandler: (messageChunk: Buffer) => void): this;
 *    transport.on("chunk",function(message_chunk) {
 *        // do something with chunk from server...
    public static defaultMaxChunk = 0; // 0 - no limits
    public static defaultMaxMessageSize = 0; // 0 - no limits
    public static defaultReceiveBufferSize = 1024 * 64 * 10;
    public static defaultSendBufferSize = 1024 * 64 * 10; // 8192 min,


        // initially before HEL/ACK
        this.maxChunkCount = 1;
        this.maxMessageSize = 4 * 1024;
        this.receiveBufferSize = 4 * 1024;


            this.setLimits(response as AcknowledgeMessage);
            maxChunkCount: ClientTCP_transport.defaultMaxChunk,
            maxMessageSize: ClientTCP_transport.defaultMaxMessageSize,
            receiveBufferSize: ClientTCP_transport.defaultReceiveBufferSize,
            sendBufferSize: ClientTCP_transport.defaultSendBufferSize
export * from "./HelloMessage";
export * from "./message_builder_base";
export * from "./status_codes";
export * from "./TCPErrorMessage";
export * from "./utils";
import { make_errorLog, make_debugLog, make_warningLog } from "node-opcua-debug";
import { StatusCode } from "node-opcua-status-code";
import { StatusCodes2 } from "./status_codes";
const warningLog = make_warningLog("MessageBuilder");
export interface MessageBuilderBaseOptions {
    signatureLength?: number;
    maxMessageSize?: number;
    maxChunkCount?: number;
    maxChunkSize?: number;
}

export interface MessageBuilderBase {
    on(eventName: "startChunk", eventHandler: (info: PacketInfo, data: Buffer) => void): this;
    on(eventName: "chunk", eventHandler: (chunk: Buffer) => void): this;
    on(eventName: "error", eventHandler: (err: Error, statusCode: StatusCode, requestId: number | null) => void): this;
    on(eventName: "full_message_body", eventHandler: (fullMessageBody: Buffer) => void): this;

    emit(eventName: "startChunk", info: PacketInfo, data: Buffer): boolean;
    emit(eventName: "chunk", chunk: Buffer): boolean;
    emit(eventName: "error", err: Error, statusCode: StatusCode, requestId: number | null): boolean;
    emit(eventName: "full_message_body", fullMessageBody: Buffer): boolean;
}
    public static defaultMaxChunkCount = 1000;
    public static defaultMaxMessageSize = 1024 * 64;
    public static defaultMaxChunkSize = 1024 * 8;

    public readonly signatureLength: number;
    public readonly maxMessageSize: number;
    public readonly maxChunkCount: number;
    public readonly maxChunkSize: number;

    public readonly options: MessageBuilderBaseOptions;
    constructor(options?: MessageBuilderBaseOptions) {
        options = options || {
            maxMessageSize: 0,
            maxChunkCount: 0,
            maxChunkSize: 0
        };
        this.maxMessageSize = options.maxMessageSize || MessageBuilderBase.defaultMaxMessageSize;
        this.maxChunkCount = options.maxChunkCount || MessageBuilderBase.defaultMaxChunkCount;
        this.maxChunkSize = options.maxChunkSize || MessageBuilderBase.defaultMaxChunkSize;

            minimumSizeInBytes: 8,
            maxChunkSize: this.maxChunkSize,
            readChunkFunc: readRawMessageHeader
        this._packetAssembler.on("chunk", (messageChunk) => this._feed_messageChunk(messageChunk));
        this._packetAssembler.on("startChunk", (info, data) => {
            this.emit("startChunk", info, data);
        });

        this._packetAssembler.on("error", (err) => {
            warningLog("packet assembler ", err.message);
            return this._report_error(StatusCodes2.BadTcpMessageTooLarge, "packet assembler: " + err.message);
                return this._report_error(StatusCodes2.BadTcpSecureChannelUnknown, "Invalid secure channel Id");
            return this._report_error(StatusCodes2.BadTcpInternalError, "_read_headers error " + (err as Error).message);
    protected _report_error(statusCode: StatusCode, errorMessage: string): false {
        this.emit("error", new Error(errorMessage), statusCode, this.sequenceHeader ? this.sequenceHeader.requestId : null);
        if (this.messageChunks.length + 1 > this.maxChunkCount) {
            return this._report_error(StatusCodes2.BadTcpMessageTooLarge, `max chunk count exceeded: ${this.maxChunkCount}`);
        }

        if (this.totalMessageSize > this.maxMessageSize) {
            return this._report_error(StatusCodes2.BadTcpMessageTooLarge, `max message size exceeded: ${this.maxMessageSize}`);
        }

            return this._report_error(StatusCodes2.BadTcpInternalError, `Invalid message header detected`);
                StatusCodes2.BadTcpInternalError,
            return this._report_error(StatusCodes2.BadRequestInterrupted, "received and Abort Message");

        // before HEL/ACK
        this.maxChunkCount = 1;
        this.maxMessageSize = 4 * 1024;
        this.receiveBufferSize = 4 * 1024;

        // istanbul ignore next
        

        // When a fatal error occurs, the Server shall send an Error Message to the Client and
        // closes the TransportConnection gracefully.
        doDebug && debugLog(chalk.cyan("_abortWithError"));
        
        /* istanbul ignore next */
        if (this._aborted) {
            // already called
            return callback(new Error(statusCode.name));
        this._aborted = 1;
        setTimeout(() => {
            // send the error message and close the connection
            this.sendErrorMessage(statusCode, statusCode.description);
            this.disconnect(() => {
                this._aborted = 2;
                callback(new Error(extraErrorDescription + " StatusCode = " + statusCode.name));
            });
        }, ServerTCP_transport.throttleTime);
    }
    private _send_ACK_response(helloMessage: HelloMessage): void {
        assert(helloMessage.receiveBufferSize >= minimumBufferSize);
        assert(helloMessage.sendBufferSize >= minimumBufferSize);
        const minBufferSize = 8192;
        const maxBufferSize = 64 * 1024;
        const minMaxMessageSize = 128 * 1024;
        const defaultMaxMessageSize = 16 * 1024 * 1024;
        const maxMaxMessageSize = 128 * 1024 * 1024;
        const minMaxChunkCount = 1;
        const defaultMaxChunkCount = defaultMaxMessageSize / maxBufferSize;
        const maxMaxChunkCount = 9000;
        const receiveBufferSize = 32 * 1024;
        const sendBufferSize = 32 * 1024;
        if (!helloMessage.maxChunkCount && helloMessage.sendBufferSize) {
            helloMessage.maxChunkCount = helloMessage.maxMessageSize / helloMessage.sendBufferSize;
        }
        this.setLimits({
            receiveBufferSize: clamp_value(helloMessage.receiveBufferSize || receiveBufferSize, minBufferSize, maxBufferSize),
            sendBufferSize: clamp_value(helloMessage.sendBufferSize || sendBufferSize, minBufferSize, maxBufferSize),
            maxMessageSize: clamp_value(helloMessage.maxMessageSize || defaultMaxMessageSize, minMaxMessageSize, maxMaxMessageSize),
            maxChunkCount: clamp_value(helloMessage.maxChunkCount || defaultMaxChunkCount, minMaxChunkCount, maxMaxChunkCount)
        });
            console.log("Client accepts only message of size => ", this.maxMessageSize);
import { StatusCodes } from "node-opcua-status-code";

export const StatusCodes2 = {
    /**
     * The Server cannot process the request because it is too busy.
     *
     * It is up to the Server to determine when it needs to return this Message.
     *
     * A Server can control the how frequently a Client reconnects by waiting to return this error.
     */
    BadTcpServerTooBusy: StatusCodes.BadTcpServerTooBusy,
    /**
     * The type of the Message specified in the header invalid.
     * Each Message starts with a 4-byte sequence of ASCII values that identifies the Message type.
     * The Server returns this error if the Message type is not accepted.
     * Some of the Message types are defined by the SecureChannel layer.
     */
    BadTcpMessageTypeInvalid: StatusCodes.BadTcpMessageTypeInvalid,
    /**
     * The SecureChannelId and/or TokenId are not currently in use.
     * This error is reported by the SecureChannel layer.
     */
    BadTcpSecureChannelUnknown: StatusCodes.BadTcpSecureChannelUnknown,
    /**
     * The size of the Message specified in the header is too large.
     * The Server returns this error if the Message size exceeds its maximum buffer size
     * or the receive buffer size negotiated during the Hello/Acknowledge exchange.
     */
    BadTcpMessageTooLarge: StatusCodes.BadTcpMessageTooLarge,

    /**
     * A timeout occurred while accessing a resource.
     * It is up to the Server to determine when a timeout occurs.
     */
    BadTimeout: StatusCodes.BadTimeout,
    /**
     * There are not enough resources to process the request.
     * The Server returns this error when it runs out of memory or encounters similar resource problems.
     * A Server can control the how frequently a Client reconnects by waiting to return this error.
     */
    BadTcpNotEnoughResources: StatusCodes.BadTcpNotEnoughResources,
    /**
     * An internal error occurred.
     * This should only be returned if an unexpected configuration or programming error occurs.
     */
    BadTcpInternalError: StatusCodes.BadTcpInternalError,
    /**
     * The Server does not recognize the EndpointUrl specified.
     */
    BadTcpEndpointUrlInvalid: StatusCodes.BadTcpEndpointUrlInvalid,
    /**
     * The Message was rejected because it could not be verified.
     */
    BadSecurityChecksFailed: StatusCodes.BadSecurityChecksFailed,
    /**
     * The request could not be sent because of a network interruption.
     */
    BadRequestInterrupted: StatusCodes.BadRequestInterrupted,
    /**
     * Timeout occurred while processing the request.
     */
    BadRequestTimeout: StatusCodes.BadRequestTimeout,
    /**
     * The secure channel has been closed.
     */
    BadSecureChannelClosed: StatusCodes.BadSecureChannelClosed,
    /**
     * The SecurityToken has expired or is not recognized. BadSecureChannelTokenUnknown
     */
    BadSecureChannelTokenUnknown: StatusCodes.BadSecureChannelTokenUnknown,
    /**
     * The sender Certificate is not trusted by the receiver.
     */
    BadCertificateUntrusted: StatusCodes.BadCertificateUntrusted,
    /**
     * The sender Certificate has expired or is not yet valid.
     */
    BadCertificateTimeInvalid: StatusCodes.BadCertificateTimeInvalid,
    /**
     * The issuer for the sender Certificate has expired or is not yet valid.
     */
    BadCertificateIssuerTimeInvalid: StatusCodes.BadCertificateIssuerTimeInvalid,
    /**
     * The sender’s Certificate may not be used for establishing a secure channel.
     */
    BadCertificateUseNotAllowed: StatusCodes.BadCertificateUseNotAllowed,
    /**
     * The issuer Certificate may not be used as a Certificate Authority.
     */
    BadCertificateIssuerUseNotAllowed: StatusCodes.BadCertificateIssuerUseNotAllowed,
    /**
     * Could not verify the revocation status of the sender’s Certificate.
     */
    BadCertificateRevocationUnknown: StatusCodes.BadCertificateRevocationUnknown,
    /**
     * Could not verify the revocation status of the issuer Certificate.
     */
    BadCertificateIssuerRevocationUnknown: StatusCodes.BadCertificateIssuerRevocationUnknown,
    /**
     * The sender Certificate has been revoked by the issuer.
     */
    BadCertificateRevoked: StatusCodes.BadCertificateRevoked
    /**The issuer Certificate has been revoked by its issuer.
     */
    // todo  BadIssuerCertificateRevoked: StatusCodes.BadIssuerCertificateRevoked,
    /** The receiver Certificate thumbprint is not recognized by the receiver.
     */
    // todo .. BadCertificateUnknown: StatusCodes.BadCertificateUnknown
};
import { BinaryStream } from "node-opcua-binary-stream";
import { make_debugLog, checkDebugFlag, make_errorLog, hexDump } from "node-opcua-debug";
import { PacketAssembler, PacketAssemblerErrorCode } from "node-opcua-packet-assembler";
import { ErrorCallback, CallbackWithData, StatusCode } from "node-opcua-status-code";
import { StatusCodes2 } from "./status_codes";
import { doTraceIncomingChunk } from "./utils";
import { TCPErrorMessage } from "./TCPErrorMessage";
import { packTcpMessage } from "./tools";
const debugLog = make_debugLog(__filename);
const doDebug = checkDebugFlag(__filename);
const errorLog = make_errorLog(__filename);

    on(eventName: "chunk", eventHandler: (messageChunk: Buffer) => void): this;
    once(eventName: "chunk", eventHandler: (messageChunk: Buffer) => void): this;
    public maxMessageSize: number;
    public maxChunkCount: number;
    public sendBufferSize: number;
    public receiveBufferSize: number;

        this.maxMessageSize = 0;
        this.maxChunkCount = 0;
        this.receiveBufferSize = 0;
        this.sendBufferSize = 0;
    public setLimits({
        receiveBufferSize,
        sendBufferSize,
        maxMessageSize,
        maxChunkCount
    }: {
        receiveBufferSize: number;
        sendBufferSize: number;
        maxMessageSize: number;
        maxChunkCount: number;
    }) {
        this.receiveBufferSize = receiveBufferSize;
        this.sendBufferSize = sendBufferSize;
        this.maxMessageSize = maxMessageSize;
        this.maxChunkCount = maxChunkCount;

        // reinstall packetAssembler with correct limits
        this._install_packetAssembler();
    }

    public write(messageChunk: Buffer, callback?: (err?: Error) => void | undefined): void {
        const c = header.messageHeader.isFinal;
        assert(c === "F" || c === "C" || c === "A");
        this._write_chunk(messageChunk, (err) => {
            callback && callback(err);
        });
    protected _write_chunk(messageChunk: Buffer, callback?: (err?: Error) => void | undefined): void {
            this._socket.write(messageChunk, callback);
        } else {
            if (callback) {
                callback();
            }
    protected _install_packetAssembler() {
        if (this.packetAssembler) {
            this.packetAssembler.removeAllListeners();
            this.packetAssembler = undefined;
        }

        // install packet assembler ...
        this.packetAssembler = new PacketAssembler({
            readChunkFunc: readRawMessageHeader,
            minimumSizeInBytes: this.headerSize,
            maxChunkSize: this.receiveBufferSize //Math.max(this.receiveBufferSize, this.sendBufferSize)
        });

        this.packetAssembler.on("chunk", (chunk: Buffer) => this._on_message_chunk_received(chunk));

        this.packetAssembler.on("error", (err, code) => {
            let statusCode = StatusCodes2.BadTcpMessageTooLarge;
            switch (code) {
                case PacketAssemblerErrorCode.ChunkSizeExceeded:
                    statusCode = StatusCodes2.BadTcpMessageTooLarge;
                    break;
                default:
                    statusCode = StatusCodes2.BadTcpInternalError;
            }

            this.sendErrorMessage(statusCode, err.message);
            this.prematureTerminate(new Error("Packet Assembler : " + err.message), statusCode);
        });
    }
        this._install_packetAssembler();
            this.prematureTerminate(new Error("socket timeout : timeout=" + this.timeout), StatusCodes2.BadTimeout);
    public sendErrorMessage(statusCode: StatusCode, extraErrorDescription: string | null): void {
        // When the Client receives an Error Message it reports the error to the application and closes the TransportConnection gracefully.
        // If a Client encounters a fatal error, it shall report the error to the application and send a CloseSecureChannel Message.

        /* istanbul ignore next*/
        if (doDebug) {
            debugLog(chalk.red(" sendErrorMessage        ") + chalk.cyan(statusCode.toString()));
            debugLog(chalk.red(" extraErrorDescription   ") + chalk.cyan(extraErrorDescription));
        }

        const reason = `${statusCode.toString()}:${extraErrorDescription || ""}`;
        const errorResponse = new TCPErrorMessage({
            statusCode,
            reason
        });
        const messageChunk = packTcpMessage("ERR", errorResponse);
        this.write(messageChunk);
    }

    public prematureTerminate(err: Error, statusCode: StatusCode): void {
        // https://reference.opcfoundation.org/v104/Core/docs/Part6/6.7.3/

        debugLog("prematureTerminate", err ? err.message : "", statusCode.toString());

            err.message = "premature socket termination " + err.message;
        // this.gracefullShutdown(err);
    private _on_message_chunk_received(messageChunk: Buffer) {
        if (doTraceIncomingChunk) {
            console.log(hexDump(messageChunk));
        }
            this.emit("chunk", messageChunk);
        // istanbul ignore next
export const doTraceHelloAck = !!transportFlag.match(/HELACK/);
export const doTraceChunk = !!transportFlag.match(/CHUNK/);
export const doTraceIncomingChunk = !!transportFlag.match(/FLOW/);
const { assert } = require("node-opcua-assert");
const { make_debugLog, make_errorLog } = require("node-opcua-debug");
const { StatusCodes, StatusCode } = require("node-opcua-status-code");

const { MessageBuilderBase, writeTCPMessageHeader } = require("..");
        transport.on("chunk", function (message_chunk) {

        /**
         * ```createChunk``` is used to construct a pre-allocated chunk to store up to ```length``` bytes of data.
         * The created chunk includes a prepended header for ```chunk_type``` of size ```self.headerSize```.
         *
         * @method createChunk
         * @param msgType
         * @param chunkType {String} chunk type. should be 'F' 'C' or 'A'
         * @param length
         * @return a buffer object with the required length representing the chunk.
         *
         * Note:
         *  - only one chunk can be created at a time.
         *  - a created chunk should be committed using the ```write``` method before an other one is created.
         */
        function createChunk(msgType, chunkType, headerSize, length) {
            assert(msgType === "MSG");
            const totalLength = length + headerSize;
            const buffer = Buffer.alloc(totalLength);
            writeTCPMessageHeader("MSG", chunkType, totalLength, buffer);
            return buffer;
        }
        transport.connect(endpointUrl, (err) => {
            const buf = createChunk("MSG", "F", transport.headerSize, message1.length);
const sinon = require("sinon");

        builder.on("chunk", (message_chunk) => {
        builder.on("chunk", (message_chunk) => {

    it("should not allow more chunks that maxChunkCount ", function (done) {

        const builder = new MessageBuilderBase({
            maxChunkCount: 5,
            maxMessageSize: 64 * 1024,
        });

        const onChunkSpy = sinon.spy();
        builder.on("chunk", onChunkSpy);

        const onFullMessageBodySpy = sinon.spy();
        builder.on("full_message_body", onFullMessageBodySpy);

        const onErrorSpy = sinon.spy();
        builder.on("error", onErrorSpy);

        const message_body = Buffer.alloc(16 * 1024);
        const chunk1 = wrap_message_in_chunk(message_body.slice(0 * 1024, 1 * 1024), "C");
        const chunk2 = wrap_message_in_chunk(message_body.slice(1 * 1024, 2 * 1024), "C");
        const chunk3 = wrap_message_in_chunk(message_body.slice(2 * 1024, 3 * 1024), "C");
        const chunk4 = wrap_message_in_chunk(message_body.slice(3 * 1024, 4 * 1024), "C");
        const chunk5 = wrap_message_in_chunk(message_body.slice(4 * 1024, 5 * 1024), "C");
        const chunk6 = wrap_message_in_chunk(message_body.slice(5 * 1024, 6 * 1024), "C");
        const chunk7 = wrap_message_in_chunk(message_body.slice(6 * 1024, 7 * 1024), "F");

        builder.feed(chunk1);
        builder.feed(chunk2);
        builder.feed(chunk3);
        builder.feed(chunk4);
        builder.feed(chunk5);
        builder.feed(chunk6);
        builder.feed(chunk7);

        onChunkSpy.callCount.should.eql(6);
        onErrorSpy.callCount.should.eql(1);
        onFullMessageBodySpy.callCount.should.eql(0);

        onErrorSpy.getCall(0).args[0].should.match(/max chunk count exceeded/);

        done();

    });
    it("should not allow message bigger than maxMessageSize ", function (done) {

        const builder = new MessageBuilderBase({
            maxChunkCount:  1000,
            maxMessageSize: 4 * 1024,
        });

        const onChunkSpy = sinon.spy();
        builder.on("chunk", onChunkSpy);

        const onFullMessageBodySpy = sinon.spy();
        builder.on("full_message_body", onFullMessageBodySpy);

        const onErrorSpy = sinon.spy();
        builder.on("error", onErrorSpy);

        const message_body = Buffer.alloc(16 * 1024);
        const chunk1 = wrap_message_in_chunk(message_body.slice(0 * 1024, 1 * 1024), "C");
        const chunk2 = wrap_message_in_chunk(message_body.slice(1 * 1024, 2 * 1024), "C");
        const chunk3 = wrap_message_in_chunk(message_body.slice(2 * 1024, 3 * 1024), "C");
        const chunk4 = wrap_message_in_chunk(message_body.slice(3 * 1024, 4 * 1024), "C");
        const chunk5 = wrap_message_in_chunk(message_body.slice(4 * 1024, 5 * 1024), "C");
        const chunk6 = wrap_message_in_chunk(message_body.slice(5 * 1024, 6 * 1024), "C");
        const chunk7 = wrap_message_in_chunk(message_body.slice(6 * 1024, 7 * 1024), "F");

        builder.feed(chunk1);
        builder.feed(chunk2);
        builder.feed(chunk3);
        builder.feed(chunk4);
        builder.feed(chunk5);
        builder.feed(chunk6);
        builder.feed(chunk7);

        onChunkSpy.callCount.should.eql(4);
        onErrorSpy.callCount.should.eql(1);
        onFullMessageBodySpy.callCount.should.eql(0);

        onErrorSpy.getCall(0).args[0].should.match(/maxMessageSize/);
        
        done();

    });
        transport.on("chunk", (messageChunk) => {
        transport.setLimits({
            maxChunkCount: 10000,
            maxMessageSize: 10000,
            receiveBufferSize: 10000,
            sendBufferSize: 10000
        });

            if (err) {
                console.log(err.message);
            }
        transport.on("chunk", (messageChunk) => {
        transport.on("chunk", (messageChunk) => {
export interface DirectTransport {
    on(eventName: "end", eventHandler:()=>void): this;
}
        this.server.on("end", (err?: Error) => {
export interface HalfComChannel {
    on(eventName: "data", eventHandler:(data: Buffer)=>void): this;
    on(eventName: "send_data", eventHandler:(data: Buffer)=>void): this;
    on(eventName: "ending", eventHandler:()=>void): this;
    on(eventName: "end", eventHandler:(err?: Error)=>void): this;

}