oracledb 6.9.0 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/LICENSE.txt +1 -1
  2. package/NOTICE.txt +1 -1
  3. package/README.md +9 -9
  4. package/build/Release/oracledb-7.0.0-darwin-arm64.node +0 -0
  5. package/build/Release/oracledb-7.0.0-darwin-arm64.node-buildinfo.txt +1 -0
  6. package/build/Release/oracledb-7.0.0-js-buildinfo.txt +1 -0
  7. package/build/Release/oracledb-7.0.0-linux-arm64.node +0 -0
  8. package/build/Release/oracledb-7.0.0-linux-arm64.node-buildinfo.txt +1 -0
  9. package/build/Release/oracledb-7.0.0-linux-x64.node +0 -0
  10. package/build/Release/oracledb-7.0.0-linux-x64.node-buildinfo.txt +1 -0
  11. package/build/Release/oracledb-7.0.0-win32-x64.node +0 -0
  12. package/build/Release/oracledb-7.0.0-win32-x64.node-buildinfo.txt +1 -0
  13. package/eslint.config.js +39 -0
  14. package/lib/aqDeqOptions.js +18 -1
  15. package/lib/aqQueue.js +10 -6
  16. package/lib/connection.js +418 -39
  17. package/lib/dbObject.js +25 -4
  18. package/lib/endUserSecurityContext.js +176 -0
  19. package/lib/errors.js +99 -8
  20. package/lib/impl/aqDeqOptions.js +10 -1
  21. package/lib/impl/connection.js +76 -2
  22. package/lib/impl/datahandlers/buffer.js +68 -5
  23. package/lib/impl/datahandlers/oson.js +7 -2
  24. package/lib/impl/datahandlers/vector.js +25 -5
  25. package/lib/impl/lob.js +9 -0
  26. package/lib/impl/parserHelpers.js +443 -0
  27. package/lib/impl/resultset.js +1 -1
  28. package/lib/impl/sodaDocument.js +5 -14
  29. package/lib/lob.js +27 -14
  30. package/lib/obfuscation.js +97 -0
  31. package/lib/oracledb.js +163 -49
  32. package/lib/parser.js +204 -0
  33. package/lib/pipeline.js +113 -0
  34. package/lib/pool.js +203 -147
  35. package/lib/poolStatistics.js +1 -1
  36. package/lib/resultset.js +42 -21
  37. package/lib/settings.js +1 -0
  38. package/lib/sodaCollection.js +12 -8
  39. package/lib/sodaDatabase.js +35 -8
  40. package/lib/sodaDocument.js +24 -6
  41. package/lib/sodaOperation.js +7 -6
  42. package/lib/thin/aq.js +475 -0
  43. package/lib/thin/connection.js +518 -54
  44. package/lib/thin/dbObject.js +7 -1
  45. package/lib/thin/lob.js +20 -1
  46. package/lib/thin/pool.js +42 -62
  47. package/lib/thin/protocol/capabilities.js +21 -4
  48. package/lib/thin/protocol/constants.js +143 -2
  49. package/lib/thin/protocol/encryptDecrypt.js +45 -10
  50. package/lib/thin/protocol/messages/aqArray.js +238 -0
  51. package/lib/thin/protocol/messages/aqBase.js +305 -0
  52. package/lib/thin/protocol/messages/aqDeq.js +164 -0
  53. package/lib/thin/protocol/messages/aqEnq.js +135 -0
  54. package/lib/thin/protocol/messages/auth.js +18 -28
  55. package/lib/thin/protocol/messages/base.js +245 -4
  56. package/lib/thin/protocol/messages/dataType.js +8 -3
  57. package/lib/thin/protocol/messages/directPathLoadStream.js +377 -0
  58. package/lib/thin/protocol/messages/directPathOp.js +65 -0
  59. package/lib/thin/protocol/messages/directPathPrepare.js +158 -0
  60. package/lib/thin/protocol/messages/endPipeline.js +56 -0
  61. package/lib/thin/protocol/messages/execute.js +9 -7
  62. package/lib/thin/protocol/messages/index.js +9 -1
  63. package/lib/thin/protocol/messages/protocol.js +2 -2
  64. package/lib/thin/protocol/messages/withData.js +10 -142
  65. package/lib/thin/protocol/packet.js +120 -28
  66. package/lib/thin/protocol/protocol.js +110 -25
  67. package/lib/thin/protocol/utils.js +1 -26
  68. package/lib/thin/resultSet.js +13 -19
  69. package/lib/thin/sqlnet/ezConnectResolver.js +2 -2
  70. package/lib/thin/sqlnet/networkSession.js +63 -41
  71. package/lib/thin/sqlnet/ntTcp.js +24 -0
  72. package/lib/thin/sqlnet/nvStrToNvPair.js +50 -25
  73. package/lib/thin/sqlnet/packet.js +25 -4
  74. package/lib/thin/sqlnet/paramParser.js +10 -7
  75. package/lib/thin/statementCache.js +2 -4
  76. package/lib/traceHandler.js +59 -2
  77. package/lib/transformer.js +8 -3
  78. package/lib/types.js +36 -22
  79. package/lib/util.js +6 -19
  80. package/lib/version.js +3 -3
  81. package/package/install.js +5 -5
  82. package/package/prunebinaries.js +1 -1
  83. package/package.json +1 -1
  84. package/plugins/configProviders/awsCommon.js +125 -0
  85. package/plugins/configProviders/awss3/index.js +138 -0
  86. package/plugins/configProviders/awssecretsmanager/index.js +147 -0
  87. package/plugins/configProviders/azure/index.js +7 -5
  88. package/plugins/token/extensionAzure/index.js +0 -1
  89. package/plugins/token/extensionOci/index.js +0 -1
  90. package/build/Release/oracledb-6.9.0-darwin-arm64.node +0 -0
  91. package/build/Release/oracledb-6.9.0-darwin-arm64.node-buildinfo.txt +0 -1
  92. package/build/Release/oracledb-6.9.0-darwin-x64.node +0 -0
  93. package/build/Release/oracledb-6.9.0-darwin-x64.node-buildinfo.txt +0 -1
  94. package/build/Release/oracledb-6.9.0-js-buildinfo.txt +0 -1
  95. package/build/Release/oracledb-6.9.0-linux-arm64.node +0 -0
  96. package/build/Release/oracledb-6.9.0-linux-arm64.node-buildinfo.txt +0 -1
  97. package/build/Release/oracledb-6.9.0-linux-x64.node +0 -0
  98. package/build/Release/oracledb-6.9.0-linux-x64.node-buildinfo.txt +0 -1
  99. package/build/Release/oracledb-6.9.0-win32-x64.node +0 -0
  100. package/build/Release/oracledb-6.9.0-win32-x64.node-buildinfo.txt +0 -1
