UNPKG

oracledb

Version:

A Node.js module for Oracle Database access from JavaScript and TypeScript

439 lines (382 loc) 13 kB
// Copyright (c) 2022, 2025, Oracle and/or its affiliates. //----------------------------------------------------------------------------- // // This software is dual-licensed to you under the Universal Permissive License // (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License // 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose // either license. // // If you elect to accept the software under the Apache License, Version 2.0, // the following applies: // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //----------------------------------------------------------------------------- 'use strict'; const Buffer = require("buffer").Buffer; const constants = require("./constants.js"); const errors = require("../../errors.js"); const MAX_CDATA_LEN = 230; const NSPCNL = 74; /** * Connect Packet (NSPTCN) * @param {Buffer} connectData Outgoing Connect Data * @param {Object} sAtts Session Attributes */ function ConnectPacket(connectData, sAtts, flags = 0) { this.connectData = connectData; this.connectDataLen = connectData.length; this.overflow = false; let size; if (this.connectDataLen <= MAX_CDATA_LEN) { size = NSPCNL + this.connectDataLen; } else { size = NSPCNL; this.overflow = true; } /* Building Connect Packet */ this.buf = Buffer.allocUnsafe(size).fill(0); this.buf.writeUInt16BE(size, constants.NSPHDLEN); this.buf.writeUInt8(flags, constants.NSPHDFLGS); this.buf.writeUInt8(constants.NSPTCN, constants.NSPHDTYP); this.buf.writeUInt16BE( constants.TNS_VERSION_DESIRED, constants.NSPCNVSN ); /* My version */ this.buf.writeUInt16BE( constants.TNS_VERSION_MINIMUM, constants.NSPCNLOV ); /* Lowest version*/ /* Options: Note : Node JS does not support TCP Out-of-band so not setting NSGSENDATTN or NSGRECVATTN */ const options = constants.NSGDONTCARE; //options = options | constants.NSGUSEVIO /* Vectored I/O support.Uncomment when support is added */ this.buf.writeUInt16BE(options, constants.NSPCNOPT); /* SDU */ if (sAtts.sdu > constants.NSPMXSDULN) { this.buf.writeUInt16BE(constants.NSPMXSDULN, constants.NSPCNSDU); } else { this.buf.writeUInt16BE(sAtts.sdu, constants.NSPCNSDU); } /* TDU */ if (sAtts.tdu > constants.NSPMXSDULN) { this.buf.writeUInt16BE(constants.NSPMXSDULN, constants.NSPCNTDU); } else { this.buf.writeUInt16BE(sAtts.tdu, constants.NSPCNTDU); } this.buf.writeUInt16BE( sAtts.ntCha, constants.NSPCNNTC ); /* Protocol characteristics */ this.buf.writeUInt16BE( 1, constants.NSPCNONE ); /* Endianness does not matter for Node */ this.buf.writeUInt16BE( this.connectDataLen, constants.NSPCNLEN ); /* Connect data Length */ this.buf.writeUInt16BE( constants.NSPCNDAT, constants.NSPCNOFF ); /* Connect data offset */ this.buf.writeUInt8( sAtts.NAFlags, constants.NSPCNFL0 ); /* NA disabled */ this.buf.writeUInt8( sAtts.NAFlags, constants.NSPCNFL1 ); /* NA disabled * /* Connection Pool is not supported */ this.buf.writeUInt16BE(0, constants.NSPCNTMO); this.buf.writeUInt16BE(0, constants.NSPCNTCK); this.buf.writeUInt16BE(0, constants.NSPCNADL); this.buf.writeUInt16BE(0, constants.NSPCNAOF); this.buf.writeUInt32BE(sAtts.sdu, constants.NSPCNLSD); /* SDU */ this.buf.writeUInt32BE(sAtts.tdu, constants.NSPCNLTD); /* TDU */ let compressionFieldBuilder = 0; // we will be using just the last 2 bytes /* * For the 2 byte field, the first bit represent ON/OFF , second is empty * After that every 4 bits represent a value in the user preference of sch eme * scheme values are 1 = LZO, 2 = ZLIB, 3 = GZIP ( only ZLIB supported in node-oracledb) */ if (sAtts.networkCompression) { compressionFieldBuilder = constants.NSPACCFON << 8 ; // set the ON bit const schemeShiftCounter = 10; // shifting by 10 puts the 4 bit field in the right place if (sAtts.networkCompressionLevels.includes('high')) compressionFieldBuilder |= constants.NETWORK_COMPRESSION_ZLIB << schemeShiftCounter; } this.buf.writeUInt16BE(compressionFieldBuilder, constants.NSPCNCFL); /* Compression field added */ this.buf.writeUInt32BE( 0, constants.NSPCNCFL2 ); /* No OOB path check support */ if (!this.overflow && this.connectDataLen) { this.buf.write(connectData.toString('ascii'), constants.NSPCNDAT, this.connectDataLen, "ascii"); } } /** * Data Pakcet (NSPTDA) * @param {int} size of Data Packet * @param {boolean} isLargeSDU Large SDU */ function DataPacket(isLargeSDU) { this.dataPtr = 0; /* data offset start */ this.dataLen = 0; /* data offset end */ this.offset = 0; /* Offset for buffer read/write (fastpath) */ this.len = 0; /* Length of buffer read/write (fastpath) */ this.bufLen = 0; /* Length of buffer */ /** * Create the Data Packet(Internal) */ this.createPacket = function(len) { /* Building Data Packet */ this.dataPtr = constants.NSPDADAT; this.dataLen = constants.NSPDADAT; this.buf = Buffer.allocUnsafe(len).fill(0); this.buf.writeUInt8(0, constants.NSPHDFLGS); this.buf.writeUInt8(constants.NSPTDA, constants.NSPHDTYP); this.bufLen = len; /* Length of buffer */ }; /** * Populate the Data Packet * @param {Buffer} userbuf User Buffer * @param {int} offset from which to fill data * @param {int} len length of data * @returns number of bytes copied */ this.fillBuf = function(userbuf, offset, len, flags = 0) { let bytes2Copy; if (!this.buf) { this.createPacket(len + constants.NSPDADAT); //Currently NS data packets are being used only in the connect/disconnect phase } if (len > this.bufLen - this.dataLen) { bytes2Copy = this.bufLen - this.dataLen; } else { bytes2Copy = len; } if (bytes2Copy) { userbuf.copy(this.buf, this.dataLen, offset, offset + bytes2Copy); } this.dataLen += bytes2Copy; this.prepare2Send(flags); return bytes2Copy; }; /** * Prepare Data Packet for send * @param {int} flags Data flags */ this.prepare2Send = function(flags = 0) { if (isLargeSDU) { this.buf.writeUInt32BE(this.dataLen, constants.NSPHDLEN); } else { this.buf.writeUInt16BE(this.dataLen, constants.NSPHDLEN); } this.buf.writeUInt16BE(flags, constants.NSPDAFLG); this.dataBuf = this.buf.subarray(0, this.dataLen); }; /** * Construct Data Packet from receive data * @param {Packet} packet NS packet */ this.fromPacket = function(packet) { this.buf = packet.buf; this.dataLen = packet.buf.length; this.dataPtr = constants.NSPDADAT; this.offset = this.dataPtr; this.len = this.dataLen; packet.dataOffset = this.dataPtr; }; } /** * Accept Packet (NSPTAC) * @param {Packet} packet NS packet * @param {*} sAtts session Attributes */ function AcceptPacket(packet, sAtts) { this.buf = packet.buf; this.len = packet.buf.length; /* Set negotiated values */ sAtts.version = packet.buf.readUInt16BE(constants.NSPACVSN); sAtts.options = packet.buf.readUInt16BE(constants.NSPACOPT); sAtts.sdu = packet.buf.readUInt16BE(constants.NSPACSDU); sAtts.tdu = packet.buf.readUInt16BE(constants.NSPACTDU); if (sAtts.version >= 315) { /* Large SDU Support */ sAtts.sdu = packet.buf.readUInt32BE(constants.NSPACLSD); sAtts.tdu = packet.buf.readUInt32BE(constants.NSPACLTD); sAtts.largeSDU = true; this.cflag = packet.buf.readUInt8(constants.NSPACCFL); // 00001000 if ((this.cflag & constants.NSPACCFON) != 0) { // compression ON sAtts.negotiatedNetworkCompressionScheme = (this.cflag & 0x3c) >> 2; // 0x3c sAtts.networkCompressionEnabled = true; sAtts.firstCompressedPacket = true; } else { sAtts.networkCompressionEnabled = false; } } /* Accept flags */ this.flag0 = packet.buf.readUInt8(constants.NSPACFL0); this.flag1 = packet.buf.readUInt8(constants.NSPACFL1); sAtts.noNA = ((this.flag1 & constants.NSINANOSERVICES) == constants.NSINANOSERVICES); if (!sAtts.noNA) sAtts.noNA = ((this.flag0 & constants.NSINADISABLEDFORCONNECTION) == constants.NSINADISABLEDFORCONNECTION); if (sAtts.version >= constants.TNS_VERSION_MIN_DATA_FLAGS) { packet.flags = packet.buf.readUInt32BE(constants.NSPACFL2); } } /** * Refuse Packet * @param {*} packet NS packet */ function RefusePacket(packet) { this.buf = packet.buf; this.len = packet.buf.length; this.userReason = packet.buf.readUInt8(constants.NSPRFURS); this.systemReason = packet.buf.readUInt8(constants.NSPRFURS); this.dataLen = packet.buf.readUInt16BE(constants.NSPRFLEN); this.dataOff = constants.NSPRFDAT; if (this.len > this.dataOff) { this.dataBuf = this.buf.toString('ascii', this.dataOff, this.len); this.overflow = false; } else { this.overflow = true; } } /** * Redirect Packet (NSPTRD) * @param {*} packet NS packet */ function RedirectPacket(packet) { this.buf = packet.buf; this.len = packet.buf.length; this.flags = packet.flags; this.dataLen = packet.buf.readUInt16BE(constants.NSPRDLEN); this.dataOff = constants.NSPRDDAT; if (this.len > this.dataOff) { this.dataBuf = this.buf.subarray(this.dataOff, this.len); this.overflow = false; } else { this.overflow = true; } } /** * Marker Packet (NSPTMK) * @param {int} isLargeSDU Large SDU */ function MarkerPacket(isLargeSDU) { this.len = constants.NSPMKDAT + 1; /* Packet length */ // Packet Buffer this.buf = Buffer.allocUnsafe(constants.NSPMKDAT + 1).fill(0); if (isLargeSDU) { this.buf.writeUInt32BE(this.len, constants.NSPHDLEN); } else { this.buf.writeUInt16BE(this.len, constants.NSPHDLEN); } this.buf.writeUInt8(0, constants.NSPHDFLGS); this.buf.writeUInt8(constants.NSPTMK, constants.NSPHDTYP); /** * Prepare Marker packet for write * @param {Uint8} type of Marker * @param {Uint8} data Marker byte */ this.prepare = function(type, data) { this.buf.writeUInt8(type, constants.NSPMKTYP); this.buf.writeUInt8(data, constants.NSPMKDAT); }; /** * Marker Packet receive * @param {Packet} packet NS packet * @param {NetworkSession} nsi Network Session */ this.fromPacket = function(packet, nsi) { this.type = packet.buf.readUInt8(constants.NSPMKTYP); switch (this.type) { case constants.NSPMKTD0: nsi.isBreak = true; break; case constants.NSPMKTD1: this.data = packet.buf.readUInt8(constants.NSPMKDAT); nsi.isBreak = true; if (this.data == constants.NIQRMARK) { nsi.isReset = true; nsi.isBreak = true; } break; default: errors.throwErr(errors.ERR_INVALID_PACKET); } }; } /** * Control Packet NSPTCTL */ function ControlPacket() { /** * Clear(reset) the packet */ this.clear = function() { this.errno = 0; this.notif = null; this.notifLen = 0; this.cmd = 0; }; /** * Control Packet receive * @param {*} packet NS packet */ this.fromPacket = function(packet) { const NSECMANSHUT = 12572; // CMAN SHUTDOWN const NSESENDMESG = 12573; // SEND MESSAGE const ORA_ERROR_EMFI_NUMBER = 22; //ORA -error let emfi; let err1; let err2; this.cmd = packet.buf.readUInt16BE(constants.NSPCTLCMD); switch (this.cmd) { case constants.NSPCTL_SERR: emfi = packet.buf.readUInt32BE(constants.NSPCTLDAT); err1 = packet.buf.readUInt32BE(constants.NSPCTLDAT + 4); err2 = packet.buf.readUInt32BE(constants.NSPCTLDAT + 8); if (err1 == NSECMANSHUT) { this.errno = err1; } else if (err1 == NSESENDMESG) { this.errno = err1; this.notifLen = err2; this.notif = Buffer.allocUnsafe(err2 + 1).fill(0); this.buf.copy(this.notif, 0, constants.NSPCTLDAT + 12, constants.NSPCTLDAT + 12 + err2); } else { this.errno = err1; if (emfi == ORA_ERROR_EMFI_NUMBER) { errors.throwErr(errors.ERR_CONNECTION_INBAND, "ORA" + "-" + err1); } else { errors.throwErr(errors.ERR_CONNECTION_INBAND, "TNS" + "-" + err1); } } break; default: errors.throwErr(errors.ERR_INVALID_PACKET); } }; } module.exports = { ConnectPacket, DataPacket, AcceptPacket, RefusePacket, RedirectPacket, MarkerPacket, ControlPacket };