mysql2 2.3.3 → 3.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.
- package/README.md +6 -2
- package/index.d.ts +23 -9
- package/index.js +2 -0
- package/lib/auth_plugins/index.js +8 -0
- package/lib/auth_plugins/mysql_clear_password.js +17 -0
- package/lib/auth_plugins/sha256_password.js +2 -2
- package/lib/commands/auth_switch.js +3 -1
- package/lib/commands/change_user.js +19 -0
- package/lib/commands/client_handshake.js +40 -1
- package/lib/commands/prepare.js +2 -1
- package/lib/commands/quit.js +3 -3
- package/lib/commands/server_handshake.js +36 -1
- package/lib/connection.js +56 -11
- package/lib/connection_config.js +20 -3
- package/lib/constants/charset_encodings.js +4 -1
- package/lib/constants/charsets.js +41 -13
- package/lib/constants/client.js +8 -0
- package/lib/constants/ssl_profiles.js +274 -0
- package/lib/constants/types.js +60 -28
- package/lib/helpers.js +3 -1
- package/lib/packets/auth_next_factor.js +35 -0
- package/lib/packets/binary_row.js +49 -2
- package/lib/packets/column_definition.js +2 -0
- package/lib/packets/execute.js +62 -0
- package/lib/packets/index.js +6 -0
- package/lib/parsers/parser_cache.js +1 -1
- package/lib/parsers/text_parser.js +11 -4
- package/lib/pool.js +1 -1
- package/package.json +21 -14
- package/promise.d.ts +22 -2
- package/promise.js +12 -2
- package/typings/mysql/index.d.ts +43 -4
- package/typings/mysql/lib/Connection.d.ts +52 -8
- package/typings/mysql/lib/Pool.d.ts +4 -2
- package/typings/mysql/lib/PoolCluster.d.ts +3 -3
- package/typings/mysql/lib/PoolConnection.d.ts +1 -0
- package/typings/mysql/lib/Server.d.ts +13 -0
- package/typings/mysql/lib/protocol/packets/index.d.ts +5 -1
- package/typings/mysql/lib/protocol/packets/params/ErrorPacketParams.d.ts +6 -0
- package/typings/mysql/lib/protocol/packets/params/OkPacketParams.d.ts +9 -0
- package/typings/mysql/lib/protocol/sequences/Prepare.d.ts +52 -0
- package/typings/mysql/lib/protocol/sequences/Query.d.ts +12 -2
package/README.md
CHANGED
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
[![Node.js Version][node-version-image]][node-version-url]
|
|
7
7
|
[![Linux Build][travis-image]][travis-url]
|
|
8
8
|
[![Windows Build][appveyor-image]][appveyor-url]
|
|
9
|
-
[![License][license-image]][license-url]
|
|
9
|
+
[![License][license-image]][license-url]
|
|
10
|
+
|
|
11
|
+
English | [简体中文](./documentation_zh-cn/)
|
|
10
12
|
|
|
11
13
|
> MySQL client for Node.js with focus on performance. Supports prepared statements, non-utf8 encodings, binary log protocol, compression, ssl [much more](https://github.com/sidorares/node-mysql2/tree/master/documentation)
|
|
12
14
|
|
|
@@ -246,7 +248,9 @@ con.query({ sql: 'select 1 as foo, 2 as foo', rowsAsArray: true }, function(err,
|
|
|
246
248
|
|
|
247
249
|
MySQL2 is mostly API compatible with [Node MySQL][node-mysql]. You should check their API documentation to see all available API options.
|
|
248
250
|
|
|
249
|
-
|
|
251
|
+
One known incompatibility is that `DECIMAL` values are returned as strings whereas in [Node MySQL][node-mysql] they are returned as numbers. This includes the result of `SUM()` and `AVG()` functions when applied to `INTEGER` arguments. This is done deliberately to avoid loss of precision - see https://github.com/sidorares/node-mysql2/issues/935.
|
|
252
|
+
|
|
253
|
+
If you find any other incompatibility with [Node MySQL][node-mysql], Please report via Issue tracker. We will fix reported incompatibility on priority basis.
|
|
250
254
|
|
|
251
255
|
## Documentation
|
|
252
256
|
|
package/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Connection as PromiseConnection,
|
|
3
3
|
Pool as PromisePool,
|
|
4
|
-
PoolConnection as PromisePoolConnection
|
|
4
|
+
PoolConnection as PromisePoolConnection,
|
|
5
5
|
} from './promise';
|
|
6
6
|
|
|
7
7
|
import * as mysql from './typings/mysql';
|
|
@@ -72,6 +72,15 @@ export interface Connection extends mysql.Connection {
|
|
|
72
72
|
): mysql.Query;
|
|
73
73
|
ping(callback?: (err: mysql.QueryError | null) => any): void;
|
|
74
74
|
promise(promiseImpl?: PromiseConstructor): PromiseConnection;
|
|
75
|
+
unprepare(sql: string): mysql.PrepareStatementInfo;
|
|
76
|
+
prepare(sql: string, callback?: (err: mysql.QueryError | null, statement: mysql.PrepareStatementInfo) => any): mysql.Prepare;
|
|
77
|
+
serverHandshake(args: any): any;
|
|
78
|
+
writeOk(args?: mysql.OkPacketParams): void;
|
|
79
|
+
writeError(args?: mysql.ErrorPacketParams): void;
|
|
80
|
+
writeEof(warnings?: number, statusFlags?: number): void;
|
|
81
|
+
writeTextResult(rows?: Array<any>, columns?: Array<any>): void;
|
|
82
|
+
writePacket(packet: any): void;
|
|
83
|
+
sequenceId: number;
|
|
75
84
|
}
|
|
76
85
|
|
|
77
86
|
export interface PoolConnection extends mysql.PoolConnection, Connection {
|
|
@@ -149,14 +158,11 @@ export interface Pool extends mysql.Connection {
|
|
|
149
158
|
on(event: 'release', listener: (connection: PoolConnection) => any): this;
|
|
150
159
|
on(event: 'enqueue', listener: () => any): this;
|
|
151
160
|
promise(promiseImpl?: PromiseConstructor): PromisePool;
|
|
152
|
-
|
|
161
|
+
unprepare(sql: string): mysql.PrepareStatementInfo;
|
|
162
|
+
prepare(sql: string, callback?: (err: mysql.QueryError | null, statement: mysql.PrepareStatementInfo) => any): mysql.Prepare;
|
|
153
163
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
command: string;
|
|
157
|
-
}) => (
|
|
158
|
-
pluginData: Buffer
|
|
159
|
-
) => Promise<string> | string | Buffer | Promise<Buffer> | null;
|
|
164
|
+
config: mysql.PoolOptions;
|
|
165
|
+
}
|
|
160
166
|
|
|
161
167
|
export interface ConnectionOptions extends mysql.ConnectionOptions {
|
|
162
168
|
charsetNumber?: number;
|
|
@@ -178,10 +184,18 @@ export interface ConnectionOptions extends mysql.ConnectionOptions {
|
|
|
178
184
|
queueLimit?: number;
|
|
179
185
|
waitForConnections?: boolean;
|
|
180
186
|
authPlugins?: {
|
|
181
|
-
[key: string]:
|
|
187
|
+
[key: string]: mysql.AuthPlugin;
|
|
182
188
|
};
|
|
183
189
|
}
|
|
184
190
|
|
|
191
|
+
export interface ConnectionConfig extends ConnectionOptions {
|
|
192
|
+
mergeFlags(defaultFlags: string[], userFlags: string[] | string): number;
|
|
193
|
+
getDefaultFlags(options?: ConnectionOptions): string[];
|
|
194
|
+
getCharsetNumber(charset: string): number;
|
|
195
|
+
getSSLProfile(name: string): { ca: string[] };
|
|
196
|
+
parseUrl(url: string): { host: string, port: number, database: string, user: string, password: string, [key: string]: any };
|
|
197
|
+
}
|
|
198
|
+
|
|
185
199
|
export interface PoolOptions extends mysql.PoolOptions, ConnectionOptions {}
|
|
186
200
|
|
|
187
201
|
export function createConnection(connectionUri: string): Connection;
|
package/index.js
CHANGED
|
@@ -12,6 +12,7 @@ exports.createConnection = function(opts) {
|
|
|
12
12
|
|
|
13
13
|
exports.connect = exports.createConnection;
|
|
14
14
|
exports.Connection = Connection;
|
|
15
|
+
exports.ConnectionConfig = ConnectionConfig;
|
|
15
16
|
|
|
16
17
|
const Pool = require('./lib/pool.js');
|
|
17
18
|
const PoolCluster = require('./lib/pool_cluster.js');
|
|
@@ -42,6 +43,7 @@ exports.createServer = function(handler) {
|
|
|
42
43
|
};
|
|
43
44
|
|
|
44
45
|
exports.PoolConnection = require('./lib/pool_connection');
|
|
46
|
+
exports.authPlugins = require('./lib/auth_plugins');
|
|
45
47
|
exports.escape = SqlString.escape;
|
|
46
48
|
exports.escapeId = SqlString.escapeId;
|
|
47
49
|
exports.format = SqlString.format;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
caching_sha2_password: require('./caching_sha2_password'),
|
|
5
|
+
mysql_clear_password: require('./mysql_clear_password'),
|
|
6
|
+
mysql_native_password: require('./mysql_native_password'),
|
|
7
|
+
sha256_password: require('./sha256_password'),
|
|
8
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function bufferFromStr(str) {
|
|
4
|
+
return Buffer.from(`${str}\0`);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const create_mysql_clear_password_plugin = pluginOptions =>
|
|
8
|
+
function mysql_clear_password_plugin({ connection, command }) {
|
|
9
|
+
const password =
|
|
10
|
+
command.password || pluginOptions.password || connection.config.password;
|
|
11
|
+
|
|
12
|
+
return function (/* pluginData */) {
|
|
13
|
+
return bufferFromStr(password);
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
module.exports = create_mysql_clear_password_plugin;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const PLUGIN_NAME = 'sha256_password';
|
|
4
4
|
const crypto = require('crypto');
|
|
5
|
-
const {
|
|
5
|
+
const { xorRotating } = require('../auth_41');
|
|
6
6
|
|
|
7
7
|
const REQUEST_SERVER_KEY_PACKET = Buffer.from([1]);
|
|
8
8
|
|
|
@@ -11,7 +11,7 @@ const STATE_WAIT_SERVER_KEY = 1;
|
|
|
11
11
|
const STATE_FINAL = -1;
|
|
12
12
|
|
|
13
13
|
function encrypt(password, scramble, key) {
|
|
14
|
-
const stage1 =
|
|
14
|
+
const stage1 = xorRotating(
|
|
15
15
|
Buffer.from(`${password}\0`, 'utf8').toString('binary'),
|
|
16
16
|
scramble.toString('binary')
|
|
17
17
|
);
|
|
@@ -9,11 +9,13 @@ const Packets = require('../packets/index.js');
|
|
|
9
9
|
const sha256_password = require('../auth_plugins/sha256_password');
|
|
10
10
|
const caching_sha2_password = require('../auth_plugins/caching_sha2_password.js');
|
|
11
11
|
const mysql_native_password = require('../auth_plugins/mysql_native_password.js');
|
|
12
|
+
const mysql_clear_password = require('../auth_plugins/mysql_clear_password.js');
|
|
12
13
|
|
|
13
14
|
const standardAuthPlugins = {
|
|
14
15
|
sha256_password: sha256_password({}),
|
|
15
16
|
caching_sha2_password: caching_sha2_password({}),
|
|
16
|
-
mysql_native_password: mysql_native_password({})
|
|
17
|
+
mysql_native_password: mysql_native_password({}),
|
|
18
|
+
mysql_clear_password: mysql_clear_password({})
|
|
17
19
|
};
|
|
18
20
|
|
|
19
21
|
function warnLegacyAuthSwitch() {
|
|
@@ -1,7 +1,14 @@
|
|
|
1
|
+
// This file was modified by Oracle on September 21, 2021.
|
|
2
|
+
// The changes involve saving additional authentication factor passwords
|
|
3
|
+
// in the command scope and enabling multi-factor authentication in the
|
|
4
|
+
// client-side when the server supports it.
|
|
5
|
+
// Modifications copyright (c) 2021, Oracle and/or its affiliates.
|
|
6
|
+
|
|
1
7
|
'use strict';
|
|
2
8
|
|
|
3
9
|
const Command = require('./command.js');
|
|
4
10
|
const Packets = require('../packets/index.js');
|
|
11
|
+
const ClientConstants = require('../constants/client');
|
|
5
12
|
const ClientHandshake = require('./client_handshake.js');
|
|
6
13
|
const CharsetToEncoding = require('../constants/charset_encodings.js');
|
|
7
14
|
|
|
@@ -11,10 +18,15 @@ class ChangeUser extends Command {
|
|
|
11
18
|
this.onResult = callback;
|
|
12
19
|
this.user = options.user;
|
|
13
20
|
this.password = options.password;
|
|
21
|
+
// "password1" is an alias of "password"
|
|
22
|
+
this.password1 = options.password;
|
|
23
|
+
this.password2 = options.password2;
|
|
24
|
+
this.password3 = options.password3;
|
|
14
25
|
this.database = options.database;
|
|
15
26
|
this.passwordSha1 = options.passwordSha1;
|
|
16
27
|
this.charsetNumber = options.charsetNumber;
|
|
17
28
|
this.currentConfig = options.currentConfig;
|
|
29
|
+
this.authenticationFactor = 0;
|
|
18
30
|
}
|
|
19
31
|
start(packet, connection) {
|
|
20
32
|
const newPacket = new Packets.ChangeUser({
|
|
@@ -35,6 +47,13 @@ class ChangeUser extends Command {
|
|
|
35
47
|
// reset prepared statements cache as all statements become invalid after changeUser
|
|
36
48
|
connection._statements.reset();
|
|
37
49
|
connection.writePacket(newPacket.toPacket());
|
|
50
|
+
// check if the server supports multi-factor authentication
|
|
51
|
+
const multiFactorAuthentication = connection.serverCapabilityFlags & ClientConstants.MULTI_FACTOR_AUTHENTICATION;
|
|
52
|
+
if (multiFactorAuthentication) {
|
|
53
|
+
// if the server supports multi-factor authentication, we enable it in
|
|
54
|
+
// the client
|
|
55
|
+
this.authenticationFactor = 1;
|
|
56
|
+
}
|
|
38
57
|
return ChangeUser.prototype.handshakeResult;
|
|
39
58
|
}
|
|
40
59
|
}
|
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
// emitted in the command instance itself.
|
|
4
4
|
// Modifications copyright (c) 2021, Oracle and/or its affiliates.
|
|
5
5
|
|
|
6
|
+
// This file was modified by Oracle on September 21, 2021.
|
|
7
|
+
// Handshake workflow now supports additional authentication factors requested
|
|
8
|
+
// by the server.
|
|
9
|
+
// Modifications copyright (c) 2021, Oracle and/or its affiliates.
|
|
10
|
+
|
|
6
11
|
'use strict';
|
|
7
12
|
|
|
8
13
|
const Command = require('./command.js');
|
|
@@ -26,6 +31,7 @@ class ClientHandshake extends Command {
|
|
|
26
31
|
super();
|
|
27
32
|
this.handshake = null;
|
|
28
33
|
this.clientFlags = clientFlags;
|
|
34
|
+
this.authenticationFactor = 0;
|
|
29
35
|
}
|
|
30
36
|
|
|
31
37
|
start() {
|
|
@@ -51,6 +57,14 @@ class ClientHandshake extends Command {
|
|
|
51
57
|
}
|
|
52
58
|
this.user = connection.config.user;
|
|
53
59
|
this.password = connection.config.password;
|
|
60
|
+
// "password1" is an alias to the original "password" value
|
|
61
|
+
// to make it easier to integrate multi-factor authentication
|
|
62
|
+
this.password1 = connection.config.password;
|
|
63
|
+
// "password2" and "password3" are the 2nd and 3rd factor authentication
|
|
64
|
+
// passwords, which can be undefined depending on the authentication
|
|
65
|
+
// plugin being used
|
|
66
|
+
this.password2 = connection.config.password2;
|
|
67
|
+
this.password3 = connection.config.password3;
|
|
54
68
|
this.passwordSha1 = connection.config.passwordSha1;
|
|
55
69
|
this.database = connection.config.database;
|
|
56
70
|
this.autPluginName = this.handshake.autPluginName;
|
|
@@ -109,6 +123,12 @@ class ClientHandshake extends Command {
|
|
|
109
123
|
connection.connectionId = this.handshake.connectionId;
|
|
110
124
|
const serverSSLSupport =
|
|
111
125
|
this.handshake.capabilityFlags & ClientConstants.SSL;
|
|
126
|
+
// multi factor authentication is enabled with the
|
|
127
|
+
// "MULTI_FACTOR_AUTHENTICATION" capability and should only be used if it
|
|
128
|
+
// is supported by the server
|
|
129
|
+
const multiFactorAuthentication =
|
|
130
|
+
this.handshake.capabilityFlags & ClientConstants.MULTI_FACTOR_AUTHENTICATION;
|
|
131
|
+
this.clientFlags = this.clientFlags | multiFactorAuthentication;
|
|
112
132
|
// use compression only if requested by client and supported by server
|
|
113
133
|
connection.config.compress =
|
|
114
134
|
connection.config.compress &&
|
|
@@ -141,17 +161,36 @@ class ClientHandshake extends Command {
|
|
|
141
161
|
} else {
|
|
142
162
|
this.sendCredentials(connection);
|
|
143
163
|
}
|
|
164
|
+
if (multiFactorAuthentication) {
|
|
165
|
+
// if the server supports multi-factor authentication, we enable it in
|
|
166
|
+
// the client
|
|
167
|
+
this.authenticationFactor = 1;
|
|
168
|
+
}
|
|
144
169
|
return ClientHandshake.prototype.handshakeResult;
|
|
145
170
|
}
|
|
146
171
|
|
|
147
172
|
handshakeResult(packet, connection) {
|
|
148
173
|
const marker = packet.peekByte();
|
|
149
|
-
|
|
174
|
+
// packet can be OK_Packet, ERR_Packet, AuthSwitchRequest, AuthNextFactor
|
|
175
|
+
// or AuthMoreData
|
|
176
|
+
if (marker === 0xfe || marker === 1 || marker === 0x02) {
|
|
150
177
|
const authSwitch = require('./auth_switch');
|
|
151
178
|
try {
|
|
152
179
|
if (marker === 1) {
|
|
153
180
|
authSwitch.authSwitchRequestMoreData(packet, connection, this);
|
|
154
181
|
} else {
|
|
182
|
+
// if authenticationFactor === 0, it means the server does not support
|
|
183
|
+
// the multi-factor authentication capability
|
|
184
|
+
if (this.authenticationFactor !== 0) {
|
|
185
|
+
// if we are past the first authentication factor, we should use the
|
|
186
|
+
// corresponding password (if there is one)
|
|
187
|
+
connection.config.password = this[`password${this.authenticationFactor}`];
|
|
188
|
+
// update the current authentication factor
|
|
189
|
+
this.authenticationFactor += 1;
|
|
190
|
+
}
|
|
191
|
+
// if marker === 0x02, it means it is an AuthNextFactor packet,
|
|
192
|
+
// which is similar in structure to an AuthSwitchRequest packet,
|
|
193
|
+
// so, we can use it directly
|
|
155
194
|
authSwitch.authSwitchRequest(packet, connection, this);
|
|
156
195
|
}
|
|
157
196
|
return ClientHandshake.prototype.handshakeResult;
|
package/lib/commands/prepare.js
CHANGED
|
@@ -55,7 +55,8 @@ class Prepare extends Command {
|
|
|
55
55
|
}
|
|
56
56
|
const cmdPacket = new Packets.PrepareStatement(
|
|
57
57
|
this.query,
|
|
58
|
-
connection.config.charsetNumber
|
|
58
|
+
connection.config.charsetNumber,
|
|
59
|
+
this.options.values
|
|
59
60
|
);
|
|
60
61
|
connection.writePacket(cmdPacket.toPacket(1));
|
|
61
62
|
return Prepare.prototype.prepareHeader;
|
package/lib/commands/quit.js
CHANGED
|
@@ -7,7 +7,7 @@ const Packet = require('../packets/packet.js');
|
|
|
7
7
|
class Quit extends Command {
|
|
8
8
|
constructor(callback) {
|
|
9
9
|
super();
|
|
10
|
-
this.
|
|
10
|
+
this.onResult = callback;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
start(packet, connection) {
|
|
@@ -18,8 +18,8 @@ class Quit extends Command {
|
|
|
18
18
|
0,
|
|
19
19
|
5
|
|
20
20
|
);
|
|
21
|
-
if (this.
|
|
22
|
-
this.
|
|
21
|
+
if (this.onResult) {
|
|
22
|
+
this.onResult();
|
|
23
23
|
}
|
|
24
24
|
connection.writePacket(quit);
|
|
25
25
|
return null;
|
|
@@ -69,12 +69,41 @@ class ServerHandshake extends Command {
|
|
|
69
69
|
return ServerHandshake.prototype.dispatchCommands;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
_isStatement(query, name) {
|
|
73
|
+
const firstWord = query.split(' ')[0].toUpperCase();
|
|
74
|
+
return firstWord === name;
|
|
75
|
+
}
|
|
76
|
+
|
|
72
77
|
dispatchCommands(packet, connection) {
|
|
73
78
|
// command from client to server
|
|
74
79
|
let knownCommand = true;
|
|
75
80
|
const encoding = connection.clientHelloReply.encoding;
|
|
76
81
|
const commandCode = packet.readInt8();
|
|
77
82
|
switch (commandCode) {
|
|
83
|
+
case CommandCode.STMT_PREPARE:
|
|
84
|
+
if (connection.listeners('stmt_prepare').length) {
|
|
85
|
+
const query = packet.readString(undefined, encoding);
|
|
86
|
+
connection.emit('stmt_prepare', query);
|
|
87
|
+
} else {
|
|
88
|
+
connection.writeError({
|
|
89
|
+
code: Errors.HA_ERR_INTERNAL_ERROR,
|
|
90
|
+
message:
|
|
91
|
+
'No query handler for prepared statements.'
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
break;
|
|
95
|
+
case CommandCode.STMT_EXECUTE:
|
|
96
|
+
if (connection.listeners('stmt_execute').length) {
|
|
97
|
+
const { stmtId, flags, iterationCount, values } = Packets.Execute.fromPacket(packet, encoding);
|
|
98
|
+
connection.emit('stmt_execute', stmtId, flags, iterationCount, values);
|
|
99
|
+
} else {
|
|
100
|
+
connection.writeError({
|
|
101
|
+
code: Errors.HA_ERR_INTERNAL_ERROR,
|
|
102
|
+
message:
|
|
103
|
+
'No query handler for execute statements.'
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
break;
|
|
78
107
|
case CommandCode.QUIT:
|
|
79
108
|
if (connection.listeners('quit').length) {
|
|
80
109
|
connection.emit('quit');
|
|
@@ -93,7 +122,13 @@ class ServerHandshake extends Command {
|
|
|
93
122
|
case CommandCode.QUERY:
|
|
94
123
|
if (connection.listeners('query').length) {
|
|
95
124
|
const query = packet.readString(undefined, encoding);
|
|
96
|
-
|
|
125
|
+
if (this._isStatement(query, 'PREPARE') || this._isStatement(query, 'SET')) {
|
|
126
|
+
connection.emit('stmt_prepare', query);
|
|
127
|
+
}
|
|
128
|
+
else if (this._isStatement(query, 'EXECUTE')) {
|
|
129
|
+
connection.emit('stmt_execute', null, null, null, null, query);
|
|
130
|
+
}
|
|
131
|
+
else connection.emit('query', query);
|
|
97
132
|
} else {
|
|
98
133
|
connection.writeError({
|
|
99
134
|
code: Errors.HA_ERR_INTERNAL_ERROR,
|
package/lib/connection.js
CHANGED
|
@@ -8,6 +8,11 @@
|
|
|
8
8
|
// there is a fatal error.
|
|
9
9
|
// Modifications copyright (c) 2021, Oracle and/or its affiliates.
|
|
10
10
|
|
|
11
|
+
// This file was modified by Oracle on September 21, 2021.
|
|
12
|
+
// The changes involve passing additional authentication factor passwords
|
|
13
|
+
// to the ChangeUser Command instance.
|
|
14
|
+
// Modifications copyright (c) 2021, Oracle and/or its affiliates.
|
|
15
|
+
|
|
11
16
|
'use strict';
|
|
12
17
|
|
|
13
18
|
const Net = require('net');
|
|
@@ -50,6 +55,10 @@ class Connection extends EventEmitter {
|
|
|
50
55
|
// Enable keep-alive on the socket. It's disabled by default, but the
|
|
51
56
|
// user can enable it and supply an initial delay.
|
|
52
57
|
this.stream.setKeepAlive(true, this.config.keepAliveInitialDelay);
|
|
58
|
+
|
|
59
|
+
// Enable TCP_NODELAY flag. This is needed so that the network packets
|
|
60
|
+
// are sent immediately to the server
|
|
61
|
+
this.stream.setNoDelay(true);
|
|
53
62
|
}
|
|
54
63
|
// if stream is a function, treat it as "stream agent / factory"
|
|
55
64
|
} else if (typeof opts.config.stream === 'function') {
|
|
@@ -65,7 +74,7 @@ class Connection extends EventEmitter {
|
|
|
65
74
|
this._paused_packets = new Queue();
|
|
66
75
|
this._statements = new LRU({
|
|
67
76
|
max: this.config.maxPreparedStatements,
|
|
68
|
-
dispose: function(
|
|
77
|
+
dispose: function(statement) {
|
|
69
78
|
statement.close();
|
|
70
79
|
}
|
|
71
80
|
});
|
|
@@ -91,6 +100,10 @@ class Connection extends EventEmitter {
|
|
|
91
100
|
}
|
|
92
101
|
this.packetParser.execute(data);
|
|
93
102
|
});
|
|
103
|
+
this.stream.on('end', () => {
|
|
104
|
+
// emit the end event so that the pooled connection can close the connection
|
|
105
|
+
this.emit('end');
|
|
106
|
+
});
|
|
94
107
|
this.stream.on('close', () => {
|
|
95
108
|
// we need to set this flag everywhere where we want connection to close
|
|
96
109
|
if (this._closing) {
|
|
@@ -112,7 +125,7 @@ class Connection extends EventEmitter {
|
|
|
112
125
|
handshakeCommand.on('end', () => {
|
|
113
126
|
// this happens when handshake finishes early either because there was
|
|
114
127
|
// some fatal error or the server sent an error packet instead of
|
|
115
|
-
// an hello packet (for example, 'Too many
|
|
128
|
+
// an hello packet (for example, 'Too many connections' error)
|
|
116
129
|
if (!handshakeCommand.handshake || this._fatalError || this._protocolError) {
|
|
117
130
|
return;
|
|
118
131
|
}
|
|
@@ -126,9 +139,9 @@ class Connection extends EventEmitter {
|
|
|
126
139
|
});
|
|
127
140
|
this.addCommand(handshakeCommand);
|
|
128
141
|
}
|
|
129
|
-
// in case there was no
|
|
142
|
+
// in case there was no initial handshake but we need to read sting, assume it utf-8
|
|
130
143
|
// most common example: "Too many connections" error ( packet is sent immediately on connection attempt, we don't know server encoding yet)
|
|
131
|
-
// will be
|
|
144
|
+
// will be overwritten with actual encoding value as soon as server handshake packet is received
|
|
132
145
|
this.serverEncoding = 'utf8';
|
|
133
146
|
if (this.config.connectTimeout) {
|
|
134
147
|
const timeoutHandler = this._handleTimeoutError.bind(this);
|
|
@@ -174,7 +187,7 @@ class Connection extends EventEmitter {
|
|
|
174
187
|
this.connectTimeout = null;
|
|
175
188
|
}
|
|
176
189
|
// Do not throw an error when a connection ends with a RST,ACK packet
|
|
177
|
-
if (err.
|
|
190
|
+
if (err.code === 'ECONNRESET' && this._closing) {
|
|
178
191
|
return;
|
|
179
192
|
}
|
|
180
193
|
this._handleFatalError(err);
|
|
@@ -337,6 +350,9 @@ class Connection extends EventEmitter {
|
|
|
337
350
|
minVersion: this.config.ssl.minVersion
|
|
338
351
|
});
|
|
339
352
|
const rejectUnauthorized = this.config.ssl.rejectUnauthorized;
|
|
353
|
+
const verifyIdentity = this.config.ssl.verifyIdentity;
|
|
354
|
+
const host = this.config.host;
|
|
355
|
+
|
|
340
356
|
let secureEstablished = false;
|
|
341
357
|
const secureSocket = new Tls.TLSSocket(this.stream, {
|
|
342
358
|
rejectUnauthorized: rejectUnauthorized,
|
|
@@ -344,6 +360,9 @@ class Connection extends EventEmitter {
|
|
|
344
360
|
secureContext: secureContext,
|
|
345
361
|
isServer: false
|
|
346
362
|
});
|
|
363
|
+
if (typeof host === 'string') {
|
|
364
|
+
secureSocket.setServername(host);
|
|
365
|
+
}
|
|
347
366
|
// error handler for secure socket
|
|
348
367
|
secureSocket.on('_tlsError', err => {
|
|
349
368
|
if (secureEstablished) {
|
|
@@ -354,7 +373,15 @@ class Connection extends EventEmitter {
|
|
|
354
373
|
});
|
|
355
374
|
secureSocket.on('secure', () => {
|
|
356
375
|
secureEstablished = true;
|
|
357
|
-
|
|
376
|
+
let callbackValue = null;
|
|
377
|
+
if (rejectUnauthorized) {
|
|
378
|
+
callbackValue = secureSocket.ssl.verifyError()
|
|
379
|
+
if (!callbackValue && typeof host === 'string' && verifyIdentity) {
|
|
380
|
+
const cert = secureSocket.ssl.getPeerCertificate(true);
|
|
381
|
+
callbackValue = Tls.checkServerIdentity(host, cert)
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
onSecure(callbackValue);
|
|
358
385
|
});
|
|
359
386
|
secureSocket.on('data', data => {
|
|
360
387
|
this.packetParser.execute(data);
|
|
@@ -396,6 +423,10 @@ class Connection extends EventEmitter {
|
|
|
396
423
|
err.code = code || 'PROTOCOL_ERROR';
|
|
397
424
|
this.emit('error', err);
|
|
398
425
|
}
|
|
426
|
+
|
|
427
|
+
get fatalError() {
|
|
428
|
+
return this._fatalError;
|
|
429
|
+
}
|
|
399
430
|
|
|
400
431
|
handlePacket(packet) {
|
|
401
432
|
if (this._paused) {
|
|
@@ -556,7 +587,7 @@ class Connection extends EventEmitter {
|
|
|
556
587
|
this._paused = false;
|
|
557
588
|
while ((packet = this._paused_packets.shift())) {
|
|
558
589
|
this.handlePacket(packet);
|
|
559
|
-
// don't resume if packet
|
|
590
|
+
// don't resume if packet handler paused connection
|
|
560
591
|
if (this._paused) {
|
|
561
592
|
return;
|
|
562
593
|
}
|
|
@@ -582,7 +613,7 @@ class Connection extends EventEmitter {
|
|
|
582
613
|
const key = Connection.statementKey(options);
|
|
583
614
|
const stmt = this._statements.get(key);
|
|
584
615
|
if (stmt) {
|
|
585
|
-
this._statements.
|
|
616
|
+
this._statements.delete(key);
|
|
586
617
|
stmt.close();
|
|
587
618
|
}
|
|
588
619
|
return stmt;
|
|
@@ -671,7 +702,12 @@ class Connection extends EventEmitter {
|
|
|
671
702
|
new Commands.ChangeUser(
|
|
672
703
|
{
|
|
673
704
|
user: options.user || this.config.user,
|
|
674
|
-
|
|
705
|
+
// for the purpose of multi-factor authentication, or not, the main
|
|
706
|
+
// password (used for the 1st authentication factor) can also be
|
|
707
|
+
// provided via the "password1" option
|
|
708
|
+
password: options.password || options.password1 || this.config.password || this.config.password1,
|
|
709
|
+
password2: options.password2 || this.config.password2,
|
|
710
|
+
password3: options.password3 || this.config.password3,
|
|
675
711
|
passwordSha1: options.passwordSha1 || this.config.passwordSha1,
|
|
676
712
|
database: options.database || this.config.database,
|
|
677
713
|
timeout: options.timeout,
|
|
@@ -804,14 +840,23 @@ class Connection extends EventEmitter {
|
|
|
804
840
|
);
|
|
805
841
|
}
|
|
806
842
|
|
|
807
|
-
|
|
843
|
+
writeBinaryRow(column) {
|
|
844
|
+
this.writePacket(
|
|
845
|
+
Packets.BinaryRow.toPacket(column, this.serverConfig.encoding)
|
|
846
|
+
);
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
writeTextResult(rows, columns, binary=false) {
|
|
808
850
|
this.writeColumns(columns);
|
|
809
851
|
rows.forEach(row => {
|
|
810
852
|
const arrayRow = new Array(columns.length);
|
|
811
853
|
columns.forEach(column => {
|
|
812
854
|
arrayRow.push(row[column.name]);
|
|
813
855
|
});
|
|
814
|
-
|
|
856
|
+
if(binary) {
|
|
857
|
+
this.writeBinaryRow(arrayRow);
|
|
858
|
+
}
|
|
859
|
+
else this.writeTextRow(arrayRow);
|
|
815
860
|
});
|
|
816
861
|
this.writeEof();
|
|
817
862
|
}
|
package/lib/connection_config.js
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
// This file was modified by Oracle on September 21, 2021.
|
|
2
|
+
// New connection options for additional authentication factors were
|
|
3
|
+
// introduced.
|
|
4
|
+
// Multi-factor authentication capability is now enabled if one of these
|
|
5
|
+
// options is used.
|
|
6
|
+
// Modifications copyright (c) 2021, Oracle and/or its affiliates.
|
|
7
|
+
|
|
1
8
|
'use strict';
|
|
2
9
|
|
|
3
10
|
const { URL } = require('url');
|
|
@@ -30,6 +37,11 @@ const validOptions = {
|
|
|
30
37
|
namedPlaceholders: 1,
|
|
31
38
|
nestTables: 1,
|
|
32
39
|
password: 1,
|
|
40
|
+
// with multi-factor authentication, the main password (used for the first
|
|
41
|
+
// authentication factor) can be provided via password1
|
|
42
|
+
password1: 1,
|
|
43
|
+
password2: 1,
|
|
44
|
+
password3: 1,
|
|
33
45
|
passwordSha1: 1,
|
|
34
46
|
pool: 1,
|
|
35
47
|
port: 1,
|
|
@@ -81,7 +93,12 @@ class ConnectionConfig {
|
|
|
81
93
|
this.localAddress = options.localAddress;
|
|
82
94
|
this.socketPath = options.socketPath;
|
|
83
95
|
this.user = options.user || undefined;
|
|
84
|
-
|
|
96
|
+
// for the purpose of multi-factor authentication, or not, the main
|
|
97
|
+
// password (used for the 1st authentication factor) can also be
|
|
98
|
+
// provided via the "password1" option
|
|
99
|
+
this.password = options.password || options.password1 || undefined;
|
|
100
|
+
this.password2 = options.password2 || undefined;
|
|
101
|
+
this.password3 = options.password3 || undefined;
|
|
85
102
|
this.passwordSha1 = options.passwordSha1 || undefined;
|
|
86
103
|
this.database = options.database;
|
|
87
104
|
this.connectTimeout = isNaN(options.connectTimeout)
|
|
@@ -128,7 +145,7 @@ class ConnectionConfig {
|
|
|
128
145
|
// "+" is a url encoded char for space so it
|
|
129
146
|
// gets translated to space when giving a
|
|
130
147
|
// connection string..
|
|
131
|
-
this.timezone = `+${this.timezone.
|
|
148
|
+
this.timezone = `+${this.timezone.slice(1)}`;
|
|
132
149
|
}
|
|
133
150
|
if (this.ssl) {
|
|
134
151
|
if (typeof this.ssl !== 'object') {
|
|
@@ -236,7 +253,7 @@ class ConnectionConfig {
|
|
|
236
253
|
const options = {
|
|
237
254
|
host: parsedUrl.hostname,
|
|
238
255
|
port: parsedUrl.port,
|
|
239
|
-
database: parsedUrl.pathname.
|
|
256
|
+
database: parsedUrl.pathname.slice(1),
|
|
240
257
|
user: parsedUrl.username,
|
|
241
258
|
password: parsedUrl.password
|
|
242
259
|
};
|
|
@@ -104,7 +104,7 @@ module.exports = [
|
|
|
104
104
|
'eucjpms',
|
|
105
105
|
'eucjpms',
|
|
106
106
|
'cp1250',
|
|
107
|
-
'
|
|
107
|
+
'utf16',
|
|
108
108
|
'utf16',
|
|
109
109
|
'utf16',
|
|
110
110
|
'utf16',
|
|
@@ -309,5 +309,8 @@ module.exports = [
|
|
|
309
309
|
'utf8',
|
|
310
310
|
'utf8',
|
|
311
311
|
'utf8',
|
|
312
|
+
'utf8',
|
|
313
|
+
'utf8',
|
|
314
|
+
'utf8',
|
|
312
315
|
'utf8'
|
|
313
316
|
];
|