node-firebird 2.3.1 → 2.3.3
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.
- package/README.md +218 -31
- package/lib/index.d.ts +22 -0
- package/lib/pool.js +97 -10
- package/lib/srp.js +12 -1
- package/lib/wire/connection.js +40 -7
- package/lib/wire/socket.js +13 -1
- package/lib/wire/xsqlvar.js +69 -6
- package/package.json +1 -1
- package/poc/README.md +160 -0
- package/poc/helpers.js +59 -0
- package/poc/node_modules/.package-lock.json +14 -0
- package/poc/node_modules/node-firebird/.eslintrc.json +12 -0
- package/poc/node_modules/node-firebird/.github/workflows/codeql.yml +76 -0
- package/poc/node_modules/node-firebird/.github/workflows/node.js.yml +95 -0
- package/poc/node_modules/node-firebird/BIGINT_MIGRATION.md +374 -0
- package/poc/node_modules/node-firebird/CI_DEBUGGING_GUIDE.md +148 -0
- package/poc/node_modules/node-firebird/ENCRYPTION_CALLBACK.md +152 -0
- package/poc/node_modules/node-firebird/FIREBIRD_LOG_FEATURE.md +145 -0
- package/poc/node_modules/node-firebird/LICENSE +373 -0
- package/poc/node_modules/node-firebird/MINIMAL_CHANGES_SUMMARY.md +136 -0
- package/poc/node_modules/node-firebird/PR_SUMMARY.md +96 -0
- package/poc/node_modules/node-firebird/README.md +794 -0
- package/poc/node_modules/node-firebird/ROADMAP.md +223 -0
- package/poc/node_modules/node-firebird/SRP_PROTOCOL.md +482 -0
- package/poc/node_modules/node-firebird/lib/callback.js +38 -0
- package/poc/node_modules/node-firebird/lib/firebird.msg +0 -0
- package/poc/node_modules/node-firebird/lib/firebird.msg.json +1371 -0
- package/poc/node_modules/node-firebird/lib/gdscodes.d.ts +1524 -0
- package/poc/node_modules/node-firebird/lib/gdscodes.js +1531 -0
- package/poc/node_modules/node-firebird/lib/ieee754-decimal.js +500 -0
- package/poc/node_modules/node-firebird/lib/index.d.ts +316 -0
- package/poc/node_modules/node-firebird/lib/index.js +128 -0
- package/poc/node_modules/node-firebird/lib/messages.js +162 -0
- package/poc/node_modules/node-firebird/lib/pool.js +108 -0
- package/poc/node_modules/node-firebird/lib/srp.js +299 -0
- package/poc/node_modules/node-firebird/lib/unix-crypt.js +343 -0
- package/poc/node_modules/node-firebird/lib/utils.js +164 -0
- package/poc/node_modules/node-firebird/lib/wire/connection.js +2510 -0
- package/poc/node_modules/node-firebird/lib/wire/const.js +807 -0
- package/poc/node_modules/node-firebird/lib/wire/database.js +378 -0
- package/poc/node_modules/node-firebird/lib/wire/eventConnection.js +118 -0
- package/poc/node_modules/node-firebird/lib/wire/fbEventManager.js +326 -0
- package/poc/node_modules/node-firebird/lib/wire/serialize.js +588 -0
- package/poc/node_modules/node-firebird/lib/wire/service.js +1058 -0
- package/poc/node_modules/node-firebird/lib/wire/socket.js +175 -0
- package/poc/node_modules/node-firebird/lib/wire/statement.js +48 -0
- package/poc/node_modules/node-firebird/lib/wire/transaction.js +206 -0
- package/poc/node_modules/node-firebird/lib/wire/xsqlvar.js +703 -0
- package/poc/node_modules/node-firebird/package.json +38 -0
- package/poc/node_modules/node-firebird/vitest.config.js +24 -0
- package/poc/package-lock.json +21 -0
- package/poc/package.json +12 -0
- package/poc/reproduce-fixed.js +150 -0
- package/poc/reproduce.js +133 -0
- package/vitest.config.js +4 -1
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
const net = require("net");
|
|
2
|
+
const zlib = require("zlib");
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Arc4 stream cipher for Firebird wire encryption.
|
|
6
|
+
* Uses the SRP session key to create RC4 encryption/decryption streams.
|
|
7
|
+
*/
|
|
8
|
+
class Arc4 {
|
|
9
|
+
constructor(key) {
|
|
10
|
+
this._s = new Uint8Array(256);
|
|
11
|
+
this._i = 0;
|
|
12
|
+
this._j = 0;
|
|
13
|
+
|
|
14
|
+
// KSA (Key-Scheduling Algorithm)
|
|
15
|
+
for (var i = 0; i < 256; i++) {
|
|
16
|
+
this._s[i] = i;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
var j = 0;
|
|
20
|
+
for (var i = 0; i < 256; i++) {
|
|
21
|
+
j = (j + this._s[i] + key[i % key.length]) & 0xFF;
|
|
22
|
+
// Swap
|
|
23
|
+
var tmp = this._s[i];
|
|
24
|
+
this._s[i] = this._s[j];
|
|
25
|
+
this._s[j] = tmp;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Transform (encrypt/decrypt) data in place.
|
|
31
|
+
* RC4 is symmetric - encrypt and decrypt are the same operation.
|
|
32
|
+
*/
|
|
33
|
+
transform(data) {
|
|
34
|
+
var out = Buffer.alloc(data.length);
|
|
35
|
+
for (var n = 0; n < data.length; n++) {
|
|
36
|
+
this._i = (this._i + 1) & 0xFF;
|
|
37
|
+
this._j = (this._j + this._s[this._i]) & 0xFF;
|
|
38
|
+
|
|
39
|
+
// Swap
|
|
40
|
+
var tmp = this._s[this._i];
|
|
41
|
+
this._s[this._i] = this._s[this._j];
|
|
42
|
+
this._s[this._j] = tmp;
|
|
43
|
+
|
|
44
|
+
var k = this._s[(this._s[this._i] + this._s[this._j]) & 0xFF];
|
|
45
|
+
out[n] = data[n] ^ k;
|
|
46
|
+
}
|
|
47
|
+
return out;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Socket proxy.
|
|
53
|
+
*/
|
|
54
|
+
class Socket {
|
|
55
|
+
constructor(port, host) {
|
|
56
|
+
this._socket = net.createConnection(port, host);
|
|
57
|
+
this._socket.setNoDelay(true);
|
|
58
|
+
this.compressor = null;
|
|
59
|
+
this.compressorBuffer = [];
|
|
60
|
+
this.decompressor = null;
|
|
61
|
+
this.decompressorBuffer = [];
|
|
62
|
+
this.buffer = null;
|
|
63
|
+
this.encrypt = false;
|
|
64
|
+
this.encryptCipher = null;
|
|
65
|
+
this.decryptCipher = null;
|
|
66
|
+
|
|
67
|
+
return new Proxy(this._socket, this);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Decompress and/or decrypt data when received.
|
|
72
|
+
* Override on data event.
|
|
73
|
+
*/
|
|
74
|
+
on(event, cb) {
|
|
75
|
+
if (event === 'data') {
|
|
76
|
+
const mainCb = cb;
|
|
77
|
+
cb = (data) => {
|
|
78
|
+
// Decrypt first if encryption is enabled
|
|
79
|
+
if (this.encrypt) {
|
|
80
|
+
data = this.decryptCipher.transform(data);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (this.compress) {
|
|
84
|
+
this.decompressor.write(data, () => {
|
|
85
|
+
mainCb(Buffer.concat(this.decompressorBuffer));
|
|
86
|
+
this.decompressorBuffer = []; // Reset buffer
|
|
87
|
+
});
|
|
88
|
+
} else {
|
|
89
|
+
mainCb(data);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
this._socket.on(event, cb);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Compress and/or encrypt data before sending to socket.
|
|
99
|
+
*/
|
|
100
|
+
write(data, defer = false) {
|
|
101
|
+
if (defer) {
|
|
102
|
+
this.buffer = Buffer.from(data);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (!defer && this.buffer) {
|
|
107
|
+
data = Buffer.concat([this.buffer, data]);
|
|
108
|
+
this.buffer = null;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (this.compress) {
|
|
112
|
+
this.compressor.write(data, () => {
|
|
113
|
+
var compressedData = Buffer.concat(this.compressorBuffer);
|
|
114
|
+
this.compressorBuffer = []; // Reset buffer
|
|
115
|
+
|
|
116
|
+
// Encrypt after compression if encryption is enabled
|
|
117
|
+
if (this.encrypt) {
|
|
118
|
+
compressedData = this.encryptCipher.transform(compressedData);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
this._socket.write(compressedData);
|
|
122
|
+
});
|
|
123
|
+
} else if (this.encrypt) {
|
|
124
|
+
this._socket.write(this.encryptCipher.transform(data));
|
|
125
|
+
} else {
|
|
126
|
+
this._socket.write(data);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Enable compression/decompression on the fly.
|
|
132
|
+
*/
|
|
133
|
+
enableCompression() {
|
|
134
|
+
this.compress = true;
|
|
135
|
+
|
|
136
|
+
// Create decompressor instance
|
|
137
|
+
this.decompressor = zlib.createInflate();
|
|
138
|
+
this.decompressor.on('data', (inflate) => {
|
|
139
|
+
this.decompressorBuffer.push(inflate);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// Create compressor instance
|
|
143
|
+
this.compressor = zlib.createDeflate({
|
|
144
|
+
flush: zlib.constants.Z_FULL_FLUSH,
|
|
145
|
+
finishFlush: zlib.constants.Z_SYNC_FLUSH
|
|
146
|
+
});
|
|
147
|
+
this.compressor.on('data', (deflate) => {
|
|
148
|
+
this.compressorBuffer.push(deflate);
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Enable encryption/decryption using Arc4 cipher.
|
|
154
|
+
* @param {Buffer} sessionKey - The session key from SRP authentication.
|
|
155
|
+
*/
|
|
156
|
+
enableEncryption(sessionKey) {
|
|
157
|
+
this.encrypt = true;
|
|
158
|
+
this.encryptCipher = new Arc4(sessionKey);
|
|
159
|
+
this.decryptCipher = new Arc4(sessionKey);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Proxy trap.
|
|
164
|
+
*/
|
|
165
|
+
get(target, field) {
|
|
166
|
+
if (field in this) {
|
|
167
|
+
return this[field].bind(this);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return target[field];
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
module.exports = Socket;
|
|
175
|
+
module.exports.Arc4 = Arc4;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/***************************************
|
|
2
|
+
*
|
|
3
|
+
* Statement
|
|
4
|
+
*
|
|
5
|
+
***************************************/
|
|
6
|
+
|
|
7
|
+
class Statement {
|
|
8
|
+
constructor(connection) {
|
|
9
|
+
this.connection = connection;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
close(callback) {
|
|
13
|
+
this.connection.closeStatement(this, callback);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
drop(callback) {
|
|
17
|
+
this.connection.dropStatement(this, callback);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
release(callback) {
|
|
21
|
+
var cache_query = this.connection.getCachedQuery(this.query);
|
|
22
|
+
if (cache_query)
|
|
23
|
+
this.connection.closeStatement(this, callback);
|
|
24
|
+
else
|
|
25
|
+
this.connection.dropStatement(this, callback);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
execute(transaction, params, callback, options) {
|
|
29
|
+
if (params instanceof Function) {
|
|
30
|
+
options = callback;
|
|
31
|
+
callback = params;
|
|
32
|
+
params = undefined;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
this.options = options;
|
|
36
|
+
this.connection.executeStatement(transaction, this, params, callback, options);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
fetch(transaction, count, callback) {
|
|
40
|
+
this.connection.fetch(this, transaction, count, callback);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
fetchAll(transaction, callback) {
|
|
44
|
+
this.connection.fetchAll(this, transaction, callback);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
module.exports = Statement;
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
const {doCallback, doError} = require('../callback');
|
|
2
|
+
const Const = require('./const');
|
|
3
|
+
|
|
4
|
+
/***************************************
|
|
5
|
+
*
|
|
6
|
+
* Transaction
|
|
7
|
+
*
|
|
8
|
+
***************************************/
|
|
9
|
+
|
|
10
|
+
class Transaction {
|
|
11
|
+
constructor(connection) {
|
|
12
|
+
this.connection = connection;
|
|
13
|
+
this.db = connection.db;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
newStatement(query, callback) {
|
|
17
|
+
var cnx = this.connection;
|
|
18
|
+
var self = this;
|
|
19
|
+
var query_cache = cnx.getCachedQuery(query);
|
|
20
|
+
|
|
21
|
+
if (query_cache) {
|
|
22
|
+
callback(null, query_cache);
|
|
23
|
+
} else {
|
|
24
|
+
cnx.prepare(self, query, false, callback);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
execute(query, params, callback, options) {
|
|
29
|
+
if (params instanceof Function) {
|
|
30
|
+
options = callback;
|
|
31
|
+
callback = params;
|
|
32
|
+
params = undefined;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
var self = this;
|
|
36
|
+
this.newStatement(query, function(err, statement) {
|
|
37
|
+
|
|
38
|
+
if (err) {
|
|
39
|
+
doError(err, callback);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function dropError(err) {
|
|
44
|
+
statement.release();
|
|
45
|
+
doCallback(err, callback);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
statement.execute(self, params, function(err, ret) {
|
|
49
|
+
if (err) {
|
|
50
|
+
dropError(err);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
switch (statement.type) {
|
|
55
|
+
case Const.isc_info_sql_stmt_select:
|
|
56
|
+
statement.fetchAll(self, function(err, r) {
|
|
57
|
+
if (err) {
|
|
58
|
+
dropError(err);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
statement.release();
|
|
63
|
+
|
|
64
|
+
if (callback)
|
|
65
|
+
callback(undefined, r, statement.output, true);
|
|
66
|
+
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
break;
|
|
70
|
+
|
|
71
|
+
case Const.isc_info_sql_stmt_exec_procedure:
|
|
72
|
+
if (ret && ret.data && ret.data.length > 0) {
|
|
73
|
+
statement.release();
|
|
74
|
+
|
|
75
|
+
if (callback)
|
|
76
|
+
callback(undefined, ret.data[0], statement.output, true);
|
|
77
|
+
|
|
78
|
+
break;
|
|
79
|
+
} else if (statement.output.length) {
|
|
80
|
+
statement.fetch(self, 1, function(err, ret) {
|
|
81
|
+
if (err) {
|
|
82
|
+
dropError(err);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
statement.release();
|
|
87
|
+
|
|
88
|
+
if (callback)
|
|
89
|
+
callback(undefined, ret.data[0], statement.output, false);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Fall through is normal
|
|
96
|
+
default:
|
|
97
|
+
statement.release();
|
|
98
|
+
if (callback)
|
|
99
|
+
callback()
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
}, options);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
sequentially(query, params, on, callback, options = {}) {
|
|
108
|
+
if (params instanceof Function) {
|
|
109
|
+
options = callback;
|
|
110
|
+
callback = on;
|
|
111
|
+
on = params;
|
|
112
|
+
params = undefined;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (on === undefined){
|
|
116
|
+
throw new Error('Expected "on" delegate.');
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (callback instanceof Boolean) {
|
|
120
|
+
options = callback;
|
|
121
|
+
callback = undefined;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
var self = this;
|
|
125
|
+
var _on = function(row, i, meta, next) {
|
|
126
|
+
var done = false;
|
|
127
|
+
var finish = function(err) {
|
|
128
|
+
if (done) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
done = true;
|
|
132
|
+
next(err);
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
var ret;
|
|
137
|
+
if (on.length >= 3) {
|
|
138
|
+
ret = on(row, i, finish);
|
|
139
|
+
} else {
|
|
140
|
+
ret = on(row, i);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (ret && typeof ret.then === 'function') {
|
|
144
|
+
ret.then(function() {
|
|
145
|
+
finish();
|
|
146
|
+
}).catch(finish);
|
|
147
|
+
} else if (on.length < 3) {
|
|
148
|
+
finish();
|
|
149
|
+
}
|
|
150
|
+
} catch (err) {
|
|
151
|
+
finish(err);
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// back compatibility - options parameter is a boolean
|
|
156
|
+
if (typeof options === 'boolean') {
|
|
157
|
+
options = { asObject: !options, asStream: true, on: _on };
|
|
158
|
+
} else {
|
|
159
|
+
options = {
|
|
160
|
+
asStream: true,
|
|
161
|
+
asObject: true,
|
|
162
|
+
on: _on,
|
|
163
|
+
...options,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
self.execute(query, params, callback, options);
|
|
168
|
+
return self;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
query(query, params, callback, options = {}) {
|
|
172
|
+
if (params instanceof Function) {
|
|
173
|
+
callback = params;
|
|
174
|
+
params = undefined;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (callback === undefined)
|
|
178
|
+
callback = noop;
|
|
179
|
+
|
|
180
|
+
options = {
|
|
181
|
+
asObject: true,
|
|
182
|
+
asStream: callback === undefined || callback === null,
|
|
183
|
+
...options,
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
this.execute(query, params, callback, options);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
commit(callback) {
|
|
190
|
+
this.connection.commit(this, callback);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
rollback(callback) {
|
|
194
|
+
this.connection.rollback(this, callback);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
commitRetaining(callback) {
|
|
198
|
+
this.connection.commitRetaining(this, callback);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
rollbackRetaining(callback) {
|
|
202
|
+
this.connection.rollbackRetaining(this, callback);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
module.exports = Transaction;
|