@@ -1,4 +1,4 @@
1
- // Copyright (c) 2022, 2025, Oracle and/or its affiliates.
1
+ // Copyright (c) 2022, 2026, Oracle and/or its affiliates.
2
2
 
3
3
  //-----------------------------------------------------------------------------
4
4
  //
@@ -69,7 +69,7 @@ class Protocol {
69
69
  async _decodeMessage(message) {
70
70
  message.preProcess();
71
71
  await this.readBuf.waitForPackets(true);
72
- while (true) { // eslint-disable-line
72
+ while (true) {
73
73
  if (this.nsi.isBreak) {
74
74
  await this.resetMessage();
75
75
  delete this.readBuf.savedPackets;
@@ -89,7 +89,31 @@ class Protocol {
89
89
  throw (err);
90
90
  }
91
91
  }
92
- await message.postProcess();
92
+ }
93
+
94
+ async _processPipelineMessage(messageObj, connImpl, continueOnError) {
95
+ let opProcessed = false;
96
+ const { message } = messageObj;
97
+ await this.readBuf.waitForPackets(true);
98
+ try {
99
+ message.preProcess();
100
+ message.decode(this.readBuf);
101
+ opProcessed = true;
102
+ this._checkAndRaiseException(message);
103
+ } catch (err) {
104
+ if (!opProcessed && !this.connInProgress &&
105
+ err.code !== errors.ERR_CONNECTION_CLOSED_CODE) {
106
+ this.readBuf.resetPackets();
107
+ }
108
+ if (messageObj.statement)
109
+ connImpl._returnStatement(messageObj.statement);
110
+ if (!continueOnError) {
111
+ err.opProcessed = opProcessed;
112
+ throw (err);
113
+ }
114
+ messageObj.pipelineResultImpl.error = err;
115
+ }
116
+ return opProcessed;
93
117
  }
94
118
 
95
119
  /**
@@ -112,6 +136,59 @@ class Protocol {
112
136
  this.writeBuf.endRequest();
113
137
  }
114
138
 
139
+ _raiseException(message) {
140
+ let err = new Error(message.errorInfo.message);
141
+ err.offset = message.errorInfo.pos;
142
+ err.errorNum = message.errorInfo.num;
143
+ err.isRecoverable = message.errorInfo.isRecoverable;
144
+ err = errors.transformErr(err);
145
+ if (err.code === errors.ERR_CONNECTION_CLOSED_CODE) {
146
+ this.nsi.disconnect();
147
+ }
148
+ throw err;
149
+ }
150
+
151
+ _checkAndRaiseException(message) {
152
+ if (message.errorOccurred) {
153
+ this._raiseException(message);
154
+ }
155
+ }
156
+ /**
157
+ * Called when all messages for the pipeline have been sent to the
158
+ * database. An end pipeline message is sent to the database and then
159
+ * the responses to all of the messages are processed.
160
+ */
161
+ async endPipeline(messagesForPipeline, endMessage, connImpl,
162
+ continueOnError) {
163
+ await this._encodeMessage(endMessage);
164
+
165
+ // required for cleanup of response packets
166
+ let numResponsesToDiscard = messagesForPipeline.length + 1;
167
+ try {
168
+ for (const messageObj of messagesForPipeline) {
169
+ const opProcessed = await this._processPipelineMessage(
170
+ messageObj, connImpl, continueOnError);
171
+ if (opProcessed)
172
+ numResponsesToDiscard--;
173
+ }
174
+
175
+ // Process the final message
176
+ await this.readBuf.waitForPackets(true);
177
+ endMessage.process(this.readBuf);
178
+ this._checkAndRaiseException(endMessage);
179
+ numResponsesToDiscard = 0;
180
+
181
+ } catch (err) {
182
+
183
+ // If an operation response was already consumed before failure, account
184
+ // for it before discarding the remaining pipeline responses.
185
+ if (err.opProcessed)
186
+ numResponsesToDiscard--;
187
+ await this.readBuf.discardPipelineResponses(numResponsesToDiscard);
188
+ throw (err);
189
+ }
190
+ }
191
+
115
192
  async _recoverFromError(caughtErr, message) {
116
193
  /*
117
194
  * We have NJS error(protocol related) detected during packet write/read
@@ -119,11 +196,16 @@ class Protocol {
119
196
  * response as ORA-1013 from the server.
120
197
  */
121
198
  try {
122
- this.breakMessage();
123
- this._breakInProgress = false;
124
- await this.resetMessage();
125
- await this.readBuf.waitForPackets();
126
- message.decode(this.readBuf);
199
+ if (message.isPipeline) {
200
+ // We just cleanup all pending response packets
201
+ this.readBuf.resetPackets();
202
+ } else {
203
+ this.breakMessage();
204
+ this._breakInProgress = false;
205
+ await this.resetMessage();
206
+ await this.readBuf.waitForPackets();
207
+ message.decode(this.readBuf);
208
+ }
127
209
  } catch (err) { // Recovery failed
128
210
  this.nsi.disconnect();
129
211
  const newErr = errors.getErr(errors.ERR_CONNECTION_CLOSED);
@@ -134,6 +216,18 @@ class Protocol {
134
216
  }
135
217
  }
136
218
 
219
+ async _processCallStatus(message) {
220
+ if (message.flushOutBinds) {
221
+ await this.flushOutBindMessage(message);
222
+ }
223
+ this.txnInProgress = Boolean(message.callStatus &
224
+ constants.TNS_EOCS_FLAGS_TXN_IN_PROGRESS);
225
+ // processes the call status flags returned by the server.
226
+ if (message.callStatus & constants.TNS_EOCS_FLAGS_SESS_RELEASE) {
227
+ message.connection.statementCache.clearCursors();
228
+ }
229
+ }
230
+
137
231
  /**
138
232
  *
139
233
  * @param {object} message The RPC dynamic structure specific to the RPC
@@ -159,6 +253,11 @@ class Protocol {
159
253
  if (traceEnabled) {
160
254
  traceContext.operation = `oracledb.${message.constructor.name}`;
161
255
  traceInstance.onBeginRoundTrip(traceContext);
256
+ if (traceContext.userContext?.traceParent) {
257
+ // The traceParent is set from instrumentation module.
258
+ message.connection.appContext('CLIENTCONTEXT',
259
+ [{ ora$opentelem$tracectx: traceContext.userContext.traceParent }]);
260
+ }
162
261
  }
163
262
  await this._encodeMessage(message);
164
263
  if (message.messageType !== constants.TNS_MSG_TYPE_ONEWAY_FN) {
@@ -184,14 +283,7 @@ class Protocol {
184
283
  traceInstance.onEndRoundTrip(traceContext);
185
284
  }
186
285
  }
187
- if (message.flushOutBinds) {
188
- await this.flushOutBindMessage(message);
189
- }
190
- this.txnInProgress = Boolean(message.callStatus & constants.TNS_EOCS_FLAGS_TXN_IN_PROGRESS);
191
- // processes the call status flags returned by the server.
192
- if (message.callStatus & constants.TNS_EOCS_FLAGS_SESS_RELEASE) {
193
- message.connection.statementCache.clearCursors();
194
- }
286
+ await this._processCallStatus(message);
195
287
 
196
288
  if (message.errorOccurred) {
197
289
  if (callTimeoutExpired) {
@@ -201,16 +293,9 @@ class Protocol {
201
293
  message.errorOccurred = false;
202
294
  return await this._processMessage(message);
203
295
  }
204
- let err = new Error(message.errorInfo.message);
205
- err.offset = message.errorInfo.pos;
206
- err.errorNum = message.errorInfo.num;
207
- err.isRecoverable = message.errorInfo.isRecoverable;
208
- err = errors.transformErr(err);
209
- if (err.code === errors.ERR_CONNECTION_CLOSED_CODE) {
210
- this.nsi.disconnect();
211
- }
212
- throw err;
296
+ this._raiseException(message);
213
297
  }
298
+ await message.postProcess();
214
299
  }
215
300
 
216
301
  async flushOutBindMessage(message) {
@@ -28,7 +28,6 @@
28
28
 
29
29
  const { Buffer } = require('buffer');
30
30
  const constants = require('./constants');
31
- const crypto = require('crypto');
32
31
 
33
32
  class OutOfPacketsError extends Error { }
34
33
 
@@ -52,31 +51,7 @@ function encodeRowID(rowID) {
52
51
  }
53
52
  }
54
53
 
55
- // obfuscate value
56
- function setObfuscatedValue(value) {
57
- const valueBytes = Buffer.from(value);
58
- const obfuscatedBytes = crypto.randomBytes(valueBytes.length);
59
- for (let i = 0; i < valueBytes.length; i++) {
60
- valueBytes[i] = obfuscatedBytes[i] ^ valueBytes[i];
61
- }
62
- return {obfuscatedValue: obfuscatedBytes, value: valueBytes};
63
- }
64
-
65
- // returns the deobfuscated value, after removing the obfuscation
66
- // and clear memory of temporary Buffers coming from Buffer pool
67
- function getDeobfuscatedValue(valueBytes, obfuscatedBytes) {
68
- const buf = Buffer.from(valueBytes);
69
- for (let i = 0; i < valueBytes.length; i++) {
70
- buf[i] = valueBytes[i] ^ obfuscatedBytes[i];
71
- }
72
- const retVal = buf.toString();
73
- buf.fill(0);
74
- return retVal;
75
- }
76
-
77
54
  module.exports = {
78
55
  encodeRowID,
79
- getDeobfuscatedValue,
80
- OutOfPacketsError,
81
- setObfuscatedValue
56
+ OutOfPacketsError
82
57
  };
@@ -1,4 +1,4 @@
1
- // Copyright (c) 2022, 2023, Oracle and/or its affiliates.
1
+ // Copyright (c) 2022, 2026, Oracle and/or its affiliates.
2
2
 
3
3
  //-----------------------------------------------------------------------------
4
4
  //
@@ -94,26 +94,20 @@ class ThinResultSetImpl extends ResultSetImpl {
94
94
  async getRows(numRows, options) {
95
95
  options.fetchArraySize = numRows || this.options.fetchArraySize;
96
96
  options.prefetchRows = this.options.prefetchRows;
97
- if (this.statement.bufferRowCount - this.statement.bufferRowIndex >= options.fetchArraySize) {
97
+ if (this.statement.bufferRowCount - this.statement.bufferRowIndex >= options.fetchArraySize)
98
98
  return this._processRows(options.fetchArraySize);
99
- } else {
100
- // We fetch for the required number of row
101
- options.fetchArraySize = options.fetchArraySize - (this.statement.bufferRowCount - this.statement.bufferRowIndex);
102
- const prevBufferRowCount = this.statement.bufferRowCount;
103
- if (this.statement.moreRowsToFetch && options.fetchArraySize > 0) {
104
- await this._fetchMoreRows(options);
105
- }
106
- options.fetchArraySize = numRows || this.options.fetchArraySize;
107
- if (prevBufferRowCount === this.statement.bufferRowCount) {
108
- const numRowsFetched = this.statement.bufferRowCount - this.statement.bufferRowIndex;
109
- this.statement.bufferRowCount = 0;
110
- if (numRowsFetched > 0) {
111
- return this._processRows(numRowsFetched);
112
- }
113
- return [];
114
- }
115
- }
99
+
100
+ // We fetch for the required number of rows
101
+ options.fetchArraySize = options.fetchArraySize - (this.statement.bufferRowCount - this.statement.bufferRowIndex);
102
+ const prevBufferRowCount = this.statement.bufferRowCount;
103
+ if (this.statement.moreRowsToFetch && options.fetchArraySize > 0)
104
+ await this._fetchMoreRows(options);
105
+ options.fetchArraySize = numRows || this.options.fetchArraySize;
116
106
  const numRowsFetched = this.statement.bufferRowCount - this.statement.bufferRowIndex;
107
+ if (prevBufferRowCount === this.statement.bufferRowCount)
108
+ this.statement.bufferRowCount = 0;
109
+
110
+ // process the rows fetched
117
111
  return this._processRows(numRowsFetched);
118
112
  }
119
113
 
@@ -294,7 +294,7 @@ class EZConnectResolver {
294
294
  if (serverDNMatch != null)
295
295
  securityInfo.push(`(SSL_SERVER_DN_MATCH=${serverDNMatch})`);
296
296
  if (serverCertDN != null)
297
- securityInfo.push(`(SSL_SERVER_CERT_DN=${serverCertDN}})`);
297
+ securityInfo.push(`(SSL_SERVER_CERT_DN=${serverCertDN})`);
298
298
  if (walletDir != null)
299
299
  securityInfo.push(`(MY_WALLET_DIRECTORY=${walletDir})`);
300
300
  }
@@ -416,7 +416,7 @@ class EZConnectResolver {
416
416
  // if it is an URL alias then add to URL props
417
417
  this.urlProps.set(propertyName, value);
418
418
  } else {
419
- this.connectionProps.set(propertyName, value);
419
+ this.connectionProps.set(key, value);
420
420
  }
421
421
  }
422
422
 
@@ -1,4 +1,4 @@
1
- // Copyright (c) 2022, 2025, Oracle and/or its affiliates.
1
+ // Copyright (c) 2022, 2026, Oracle and/or its affiliates.
2
2
 
3
3
  //-----------------------------------------------------------------------------
4
4
  //
@@ -34,11 +34,27 @@ const { createNode } = require('./connStrategy.js');
34
34
  const errors = require("../../errors.js");
35
35
  const { findNVPairRecurse, createNVPair, findValue } = require("./nvStrToNvPair.js");
36
36
  const { Buffer } = require('buffer');
37
- const EzConnect = require("./ezConnectResolver.js");
38
- const { NLParamParser, tnsnamesFilePath } = require("./paramParser.js");
39
- const process = require('process');
40
37
  const downHostInstance = require("./connStrategy.js").SOLE_INST_DHCACHE;
41
38
  const {ANO} = require("./ANO.js");
39
+ const { resolveConnectStr } = require('../../impl/parserHelpers.js');
40
+
41
+ const MAX_REDIRECTS = 2;
42
+
43
+ const REDIRECT_SECURITY_KEYS = new Set([
44
+ 'sslServerDNMatch',
45
+ 'sslAllowWeakDNMatch',
46
+ 'sslServerCertDN',
47
+ 'walletLocation',
48
+ 'walletPassword',
49
+ 'walletContent'
50
+ ]);
51
+
52
+ function getRedirectParams(params) {
53
+ return Object.fromEntries(
54
+ Object.entries(params).filter(([key]) => !REDIRECT_SECURITY_KEYS.has(key))
55
+ );
56
+ }
57
+
42
58
  /**
43
59
  *
44
60
  * @param {string} userConfig
@@ -68,36 +84,6 @@ async function getConnectionInfo(userConfig) {
68
84
  * @param {String} connStr Connect string
69
85
  * @returns AddressNode
70
86
  */
71
- const nlParamParser = new NLParamParser;
72
- async function resolveConnectStr(connectString, configDir) {
73
- const connStr = connectString.trim();
74
- let resolvedVal = connStr;
75
- if ((connStr.indexOf(')') == -1) || (connStr.indexOf('(') != 0)) {
76
- if ((connStr.indexOf(':') != -1) || (connStr.indexOf('/') != -1)) {
77
- const ezcnObj = new EzConnect(connStr);
78
- resolvedVal = ezcnObj.getResolvedUrl();
79
- return resolvedVal;
80
- } else {
81
- //try tns alias
82
- const namesFilePath = tnsnamesFilePath(configDir);
83
- const p = await nlParamParser.initializeNlpa(namesFilePath);
84
- resolvedVal = p.get(connStr.toUpperCase());
85
- if (!resolvedVal)
86
- errors.throwErr(errors.ERR_TNS_ENTRY_NOT_FOUND, connStr, configDir ? configDir + '/tnsnames.ora' : process.env.TNS_ADMIN + '/tnsnames.ora');
87
- if (resolvedVal.rhsType == 1) {
88
- const rString = resolvedVal.atom;
89
- if ((rString.indexOf(':') != -1) || (rString.indexOf('/') != -1)) {
90
- return new EzConnect(rString).getResolvedUrl();
91
- }
92
- }
93
- resolvedVal = resolvedVal.getListElement(0);
94
- }
95
-
96
- }
97
- return resolvedVal;
98
-
99
- }
100
-
101
87
  async function resolveAddress(connStr, userConfig) {
102
88
  const connstr = await resolveConnectStr(connStr, userConfig.configDir);
103
89
  return createNode(connstr, userConfig);
@@ -228,6 +214,8 @@ class NetworkSession {
228
214
  let connectPkt = new Packet.ConnectPacket(this.cData, this.sAtts);
229
215
  this._sendConnect(connectPkt);
230
216
 
217
+ let redirectCount = 0;
218
+
231
219
  /* Read the response */
232
220
  while (true) { // eslint-disable-line
233
221
  const packet = await this._recvPacket();
@@ -257,6 +245,11 @@ class NetworkSession {
257
245
  }
258
246
  this._sendConnect(connectPkt);
259
247
  } else if (packet.type === constants.NSPTRD) { /* REDIRECT */
248
+ if (redirectCount >= MAX_REDIRECTS) {
249
+ errors.throwErr(errors.ERR_TOO_MANY_REDIRECTS, MAX_REDIRECTS);
250
+ }
251
+ redirectCount++;
252
+
260
253
  let adrLen, adrStr, redirConnData;
261
254
 
262
255
  /* Read and connect to Redirect address */
@@ -277,8 +270,10 @@ class NetworkSession {
277
270
  const redirAddressNode = await resolveAddress(adrStr, userConfig);
278
271
  const host = address.hostname;
279
272
  address = await redirAddressNode.execute();
280
- if (address.desc)
281
- this.sAtts.setFrom(address.desc.params); /* Add on attributes from redirect connect String */
273
+ /* Add on attributes from redirect connect String */
274
+ if (address.desc?.params) {
275
+ this.sAtts.setFrom(getRedirectParams(address.desc.params));
276
+ }
282
277
  address.originHost = host;
283
278
  this.redirectPkt = null;
284
279
  this.ntAdapter.disconnect(constants.NSFIMM);
@@ -298,7 +293,6 @@ class NetworkSession {
298
293
 
299
294
  /* Accepted */
300
295
 
301
- this.connected = true;
302
296
  this.cData = null;
303
297
  this.sndDatapkt = new Packet.DataPacket(this.sAtts.largeSDU);
304
298
  this.markerPkt = new Packet.MarkerPacket(this.sAtts.largeSDU);
@@ -319,6 +313,7 @@ class NetworkSession {
319
313
  this.sndDatapkt.createPacket(constants.NSPDADAT); //Currently only used for disconnect
320
314
  this.sndDatapkt.offset = this.sndDatapkt.dataPtr;
321
315
  this.sndDatapkt.len = this.sndDatapkt.bufLen;
316
+ this.connected = true;
322
317
  return (true);
323
318
  }
324
319
 
@@ -349,7 +344,7 @@ class NetworkSession {
349
344
  savedErr = err;
350
345
  try {
351
346
  address = await this.getAddress(addressNode, userConfig);
352
- } catch (err) {
347
+ } catch {
353
348
  break;
354
349
  }
355
350
  }
@@ -370,20 +365,39 @@ class NetworkSession {
370
365
  const size = packet.buf.length;
371
366
  const dataFlags = packet.buf.readUInt16BE(constants.NSPDAFLG);
372
367
  if ((dataFlags & constants.NSPDAFCMP) != 0) { // compression enabled
368
+ if (!this.sAtts.networkCompressionEnabled) {
369
+ errors.throwErr(errors.ERR_INVALID_PACKET, "Compressed packet received without negotiation");
370
+ }
373
371
  const packetHeader = packet.buf.subarray(0, constants.NSPDADAT);
374
372
  const buffertoDeCompress = packet.buf.subarray(constants.NSPDADAT, size);
375
373
  let deCompressedDataBuffer;
374
+
375
+ const maxPayload = this.sAtts.largeSDU
376
+ ? Math.min(this.sAtts.sdu, constants.NSPABSSDULN)
377
+ : Math.min(this.sAtts.sdu, constants.NSPMXSDULN);
378
+
379
+ const inflateOptions = {
380
+ finishFlush: zlib.constants.Z_SYNC_FLUSH,
381
+ maxOutputLength: maxPayload
382
+ };
376
383
  try {
377
384
  if (this.sAtts.firstRecvCompressedPacket) {
378
- deCompressedDataBuffer = zlib.inflateSync(buffertoDeCompress, {finishFlush: zlib.constants.Z_SYNC_FLUSH});
385
+ deCompressedDataBuffer = zlib.inflateSync(buffertoDeCompress, inflateOptions);
379
386
  this.sAtts.firstRecvCompressedPacket = 0;
380
387
  } else {
381
- deCompressedDataBuffer = zlib.inflateRawSync(buffertoDeCompress, {finishFlush: zlib.constants.Z_SYNC_FLUSH});
388
+ deCompressedDataBuffer = zlib.inflateRawSync(buffertoDeCompress, inflateOptions);
382
389
  }
383
390
  } catch (err) {
391
+ if (err.code == 'ERR_BUFFER_TOO_LARGE') {
392
+ errors.throwErr(errors.ERR_INVALID_PACKET,
393
+ `: decompressed packet exceeds SDU ${maxPayload}`);
394
+ }
384
395
  errors.throwErr(errors.ERR_DATA_COMPRESSION, err.message);
385
396
  }
386
397
  const resultLength = deCompressedDataBuffer.length;
398
+ if (resultLength > maxPayload) {
399
+ errors.throwErr(errors.ERR_INVALID_PACKET, `Decompressed size ${resultLength} exceeds SDU ${maxPayload}`);
400
+ }
387
401
  //concatenate packet header with decompressed data
388
402
  packet.buf = Buffer.concat([packetHeader, deCompressedDataBuffer]);
389
403
  const length = resultLength + constants.NSPDADAT;
@@ -407,6 +421,7 @@ class NetworkSession {
407
421
  if (this.sAtts.version >= constants.TNS_VERSION_MIN_END_OF_RESPONSE
408
422
  && (packet.flags & constants.TNS_ACCEPT_FLAG_HAS_END_OF_REQUEST)) {
409
423
  this.endOfRequestSupport = true;
424
+ this.pipeliningSupport = true;
410
425
  }
411
426
  if (packet.flags & constants.TNS_ACCEPT_FLAG_FAST_AUTH) {
412
427
  this.supportsFastAuth = true;
@@ -430,7 +445,7 @@ class NetworkSession {
430
445
  * Control packets are consumed internally and discarded
431
446
  */
432
447
  async _recvPacket() {
433
- while (true) { // eslint-disable-line
448
+ while (true) {
434
449
  const packet = await this.ntAdapter.receive();
435
450
  if (!packet)
436
451
  break;
@@ -501,6 +516,9 @@ class NetworkSession {
501
516
  this.isBreak = true;
502
517
  this.markerPkt.prepare(constants.NSPMKTD1, constants.NIQIMARK);
503
518
  this.sendPacket(this.markerPkt.buf);
519
+ if (this.pipelineEnabled) {
520
+ this.isBreak = false;
521
+ }
504
522
  }
505
523
 
506
524
  /**
@@ -513,6 +531,10 @@ class NetworkSession {
513
531
  this.sendPacket(this.markerPkt.buf);
514
532
  this.breakPosted = false;
515
533
  }
534
+ if (this.pipelineEnabled) {
535
+ this.isBreak = this.isReset = false;
536
+ return;
537
+ }
516
538
  /* Send Reset */
517
539
  this.markerPkt.prepare(constants.NSPMKTD1, constants.NIQRMARK);
518
540
  this.sendPacket(this.markerPkt.buf);
@@ -389,6 +389,19 @@ class NTTCP {
389
389
  this.numPacketsSinceLastWait = 0;
390
390
  }
391
391
 
392
+ /**
393
+ * Reject invalid packet and clean up
394
+ * @param {string} message Error message
395
+ * @returns {null} to clear tempBuf
396
+ */
397
+ rejectInvalidPacket(message) {
398
+ this.savedErr = new Error(message);
399
+ this.err = true;
400
+ this.stream.destroy();
401
+ this._notifyWaiters();
402
+ return null;
403
+ }
404
+
392
405
  /**
393
406
  * Start Async reads
394
407
  */
@@ -414,6 +427,17 @@ class NTTCP {
414
427
  len = tempBuf.readUInt16BE();
415
428
  }
416
429
 
430
+ if (len < PACKET_HEADER_SIZE) {
431
+ tempBuf = this.rejectInvalidPacket(`Invalid packet length: ${len} (must be at least ${PACKET_HEADER_SIZE})`);
432
+ break;
433
+ }
434
+
435
+ const maxPacket = this.largeSDU ? constants.NSPABSSDULN : constants.NSPMXSDULN;
436
+ if (len > maxPacket) {
437
+ tempBuf = this.rejectInvalidPacket(`Invalid packet length: ${len} (exceeds maximum ${maxPacket})`);
438
+ break;
439
+ }
440
+
417
441
  // not enough for a full packet so wait for more data to arrive
418
442
  if (len > tempBuf.length)
419
443
  break;
@@ -1,4 +1,4 @@
1
- // Copyright (c) 2022, 2023, Oracle and/or its affiliates.
1
+ // Copyright (c) 2022, 2026, Oracle and/or its affiliates.
2
2
 
3
3
  //-----------------------------------------------------------------------------
4
4
  //
@@ -28,6 +28,26 @@
28
28
 
29
29
  const errors = require("../../errors.js");
30
30
 
31
+ // Maximum supported descriptor byte length.
32
+ const DEFAULT_MAX_DESCRIPTOR_LENGTH = 512 * 8;
33
+
34
+ // Maximum supported descriptor nesting depth before rejecting the parse.
35
+ const DEFAULT_MAX_DESCRIPTOR_DEPTH = 64;
36
+
37
+ function checkLength(nvString, maxLength) {
38
+ if (Buffer.byteLength(nvString, 'utf8') > maxLength) {
39
+ errors.throwErr(errors.ERR_CONNECT_DESCRIPTOR_LIMIT_EXCEEDED,
40
+ 'byte length', maxLength);
41
+ }
42
+ }
43
+
44
+ function checkDepth(depth, maxDepth) {
45
+ if (depth > maxDepth) {
46
+ errors.throwErr(errors.ERR_CONNECT_DESCRIPTOR_LIMIT_EXCEEDED,
47
+ 'nesting depth', maxDepth);
48
+ }
49
+ }
50
+
31
51
 
32
52
  /**
33
53
  * Constant which implies that the VALUE has not been set to ATOM or LIST.
@@ -556,10 +576,13 @@ class NVTokens {
556
576
  * Returns an NVPair which contains the broken-down form of nvString
557
577
  * @param nvString the nvString to parse
558
578
  */
559
- function createNVPair(nvString) {
579
+ function createNVPair(nvString, options = {}) {
580
+ const maxLength = options.maxLength ?? DEFAULT_MAX_DESCRIPTOR_LENGTH;
581
+ const maxDepth = options.maxDepth ?? DEFAULT_MAX_DESCRIPTOR_DEPTH;
582
+ checkLength(nvString, maxLength);
560
583
  const nvt = new NVTokens();
561
584
  nvt.parseTokens(nvString);
562
- return readTopLevelNVPair(nvt);
585
+ return readTopLevelNVPair(nvt, 1, maxDepth);
563
586
  }
564
587
 
565
588
  /*
@@ -567,7 +590,8 @@ function createNVPair(nvString) {
567
590
  * NVPair: (name=value)
568
591
  * value: atom | NVList
569
592
  */
570
- function readTopLevelNVPair(nvt) {
593
+ function readTopLevelNVPair(nvt, depth, maxDepth) {
594
+ checkDepth(depth, maxDepth);
571
595
  //check for opening (
572
596
  let tk = nvt.getToken();
573
597
  nvt.eatToken();
@@ -586,17 +610,18 @@ function readTopLevelNVPair(nvt) {
586
610
  }
587
611
  nvp.name = name;
588
612
 
589
- return readRightHandSide(nvp, nvt);
613
+ return readRightHandSide(nvp, nvt, depth, maxDepth);
590
614
  }
591
615
 
592
- return readRightHandSide(nvp, nvt);
616
+ return readRightHandSide(nvp, nvt, depth, maxDepth);
593
617
  }
594
618
  /*
595
619
  * function which returns the next NVPair from NVTokens.
596
620
  * NVPair: (name=value) | (name, | ,name) | ,name,
597
621
  * value: atom | NVList
598
622
  */
599
- function readNVPair(nvt) {
623
+ function readNVPair(nvt, depth, maxDepth) {
624
+ checkDepth(depth, maxDepth);
600
625
  // Opening ( or , for NVPair
601
626
  const tk = nvt.getToken();
602
627
  nvt.eatToken();
@@ -607,7 +632,7 @@ function readNVPair(nvt) {
607
632
  const name = readNVLiteral(nvt);
608
633
  const nvp = new NVPair(name);
609
634
 
610
- return readRightHandSide(nvp, nvt);
635
+ return readRightHandSide(nvp, nvt, depth, maxDepth);
611
636
  }
612
637
 
613
638
  /*
@@ -615,7 +640,7 @@ function readNVPair(nvt) {
615
640
  * NVPair: (name=value)
616
641
  * value: atom | NVList
617
642
  */
618
- function readRightHandSide(nvp, nvt) {
643
+ function readRightHandSide(nvp, nvt, depth, maxDepth) {
619
644
  let tk = nvt.getToken();
620
645
  switch (tk) {
621
646
  case TKN_EQUAL:
@@ -630,7 +655,7 @@ function readRightHandSide(nvp, nvt) {
630
655
  } else {
631
656
  // NVList is responsible for adding child NVPairs to this parent
632
657
  // NVPair.
633
- readNVList(nvt, nvp);
658
+ readNVList(nvt, nvp, depth, maxDepth);
634
659
  }
635
660
  break;
636
661
 
@@ -671,24 +696,24 @@ function readNVLiteral(nvt) {
671
696
  * function which adds a list of NVPairs to a parent NVPair.
672
697
  * NVList: NVPair NVList | epsilon
673
698
  */
674
- function readNVList(nvt, parent) {
675
- // if next token is "(" or ",", then read an NVPair
676
- // otherwise, assume epsilon
677
- const tk = nvt.getToken();
678
- if (!(tk == TKN_LPAREN || tk == TKN_COMMA)) {
679
- return; // didn't read an nvpair
680
- }
699
+ function readNVList(nvt, parent, depth, maxDepth) {
700
+ while (true) {
701
+ // if next token is "(" or ",", then read an NVPair
702
+ // otherwise, assume epsilon
703
+ const tk = nvt.getToken();
704
+ if (!(tk == TKN_LPAREN || tk == TKN_COMMA)) {
705
+ return; // didn't read an nvpair
706
+ }
681
707
 
682
- const child = readNVPair(nvt);
708
+ const child = readNVPair(nvt, depth + 1, maxDepth);
683
709
 
684
- // read a good NVPair
685
- parent.addListElement(child);
686
- if ((tk == TKN_COMMA) || (child.name == child.atom)) {
687
- if (parent.getListType != LIST_COMMASEP) // if not already set
688
- parent.setListType = LIST_COMMASEP; // set it
710
+ // read a good NVPair
711
+ parent.addListElement(child);
712
+ if ((tk == TKN_COMMA) || (child.name == child.atom)) {
713
+ if (parent.getListType != LIST_COMMASEP) // if not already set
714
+ parent.setListType = LIST_COMMASEP; // set it
715
+ }
689
716
  }
690
-
691
- readNVList(nvt, parent); // next iteration of NVList()
692
717
  }
693
718
 
694
719
  /**