node-firebird 1.1.10 → 2.0.1
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/.github/workflows/node.js.yml +45 -12
- package/0001-fix-attachEvent-host-resolution.patch +72 -0
- package/ENCRYPTION_CALLBACK.md +152 -0
- package/PR_SUMMARY.md +139 -0
- package/README.md +59 -25
- package/Roadmap.md +13 -13
- package/diff +62 -0
- package/lib/index.d.ts +5 -1
- package/lib/index.js +1 -1
- package/lib/pool.js +83 -82
- package/lib/srp.js +3 -0
- package/lib/wire/connection.js +1623 -1366
- package/lib/wire/const.js +11 -5
- package/lib/wire/database.js +229 -118
- package/lib/wire/eventConnection.js +94 -92
- package/lib/wire/fbEventManager.js +107 -72
- package/lib/wire/serialize.js +434 -408
- package/lib/wire/service.js +935 -931
- package/lib/wire/socket.js +78 -3
- package/lib/wire/statement.js +32 -31
- package/lib/wire/transaction.js +135 -107
- package/lib/wire/xsqlvar.js +339 -330
- package/package.json +4 -4
- package/vitest.config.js +13 -0
package/lib/wire/socket.js
CHANGED
|
@@ -1,6 +1,53 @@
|
|
|
1
1
|
const net = require("net");
|
|
2
2
|
const zlib = require("zlib");
|
|
3
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
|
+
|
|
4
51
|
/**
|
|
5
52
|
* Socket proxy.
|
|
6
53
|
*/
|
|
@@ -13,18 +60,26 @@ class Socket {
|
|
|
13
60
|
this.decompressor = null;
|
|
14
61
|
this.decompressorBuffer = [];
|
|
15
62
|
this.buffer = null;
|
|
63
|
+
this.encrypt = false;
|
|
64
|
+
this.encryptCipher = null;
|
|
65
|
+
this.decryptCipher = null;
|
|
16
66
|
|
|
17
67
|
return new Proxy(this._socket, this);
|
|
18
68
|
}
|
|
19
69
|
|
|
20
70
|
/**
|
|
21
|
-
* Decompress data when
|
|
71
|
+
* Decompress and/or decrypt data when received.
|
|
22
72
|
* Override on data event.
|
|
23
73
|
*/
|
|
24
74
|
on(event, cb) {
|
|
25
75
|
if (event === 'data') {
|
|
26
76
|
const mainCb = cb;
|
|
27
77
|
cb = (data) => {
|
|
78
|
+
// Decrypt first if encryption is enabled
|
|
79
|
+
if (this.encrypt) {
|
|
80
|
+
data = this.decryptCipher.transform(data);
|
|
81
|
+
}
|
|
82
|
+
|
|
28
83
|
if (this.compress) {
|
|
29
84
|
this.decompressor.write(data, () => {
|
|
30
85
|
mainCb(Buffer.concat(this.decompressorBuffer));
|
|
@@ -40,7 +95,7 @@ class Socket {
|
|
|
40
95
|
}
|
|
41
96
|
|
|
42
97
|
/**
|
|
43
|
-
* Compress data before sending to socket
|
|
98
|
+
* Compress and/or encrypt data before sending to socket.
|
|
44
99
|
*/
|
|
45
100
|
write(data, defer = false) {
|
|
46
101
|
if (defer) {
|
|
@@ -55,9 +110,18 @@ class Socket {
|
|
|
55
110
|
|
|
56
111
|
if (this.compress) {
|
|
57
112
|
this.compressor.write(data, () => {
|
|
58
|
-
|
|
113
|
+
var compressedData = Buffer.concat(this.compressorBuffer);
|
|
59
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);
|
|
60
122
|
});
|
|
123
|
+
} else if (this.encrypt) {
|
|
124
|
+
this._socket.write(this.encryptCipher.transform(data));
|
|
61
125
|
} else {
|
|
62
126
|
this._socket.write(data);
|
|
63
127
|
}
|
|
@@ -85,6 +149,16 @@ class Socket {
|
|
|
85
149
|
});
|
|
86
150
|
}
|
|
87
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
|
+
|
|
88
162
|
/**
|
|
89
163
|
* Proxy trap.
|
|
90
164
|
*/
|
|
@@ -98,3 +172,4 @@ class Socket {
|
|
|
98
172
|
}
|
|
99
173
|
|
|
100
174
|
module.exports = Socket;
|
|
175
|
+
module.exports.Arc4 = Arc4;
|
package/lib/wire/statement.js
CHANGED
|
@@ -4,44 +4,45 @@
|
|
|
4
4
|
*
|
|
5
5
|
***************************************/
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
Statement.prototype.close = function(callback) {
|
|
12
|
-
this.connection.closeStatement(this, callback);
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
Statement.prototype.drop = function(callback) {
|
|
16
|
-
this.connection.dropStatement(this, callback);
|
|
17
|
-
};
|
|
7
|
+
class Statement {
|
|
8
|
+
constructor(connection) {
|
|
9
|
+
this.connection = connection;
|
|
10
|
+
}
|
|
18
11
|
|
|
19
|
-
|
|
20
|
-
var cache_query = this.connection.getCachedQuery(this.query);
|
|
21
|
-
if (cache_query)
|
|
12
|
+
close(callback) {
|
|
22
13
|
this.connection.closeStatement(this, callback);
|
|
23
|
-
|
|
24
|
-
this.connection.dropStatement(this, callback);
|
|
25
|
-
};
|
|
14
|
+
}
|
|
26
15
|
|
|
27
|
-
|
|
16
|
+
drop(callback) {
|
|
17
|
+
this.connection.dropStatement(this, callback);
|
|
18
|
+
}
|
|
28
19
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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);
|
|
33
26
|
}
|
|
34
27
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
28
|
+
execute(transaction, params, callback, custom) {
|
|
29
|
+
if (params instanceof Function) {
|
|
30
|
+
custom = callback;
|
|
31
|
+
callback = params;
|
|
32
|
+
params = undefined;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
this.custom = custom;
|
|
36
|
+
this.connection.executeStatement(transaction, this, params, callback, custom);
|
|
37
|
+
}
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
39
|
+
fetch(transaction, count, callback) {
|
|
40
|
+
this.connection.fetch(this, transaction, count, callback);
|
|
41
|
+
}
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
43
|
+
fetchAll(transaction, callback) {
|
|
44
|
+
this.connection.fetchAll(this, transaction, callback);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
46
47
|
|
|
47
48
|
module.exports = Statement;
|
package/lib/wire/transaction.js
CHANGED
|
@@ -7,77 +7,53 @@ const Const = require('./const');
|
|
|
7
7
|
*
|
|
8
8
|
***************************************/
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
Transaction.prototype.newStatement = function(query, callback) {
|
|
16
|
-
var cnx = this.connection;
|
|
17
|
-
var self = this;
|
|
18
|
-
var query_cache = cnx.getCachedQuery(query);
|
|
19
|
-
|
|
20
|
-
if (query_cache) {
|
|
21
|
-
callback(null, query_cache);
|
|
22
|
-
} else {
|
|
23
|
-
cnx.prepare(self, query, false, callback);
|
|
10
|
+
class Transaction {
|
|
11
|
+
constructor(connection) {
|
|
12
|
+
this.connection = connection;
|
|
13
|
+
this.db = connection.db;
|
|
24
14
|
}
|
|
25
|
-
};
|
|
26
15
|
|
|
27
|
-
|
|
16
|
+
newStatement(query, callback) {
|
|
17
|
+
var cnx = this.connection;
|
|
18
|
+
var self = this;
|
|
19
|
+
var query_cache = cnx.getCachedQuery(query);
|
|
28
20
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
21
|
+
if (query_cache) {
|
|
22
|
+
callback(null, query_cache);
|
|
23
|
+
} else {
|
|
24
|
+
cnx.prepare(self, query, false, callback);
|
|
25
|
+
}
|
|
33
26
|
}
|
|
34
27
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
return;
|
|
28
|
+
execute(query, params, callback, custom) {
|
|
29
|
+
if (params instanceof Function) {
|
|
30
|
+
custom = callback;
|
|
31
|
+
callback = params;
|
|
32
|
+
params = undefined;
|
|
41
33
|
}
|
|
42
34
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
doCallback(err, callback);
|
|
46
|
-
}
|
|
35
|
+
var self = this;
|
|
36
|
+
this.newStatement(query, function(err, statement) {
|
|
47
37
|
|
|
48
|
-
statement.execute(self, params, function(err, ret) {
|
|
49
38
|
if (err) {
|
|
50
|
-
|
|
39
|
+
doError(err, callback);
|
|
51
40
|
return;
|
|
52
41
|
}
|
|
53
42
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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();
|
|
43
|
+
function dropError(err) {
|
|
44
|
+
statement.release();
|
|
45
|
+
doCallback(err, callback);
|
|
46
|
+
}
|
|
74
47
|
|
|
75
|
-
|
|
76
|
-
|
|
48
|
+
statement.execute(self, params, function(err, ret) {
|
|
49
|
+
if (err) {
|
|
50
|
+
dropError(err);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
77
53
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
statement.
|
|
54
|
+
switch (statement.type) {
|
|
55
|
+
case Const.isc_info_sql_stmt_select:
|
|
56
|
+
statement.fetchAll(self, function(err, r) {
|
|
81
57
|
if (err) {
|
|
82
58
|
dropError(err);
|
|
83
59
|
return;
|
|
@@ -86,75 +62,127 @@ Transaction.prototype.execute = function(query, params, callback, custom) {
|
|
|
86
62
|
statement.release();
|
|
87
63
|
|
|
88
64
|
if (callback)
|
|
89
|
-
callback(undefined,
|
|
65
|
+
callback(undefined, r, statement.output, true);
|
|
66
|
+
|
|
90
67
|
});
|
|
91
68
|
|
|
92
69
|
break;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Fall through is normal
|
|
96
|
-
default:
|
|
97
|
-
statement.release();
|
|
98
|
-
if (callback)
|
|
99
|
-
callback()
|
|
100
|
-
break;
|
|
101
|
-
}
|
|
102
70
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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);
|
|
106
77
|
|
|
107
|
-
|
|
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
|
+
}
|
|
108
85
|
|
|
109
|
-
|
|
110
|
-
asArray = callback;
|
|
111
|
-
callback = on;
|
|
112
|
-
on = params;
|
|
113
|
-
params = undefined;
|
|
114
|
-
}
|
|
86
|
+
statement.release();
|
|
115
87
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
88
|
+
if (callback)
|
|
89
|
+
callback(undefined, ret.data[0], statement.output, false);
|
|
90
|
+
});
|
|
119
91
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Fall through is normal
|
|
96
|
+
default:
|
|
97
|
+
statement.release();
|
|
98
|
+
if (callback)
|
|
99
|
+
callback()
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
}, custom);
|
|
104
|
+
});
|
|
123
105
|
}
|
|
124
106
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
107
|
+
sequentially(query, params, on, callback, asArray) {
|
|
108
|
+
if (params instanceof Function) {
|
|
109
|
+
asArray = 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
|
+
asArray = callback;
|
|
121
|
+
callback = undefined;
|
|
122
|
+
}
|
|
129
123
|
|
|
130
|
-
|
|
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
|
+
};
|
|
131
154
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
params = undefined;
|
|
155
|
+
self.execute(query, params, callback, { asObject: !asArray, asStream: true, on: _on });
|
|
156
|
+
return self;
|
|
135
157
|
}
|
|
136
158
|
|
|
137
|
-
|
|
138
|
-
|
|
159
|
+
query(query, params, callback) {
|
|
160
|
+
if (params instanceof Function) {
|
|
161
|
+
callback = params;
|
|
162
|
+
params = undefined;
|
|
163
|
+
}
|
|
139
164
|
|
|
140
|
-
|
|
165
|
+
if (callback === undefined)
|
|
166
|
+
callback = noop;
|
|
141
167
|
|
|
142
|
-
};
|
|
168
|
+
this.execute(query, params, callback, { asObject: true, asStream: callback === undefined || callback === null });
|
|
169
|
+
}
|
|
143
170
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}
|
|
171
|
+
commit(callback) {
|
|
172
|
+
this.connection.commit(this, callback);
|
|
173
|
+
}
|
|
147
174
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
175
|
+
rollback(callback) {
|
|
176
|
+
this.connection.rollback(this, callback);
|
|
177
|
+
}
|
|
151
178
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
179
|
+
commitRetaining(callback) {
|
|
180
|
+
this.connection.commitRetaining(this, callback);
|
|
181
|
+
}
|
|
155
182
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}
|
|
183
|
+
rollbackRetaining(callback) {
|
|
184
|
+
this.connection.rollbackRetaining(this, callback);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
159
187
|
|
|
160
188
|
module.exports = Transaction;
|