@depup/mysql2 3.19.0-depup.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/License +19 -0
- package/README.md +114 -0
- package/index.d.ts +1 -0
- package/index.js +77 -0
- package/lib/auth_41.js +95 -0
- package/lib/auth_plugins/caching_sha2_password.js +114 -0
- package/lib/auth_plugins/caching_sha2_password.md +18 -0
- package/lib/auth_plugins/index.js +8 -0
- package/lib/auth_plugins/mysql_clear_password.js +17 -0
- package/lib/auth_plugins/mysql_native_password.js +34 -0
- package/lib/auth_plugins/sha256_password.js +68 -0
- package/lib/base/connection.js +988 -0
- package/lib/base/pool.js +282 -0
- package/lib/base/pool_connection.js +77 -0
- package/lib/commands/auth_switch.js +129 -0
- package/lib/commands/binlog_dump.js +109 -0
- package/lib/commands/change_user.js +68 -0
- package/lib/commands/client_handshake.js +383 -0
- package/lib/commands/close_statement.js +18 -0
- package/lib/commands/command.js +54 -0
- package/lib/commands/execute.js +112 -0
- package/lib/commands/index.js +27 -0
- package/lib/commands/ping.js +36 -0
- package/lib/commands/prepare.js +143 -0
- package/lib/commands/query.js +361 -0
- package/lib/commands/quit.js +29 -0
- package/lib/commands/register_slave.js +27 -0
- package/lib/commands/server_handshake.js +202 -0
- package/lib/compressed_protocol.js +153 -0
- package/lib/connection.js +12 -0
- package/lib/connection_config.js +293 -0
- package/lib/constants/charset_encodings.js +317 -0
- package/lib/constants/charsets.js +317 -0
- package/lib/constants/client.js +38 -0
- package/lib/constants/commands.js +36 -0
- package/lib/constants/cursor.js +8 -0
- package/lib/constants/encoding_charset.js +50 -0
- package/lib/constants/errors.js +3973 -0
- package/lib/constants/field_flags.js +20 -0
- package/lib/constants/server_status.js +44 -0
- package/lib/constants/session_track.js +11 -0
- package/lib/constants/ssl_profiles.js +11 -0
- package/lib/constants/types.js +64 -0
- package/lib/create_connection.js +10 -0
- package/lib/create_pool.js +10 -0
- package/lib/create_pool_cluster.js +9 -0
- package/lib/helpers.js +83 -0
- package/lib/packet_parser.js +195 -0
- package/lib/packets/auth_next_factor.js +35 -0
- package/lib/packets/auth_switch_request.js +38 -0
- package/lib/packets/auth_switch_request_more_data.js +33 -0
- package/lib/packets/auth_switch_response.js +30 -0
- package/lib/packets/binary_row.js +95 -0
- package/lib/packets/binlog_dump.js +33 -0
- package/lib/packets/binlog_query_statusvars.js +115 -0
- package/lib/packets/change_user.js +97 -0
- package/lib/packets/close_statement.js +21 -0
- package/lib/packets/column_definition.js +291 -0
- package/lib/packets/execute.js +214 -0
- package/lib/packets/handshake.js +112 -0
- package/lib/packets/handshake_response.js +173 -0
- package/lib/packets/index.js +152 -0
- package/lib/packets/packet.js +946 -0
- package/lib/packets/prepare_statement.js +27 -0
- package/lib/packets/prepared_statement_header.js +16 -0
- package/lib/packets/query.js +27 -0
- package/lib/packets/register_slave.js +46 -0
- package/lib/packets/resultset_header.js +124 -0
- package/lib/packets/ssl_request.js +25 -0
- package/lib/packets/text_row.js +47 -0
- package/lib/parsers/binary_parser.js +235 -0
- package/lib/parsers/parser_cache.js +68 -0
- package/lib/parsers/static_binary_parser.js +213 -0
- package/lib/parsers/static_text_parser.js +152 -0
- package/lib/parsers/string.js +50 -0
- package/lib/parsers/text_parser.js +214 -0
- package/lib/pool.js +12 -0
- package/lib/pool_cluster.js +375 -0
- package/lib/pool_config.js +30 -0
- package/lib/pool_connection.js +12 -0
- package/lib/promise/connection.js +228 -0
- package/lib/promise/inherit_events.js +27 -0
- package/lib/promise/make_done_cb.js +19 -0
- package/lib/promise/pool.js +118 -0
- package/lib/promise/pool_cluster.js +54 -0
- package/lib/promise/pool_connection.js +23 -0
- package/lib/promise/prepared_statement_info.js +32 -0
- package/lib/results_stream.js +38 -0
- package/lib/server.js +37 -0
- package/package.json +96 -0
- package/promise.d.ts +139 -0
- package/promise.js +208 -0
- package/typings/mysql/LICENSE.txt +15 -0
- package/typings/mysql/index.d.ts +84 -0
- package/typings/mysql/info.txt +1 -0
- package/typings/mysql/lib/Auth.d.ts +30 -0
- package/typings/mysql/lib/Connection.d.ts +442 -0
- package/typings/mysql/lib/Pool.d.ts +71 -0
- package/typings/mysql/lib/PoolCluster.d.ts +92 -0
- package/typings/mysql/lib/PoolConnection.d.ts +11 -0
- package/typings/mysql/lib/Server.d.ts +11 -0
- package/typings/mysql/lib/constants/CharsetToEncoding.d.ts +8 -0
- package/typings/mysql/lib/constants/Charsets.d.ts +326 -0
- package/typings/mysql/lib/constants/Types.d.ts +70 -0
- package/typings/mysql/lib/constants/index.d.ts +5 -0
- package/typings/mysql/lib/parsers/ParserCache.d.ts +4 -0
- package/typings/mysql/lib/parsers/index.d.ts +18 -0
- package/typings/mysql/lib/parsers/typeCast.d.ts +54 -0
- package/typings/mysql/lib/protocol/packets/Field.d.ts +10 -0
- package/typings/mysql/lib/protocol/packets/FieldPacket.d.ts +27 -0
- package/typings/mysql/lib/protocol/packets/OkPacket.d.ts +23 -0
- package/typings/mysql/lib/protocol/packets/ProcedurePacket.d.ts +13 -0
- package/typings/mysql/lib/protocol/packets/ResultSetHeader.d.ts +18 -0
- package/typings/mysql/lib/protocol/packets/RowDataPacket.d.ts +9 -0
- package/typings/mysql/lib/protocol/packets/index.d.ts +28 -0
- 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/ExecutableBase.d.ts +41 -0
- package/typings/mysql/lib/protocol/sequences/Prepare.d.ts +65 -0
- package/typings/mysql/lib/protocol/sequences/Query.d.ts +228 -0
- package/typings/mysql/lib/protocol/sequences/QueryableBase.d.ts +41 -0
- package/typings/mysql/lib/protocol/sequences/Sequence.d.ts +5 -0
- package/typings/mysql/lib/protocol/sequences/promise/ExecutableBase.d.ts +17 -0
- package/typings/mysql/lib/protocol/sequences/promise/QueryableBase.d.ts +18 -0
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
// This file was modified by Oracle on June 17, 2021.
|
|
2
|
+
// Handshake errors are now maked as fatal and the corresponding events are
|
|
3
|
+
// emitted in the command instance itself.
|
|
4
|
+
// Modifications copyright (c) 2021, Oracle and/or its affiliates.
|
|
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
|
+
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
const Command = require('./command.js');
|
|
14
|
+
const Packets = require('../packets/index.js');
|
|
15
|
+
const ClientConstants = require('../constants/client.js');
|
|
16
|
+
const CharsetToEncoding = require('../constants/charset_encodings.js');
|
|
17
|
+
const auth41 = require('../auth_41.js');
|
|
18
|
+
const { getAuthPlugin } = require('./auth_switch.js');
|
|
19
|
+
const {
|
|
20
|
+
calculateToken: calculateSha2Token,
|
|
21
|
+
} = require('../auth_plugins/caching_sha2_password.js');
|
|
22
|
+
|
|
23
|
+
function flagNames(flags) {
|
|
24
|
+
const res = [];
|
|
25
|
+
for (const c in ClientConstants) {
|
|
26
|
+
if (flags & ClientConstants[c]) {
|
|
27
|
+
res.push(c.replace(/_/g, ' ').toLowerCase());
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return res;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
class ClientHandshake extends Command {
|
|
34
|
+
constructor(clientFlags) {
|
|
35
|
+
super();
|
|
36
|
+
this.handshake = null;
|
|
37
|
+
this.clientFlags = clientFlags;
|
|
38
|
+
this.authenticationFactor = 0;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
start() {
|
|
42
|
+
return ClientHandshake.prototype.handshakeInit;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
sendSSLRequest(connection) {
|
|
46
|
+
const sslRequest = new Packets.SSLRequest(
|
|
47
|
+
this.clientFlags,
|
|
48
|
+
connection.config.charsetNumber
|
|
49
|
+
);
|
|
50
|
+
connection.writePacket(sslRequest.toPacket());
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
sendCredentials(connection) {
|
|
54
|
+
if (connection.config.debug) {
|
|
55
|
+
console.log(
|
|
56
|
+
'Sending handshake packet: flags:%d=(%s)',
|
|
57
|
+
this.clientFlags,
|
|
58
|
+
flagNames(this.clientFlags).join(', ')
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
this.user = connection.config.user;
|
|
62
|
+
this.password = connection.config.password;
|
|
63
|
+
// "password1" is an alias to the original "password" value
|
|
64
|
+
// to make it easier to integrate multi-factor authentication
|
|
65
|
+
this.password1 = connection.config.password;
|
|
66
|
+
// "password2" and "password3" are the 2nd and 3rd factor authentication
|
|
67
|
+
// passwords, which can be undefined depending on the authentication
|
|
68
|
+
// plugin being used
|
|
69
|
+
this.password2 = connection.config.password2;
|
|
70
|
+
this.password3 = connection.config.password3;
|
|
71
|
+
this.passwordSha1 = connection.config.passwordSha1;
|
|
72
|
+
this.database = connection.config.database;
|
|
73
|
+
this.authPluginName = this.handshake.authPluginName;
|
|
74
|
+
|
|
75
|
+
// Optimization: Try to use the server's preferred authentication method
|
|
76
|
+
// to avoid an unnecessary auth switch roundtrip
|
|
77
|
+
const serverAuthMethod = this.handshake.authPluginName;
|
|
78
|
+
const isSecureConnection =
|
|
79
|
+
connection.config.ssl || connection.config.socketPath;
|
|
80
|
+
|
|
81
|
+
// Combine auth plugin data for easier handling
|
|
82
|
+
// Note: authPluginData2 can include a trailing NUL byte when PLUGIN_AUTH is set
|
|
83
|
+
// We must ensure exactly 20 bytes for the scramble
|
|
84
|
+
const authPluginData =
|
|
85
|
+
this.handshake.authPluginData1 && this.handshake.authPluginData2
|
|
86
|
+
? Buffer.concat([
|
|
87
|
+
this.handshake.authPluginData1,
|
|
88
|
+
this.handshake.authPluginData2,
|
|
89
|
+
]).slice(0, 20)
|
|
90
|
+
: Buffer.alloc(20);
|
|
91
|
+
|
|
92
|
+
// Check if user has custom auth plugin or legacy handler for the server-advertised method
|
|
93
|
+
// If so, we must not bypass the auth switch flow with our built-in implementation
|
|
94
|
+
const hasCustomAuthPlugin =
|
|
95
|
+
connection.config.authPlugins &&
|
|
96
|
+
Object.prototype.hasOwnProperty.call(
|
|
97
|
+
connection.config.authPlugins,
|
|
98
|
+
serverAuthMethod
|
|
99
|
+
);
|
|
100
|
+
const hasLegacyAuthSwitchHandler =
|
|
101
|
+
typeof connection.config.authSwitchHandler === 'function';
|
|
102
|
+
|
|
103
|
+
// Determine which auth method to use
|
|
104
|
+
// Try to use server's preferred method if we can, otherwise fallback to native
|
|
105
|
+
const canUseDirectAuth =
|
|
106
|
+
!hasCustomAuthPlugin &&
|
|
107
|
+
!hasLegacyAuthSwitchHandler &&
|
|
108
|
+
this.canUseAuthMethodDirectly(serverAuthMethod, isSecureConnection);
|
|
109
|
+
|
|
110
|
+
const clientAuthMethod = canUseDirectAuth
|
|
111
|
+
? serverAuthMethod
|
|
112
|
+
: 'mysql_native_password';
|
|
113
|
+
|
|
114
|
+
// Calculate the auth token for the chosen method
|
|
115
|
+
const authToken = this.calculateAuthToken(
|
|
116
|
+
clientAuthMethod,
|
|
117
|
+
this.password,
|
|
118
|
+
authPluginData
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
if (connection.config.debug) {
|
|
122
|
+
console.log(
|
|
123
|
+
'Server auth method: %s, Using auth method: %s',
|
|
124
|
+
serverAuthMethod,
|
|
125
|
+
clientAuthMethod
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const handshakeResponse = new Packets.HandshakeResponse({
|
|
130
|
+
flags: this.clientFlags,
|
|
131
|
+
user: this.user,
|
|
132
|
+
database: this.database,
|
|
133
|
+
password: this.password,
|
|
134
|
+
passwordSha1: this.passwordSha1,
|
|
135
|
+
charsetNumber: connection.config.charsetNumber,
|
|
136
|
+
authPluginData1: this.handshake.authPluginData1,
|
|
137
|
+
authPluginData2: this.handshake.authPluginData2,
|
|
138
|
+
compress: connection.config.compress,
|
|
139
|
+
connectAttributes: connection.config.connectAttributes,
|
|
140
|
+
authToken: authToken,
|
|
141
|
+
authPluginName: clientAuthMethod,
|
|
142
|
+
});
|
|
143
|
+
connection.writePacket(handshakeResponse.toPacket());
|
|
144
|
+
|
|
145
|
+
// If we used a non-native auth method in the initial handshake response,
|
|
146
|
+
// we need to prepare for potential AuthMoreData packets by creating
|
|
147
|
+
// the appropriate auth plugin instance
|
|
148
|
+
if (clientAuthMethod !== 'mysql_native_password') {
|
|
149
|
+
this.initializeAuthPlugin(clientAuthMethod, authPluginData, connection);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
calculateNativePasswordAuthToken(authPluginData) {
|
|
154
|
+
// TODO: dont split into authPluginData1 and authPluginData2, instead join when 1 & 2 received
|
|
155
|
+
const authPluginData1 = authPluginData.slice(0, 8);
|
|
156
|
+
const authPluginData2 = authPluginData.slice(8, 20);
|
|
157
|
+
let authToken;
|
|
158
|
+
if (this.passwordSha1) {
|
|
159
|
+
authToken = auth41.calculateTokenFromPasswordSha(
|
|
160
|
+
this.passwordSha1,
|
|
161
|
+
authPluginData1,
|
|
162
|
+
authPluginData2
|
|
163
|
+
);
|
|
164
|
+
} else {
|
|
165
|
+
authToken = auth41.calculateToken(
|
|
166
|
+
this.password,
|
|
167
|
+
authPluginData1,
|
|
168
|
+
authPluginData2
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
return authToken;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
calculateSha256Token(password, scramble) {
|
|
175
|
+
// Reuse the token calculation from caching_sha2_password plugin
|
|
176
|
+
// to avoid code duplication and ensure consistency
|
|
177
|
+
return calculateSha2Token(password, scramble);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Helper: Calculate auth token for a specific auth method
|
|
181
|
+
calculateAuthToken(authMethod, password, authPluginData) {
|
|
182
|
+
switch (authMethod) {
|
|
183
|
+
case 'mysql_native_password':
|
|
184
|
+
return this.calculateNativePasswordAuthToken(authPluginData);
|
|
185
|
+
|
|
186
|
+
case 'caching_sha2_password':
|
|
187
|
+
return this.calculateSha256Token(password, authPluginData);
|
|
188
|
+
|
|
189
|
+
case 'sha256_password':
|
|
190
|
+
case 'mysql_clear_password':
|
|
191
|
+
// These methods send plaintext password over secure connections
|
|
192
|
+
return password
|
|
193
|
+
? Buffer.from(`${password}\0`, 'utf8')
|
|
194
|
+
: Buffer.alloc(0);
|
|
195
|
+
|
|
196
|
+
default:
|
|
197
|
+
// Unknown method - use native password as fallback
|
|
198
|
+
return this.calculateNativePasswordAuthToken(authPluginData);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Helper: Determine if we can use a specific auth method directly
|
|
203
|
+
canUseAuthMethodDirectly(authMethod, isSecureConnection) {
|
|
204
|
+
switch (authMethod) {
|
|
205
|
+
case 'mysql_native_password':
|
|
206
|
+
case 'caching_sha2_password':
|
|
207
|
+
// These methods work with or without SSL
|
|
208
|
+
return true;
|
|
209
|
+
|
|
210
|
+
case 'sha256_password':
|
|
211
|
+
case 'mysql_clear_password':
|
|
212
|
+
// These methods require secure connection for direct use
|
|
213
|
+
return isSecureConnection;
|
|
214
|
+
|
|
215
|
+
default:
|
|
216
|
+
// Unknown methods - fallback to native password
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Helper: Initialize auth plugin for handling subsequent AuthMoreData packets
|
|
222
|
+
initializeAuthPlugin(authMethod, authPluginData, connection) {
|
|
223
|
+
const authPlugin = getAuthPlugin(authMethod, connection);
|
|
224
|
+
if (!authPlugin) {
|
|
225
|
+
return; // Plugin not found, will fallback to auth switch if needed
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Initialize the plugin with connection and command context
|
|
229
|
+
const pluginHandler = authPlugin({ connection, command: this });
|
|
230
|
+
connection._authPlugin = pluginHandler;
|
|
231
|
+
|
|
232
|
+
// Prime the plugin by calling it with the scramble data
|
|
233
|
+
// This advances the plugin's state machine (e.g., to STATE_TOKEN_SENT)
|
|
234
|
+
// We don't send the result because we already included it in the handshake response
|
|
235
|
+
try {
|
|
236
|
+
Promise.resolve(pluginHandler(authPluginData)).catch((err) => {
|
|
237
|
+
// Ignore errors during initialization since we already sent the token
|
|
238
|
+
if (connection.config.debug) {
|
|
239
|
+
console.log('Auth plugin initialization:', err.message);
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
} catch (err) {
|
|
243
|
+
// Ignore synchronous errors during initialization
|
|
244
|
+
if (connection.config.debug) {
|
|
245
|
+
console.log('Auth plugin initialization error:', err.message);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
handshakeInit(helloPacket, connection) {
|
|
251
|
+
this.on('error', (e) => {
|
|
252
|
+
connection._fatalError = e;
|
|
253
|
+
connection._protocolError = e;
|
|
254
|
+
});
|
|
255
|
+
this.handshake = Packets.Handshake.fromPacket(helloPacket);
|
|
256
|
+
if (connection.config.debug) {
|
|
257
|
+
console.log(
|
|
258
|
+
'Server hello packet: capability flags:%d=(%s)',
|
|
259
|
+
this.handshake.capabilityFlags,
|
|
260
|
+
flagNames(this.handshake.capabilityFlags).join(', ')
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
connection.serverCapabilityFlags = this.handshake.capabilityFlags;
|
|
264
|
+
connection.serverEncoding = CharsetToEncoding[this.handshake.characterSet];
|
|
265
|
+
connection.connectionId = this.handshake.connectionId;
|
|
266
|
+
const serverSSLSupport =
|
|
267
|
+
this.handshake.capabilityFlags & ClientConstants.SSL;
|
|
268
|
+
// multi factor authentication is enabled with the
|
|
269
|
+
// "MULTI_FACTOR_AUTHENTICATION" capability and should only be used if it
|
|
270
|
+
// is supported by the server
|
|
271
|
+
const multiFactorAuthentication =
|
|
272
|
+
this.handshake.capabilityFlags &
|
|
273
|
+
ClientConstants.MULTI_FACTOR_AUTHENTICATION;
|
|
274
|
+
this.clientFlags = this.clientFlags | multiFactorAuthentication;
|
|
275
|
+
// use compression only if requested by client and supported by server
|
|
276
|
+
connection.config.compress =
|
|
277
|
+
connection.config.compress &&
|
|
278
|
+
this.handshake.capabilityFlags & ClientConstants.COMPRESS;
|
|
279
|
+
this.clientFlags = this.clientFlags | connection.config.compress;
|
|
280
|
+
if (connection.config.ssl) {
|
|
281
|
+
// client requires SSL but server does not support it
|
|
282
|
+
if (!serverSSLSupport) {
|
|
283
|
+
const err = new Error('Server does not support secure connection');
|
|
284
|
+
err.code = 'HANDSHAKE_NO_SSL_SUPPORT';
|
|
285
|
+
err.fatal = true;
|
|
286
|
+
this.emit('error', err);
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
// send ssl upgrade request and immediately upgrade connection to secure
|
|
290
|
+
this.clientFlags |= ClientConstants.SSL;
|
|
291
|
+
this.sendSSLRequest(connection);
|
|
292
|
+
connection.startTLS((err) => {
|
|
293
|
+
// after connection is secure
|
|
294
|
+
if (err) {
|
|
295
|
+
// SSL negotiation error are fatal
|
|
296
|
+
err.code = 'HANDSHAKE_SSL_ERROR';
|
|
297
|
+
err.fatal = true;
|
|
298
|
+
this.emit('error', err);
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
// rest of communication is encrypted
|
|
302
|
+
this.sendCredentials(connection);
|
|
303
|
+
});
|
|
304
|
+
} else {
|
|
305
|
+
this.sendCredentials(connection);
|
|
306
|
+
}
|
|
307
|
+
if (multiFactorAuthentication) {
|
|
308
|
+
// if the server supports multi-factor authentication, we enable it in
|
|
309
|
+
// the client
|
|
310
|
+
this.authenticationFactor = 1;
|
|
311
|
+
}
|
|
312
|
+
return ClientHandshake.prototype.handshakeResult;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
handshakeResult(packet, connection) {
|
|
316
|
+
const marker = packet.peekByte();
|
|
317
|
+
// packet can be OK_Packet, ERR_Packet, AuthSwitchRequest, AuthNextFactor
|
|
318
|
+
// or AuthMoreData
|
|
319
|
+
if (marker === 0xfe || marker === 1 || marker === 0x02) {
|
|
320
|
+
const authSwitch = require('./auth_switch');
|
|
321
|
+
try {
|
|
322
|
+
if (marker === 1) {
|
|
323
|
+
authSwitch.authSwitchRequestMoreData(packet, connection, this);
|
|
324
|
+
} else {
|
|
325
|
+
// if authenticationFactor === 0, it means the server does not support
|
|
326
|
+
// the multi-factor authentication capability
|
|
327
|
+
if (this.authenticationFactor !== 0) {
|
|
328
|
+
// if we are past the first authentication factor, we should use the
|
|
329
|
+
// corresponding password (if there is one)
|
|
330
|
+
connection.config.password =
|
|
331
|
+
this[`password${this.authenticationFactor}`];
|
|
332
|
+
// update the current authentication factor
|
|
333
|
+
this.authenticationFactor += 1;
|
|
334
|
+
}
|
|
335
|
+
// if marker === 0x02, it means it is an AuthNextFactor packet,
|
|
336
|
+
// which is similar in structure to an AuthSwitchRequest packet,
|
|
337
|
+
// so, we can use it directly
|
|
338
|
+
authSwitch.authSwitchRequest(packet, connection, this);
|
|
339
|
+
}
|
|
340
|
+
return ClientHandshake.prototype.handshakeResult;
|
|
341
|
+
} catch (err) {
|
|
342
|
+
// Authentication errors are fatal
|
|
343
|
+
err.code = 'AUTH_SWITCH_PLUGIN_ERROR';
|
|
344
|
+
err.fatal = true;
|
|
345
|
+
|
|
346
|
+
if (this.onResult) {
|
|
347
|
+
this.onResult(err);
|
|
348
|
+
} else {
|
|
349
|
+
this.emit('error', err);
|
|
350
|
+
}
|
|
351
|
+
return null;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
if (marker !== 0) {
|
|
355
|
+
const err = new Error('Unexpected packet during handshake phase');
|
|
356
|
+
// Unknown handshake errors are fatal
|
|
357
|
+
err.code = 'HANDSHAKE_UNKNOWN_ERROR';
|
|
358
|
+
err.fatal = true;
|
|
359
|
+
|
|
360
|
+
if (this.onResult) {
|
|
361
|
+
this.onResult(err);
|
|
362
|
+
} else {
|
|
363
|
+
this.emit('error', err);
|
|
364
|
+
}
|
|
365
|
+
return null;
|
|
366
|
+
}
|
|
367
|
+
// this should be called from ClientHandshake command only
|
|
368
|
+
// and skipped when called from ChangeUser command
|
|
369
|
+
if (!connection.authorized) {
|
|
370
|
+
connection.authorized = true;
|
|
371
|
+
if (connection.config.compress) {
|
|
372
|
+
const enableCompression =
|
|
373
|
+
require('../compressed_protocol.js').enableCompression;
|
|
374
|
+
enableCompression(connection);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
if (this.onResult) {
|
|
378
|
+
this.onResult(null);
|
|
379
|
+
}
|
|
380
|
+
return null;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
module.exports = ClientHandshake;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Command = require('./command');
|
|
4
|
+
const Packets = require('../packets/index.js');
|
|
5
|
+
|
|
6
|
+
class CloseStatement extends Command {
|
|
7
|
+
constructor(id) {
|
|
8
|
+
super();
|
|
9
|
+
this.id = id;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
start(packet, connection) {
|
|
13
|
+
connection.writePacket(new Packets.CloseStatement(this.id).toPacket(1));
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
module.exports = CloseStatement;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const EventEmitter = require('events').EventEmitter;
|
|
4
|
+
const Timers = require('timers');
|
|
5
|
+
|
|
6
|
+
class Command extends EventEmitter {
|
|
7
|
+
constructor() {
|
|
8
|
+
super();
|
|
9
|
+
this.next = null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// slow. debug only
|
|
13
|
+
stateName() {
|
|
14
|
+
const state = this.next;
|
|
15
|
+
for (const i in this) {
|
|
16
|
+
if (this[i] === state && i !== 'next') {
|
|
17
|
+
return i;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return 'unknown name';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
execute(packet, connection) {
|
|
24
|
+
if (!this.next) {
|
|
25
|
+
this.next = this.start;
|
|
26
|
+
connection._resetSequenceId();
|
|
27
|
+
}
|
|
28
|
+
if (packet && packet.isError()) {
|
|
29
|
+
const err = packet.asError(connection.clientEncoding);
|
|
30
|
+
err.sql = this.sql || this.query;
|
|
31
|
+
if (this.queryTimeout) {
|
|
32
|
+
Timers.clearTimeout(this.queryTimeout);
|
|
33
|
+
this.queryTimeout = null;
|
|
34
|
+
}
|
|
35
|
+
if (this.onResult) {
|
|
36
|
+
this.onResult(err);
|
|
37
|
+
this.emit('end');
|
|
38
|
+
} else {
|
|
39
|
+
this.emit('error', err);
|
|
40
|
+
this.emit('end');
|
|
41
|
+
}
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
// TODO: don't return anything from execute, it's ugly and error-prone. Listen for 'end' event in connection
|
|
45
|
+
this.next = this.next(packet, connection);
|
|
46
|
+
if (this.next) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
this.emit('end');
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
module.exports = Command;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Command = require('./command.js');
|
|
4
|
+
const Query = require('./query.js');
|
|
5
|
+
const Packets = require('../packets/index.js');
|
|
6
|
+
|
|
7
|
+
const getBinaryParser = require('../parsers/binary_parser.js');
|
|
8
|
+
const getStaticBinaryParser = require('../parsers/static_binary_parser.js');
|
|
9
|
+
|
|
10
|
+
class Execute extends Command {
|
|
11
|
+
constructor(options, callback) {
|
|
12
|
+
super();
|
|
13
|
+
this.statement = options.statement;
|
|
14
|
+
this.sql = options.sql;
|
|
15
|
+
this.values = options.values;
|
|
16
|
+
this.onResult = callback;
|
|
17
|
+
this.parameters = options.values;
|
|
18
|
+
this.insertId = 0;
|
|
19
|
+
this.timeout = options.timeout;
|
|
20
|
+
this.queryTimeout = null;
|
|
21
|
+
this._rows = [];
|
|
22
|
+
this._fields = [];
|
|
23
|
+
this._result = [];
|
|
24
|
+
this._fieldCount = 0;
|
|
25
|
+
this._rowParser = null;
|
|
26
|
+
this._executeOptions = options;
|
|
27
|
+
this._resultIndex = 0;
|
|
28
|
+
this._localStream = null;
|
|
29
|
+
this._unpipeStream = function () {};
|
|
30
|
+
this._streamFactory = options.infileStreamFactory;
|
|
31
|
+
this._connection = null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
buildParserFromFields(fields, connection) {
|
|
35
|
+
if (this.options.disableEval) {
|
|
36
|
+
return getStaticBinaryParser(fields, this.options, connection.config);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return getBinaryParser(fields, this.options, connection.config);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
start(packet, connection) {
|
|
43
|
+
this._connection = connection;
|
|
44
|
+
this.options = Object.assign({}, connection.config, this._executeOptions);
|
|
45
|
+
this._setTimeout();
|
|
46
|
+
const executePacket = new Packets.Execute(
|
|
47
|
+
this.statement.id,
|
|
48
|
+
this.parameters,
|
|
49
|
+
connection.config.charsetNumber,
|
|
50
|
+
connection.config.timezone
|
|
51
|
+
);
|
|
52
|
+
//For reasons why this try-catch is here, please see
|
|
53
|
+
// https://github.com/sidorares/node-mysql2/pull/689
|
|
54
|
+
//For additional discussion, see
|
|
55
|
+
// 1. https://github.com/sidorares/node-mysql2/issues/493
|
|
56
|
+
// 2. https://github.com/sidorares/node-mysql2/issues/187
|
|
57
|
+
// 3. https://github.com/sidorares/node-mysql2/issues/480
|
|
58
|
+
try {
|
|
59
|
+
connection.writePacket(executePacket.toPacket(1));
|
|
60
|
+
} catch (error) {
|
|
61
|
+
this.onResult(error);
|
|
62
|
+
}
|
|
63
|
+
return Execute.prototype.resultsetHeader;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
readField(packet, connection) {
|
|
67
|
+
let fields;
|
|
68
|
+
// disabling for now, but would be great to find reliable way to parse fields only once
|
|
69
|
+
// fields reported by prepare can be empty at all or just incorrect - see #169
|
|
70
|
+
//
|
|
71
|
+
// perfomance optimisation: if we already have this field parsed in statement header, use one from header
|
|
72
|
+
// const field = this.statement.columns.length == this._fieldCount ?
|
|
73
|
+
// this.statement.columns[this._receivedFieldsCount] : new Packets.ColumnDefinition(packet);
|
|
74
|
+
const field = new Packets.ColumnDefinition(
|
|
75
|
+
packet,
|
|
76
|
+
connection.clientEncoding
|
|
77
|
+
);
|
|
78
|
+
this._receivedFieldsCount++;
|
|
79
|
+
this._fields[this._resultIndex].push(field);
|
|
80
|
+
if (this._receivedFieldsCount === this._fieldCount) {
|
|
81
|
+
fields = this._fields[this._resultIndex];
|
|
82
|
+
this.emit('fields', fields, this._resultIndex);
|
|
83
|
+
return Execute.prototype.fieldsEOF;
|
|
84
|
+
}
|
|
85
|
+
return Execute.prototype.readField;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
fieldsEOF(packet, connection) {
|
|
89
|
+
// check EOF
|
|
90
|
+
if (!packet.isEOF()) {
|
|
91
|
+
return connection.protocolError('Expected EOF packet');
|
|
92
|
+
}
|
|
93
|
+
this._rowParser = new (this.buildParserFromFields(
|
|
94
|
+
this._fields[this._resultIndex],
|
|
95
|
+
connection
|
|
96
|
+
))();
|
|
97
|
+
return Execute.prototype.row;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
Execute.prototype.done = Query.prototype.done;
|
|
102
|
+
Execute.prototype.doneInsert = Query.prototype.doneInsert;
|
|
103
|
+
Execute.prototype.resultsetHeader = Query.prototype.resultsetHeader;
|
|
104
|
+
Execute.prototype._findOrCreateReadStream =
|
|
105
|
+
Query.prototype._findOrCreateReadStream;
|
|
106
|
+
Execute.prototype._streamLocalInfile = Query.prototype._streamLocalInfile;
|
|
107
|
+
Execute.prototype._setTimeout = Query.prototype._setTimeout;
|
|
108
|
+
Execute.prototype._handleTimeoutError = Query.prototype._handleTimeoutError;
|
|
109
|
+
Execute.prototype.row = Query.prototype.row;
|
|
110
|
+
Execute.prototype.stream = Query.prototype.stream;
|
|
111
|
+
|
|
112
|
+
module.exports = Execute;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const ClientHandshake = require('./client_handshake.js');
|
|
4
|
+
const ServerHandshake = require('./server_handshake.js');
|
|
5
|
+
const Query = require('./query.js');
|
|
6
|
+
const Prepare = require('./prepare.js');
|
|
7
|
+
const CloseStatement = require('./close_statement.js');
|
|
8
|
+
const Execute = require('./execute.js');
|
|
9
|
+
const Ping = require('./ping.js');
|
|
10
|
+
const RegisterSlave = require('./register_slave.js');
|
|
11
|
+
const BinlogDump = require('./binlog_dump.js');
|
|
12
|
+
const ChangeUser = require('./change_user.js');
|
|
13
|
+
const Quit = require('./quit.js');
|
|
14
|
+
|
|
15
|
+
module.exports = {
|
|
16
|
+
ClientHandshake,
|
|
17
|
+
ServerHandshake,
|
|
18
|
+
Query,
|
|
19
|
+
Prepare,
|
|
20
|
+
CloseStatement,
|
|
21
|
+
Execute,
|
|
22
|
+
Ping,
|
|
23
|
+
RegisterSlave,
|
|
24
|
+
BinlogDump,
|
|
25
|
+
ChangeUser,
|
|
26
|
+
Quit,
|
|
27
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Command = require('./command');
|
|
4
|
+
const CommandCode = require('../constants/commands');
|
|
5
|
+
const Packet = require('../packets/packet');
|
|
6
|
+
|
|
7
|
+
// TODO: time statistics?
|
|
8
|
+
// usefull for queue size and network latency monitoring
|
|
9
|
+
// store created,sent,reply timestamps
|
|
10
|
+
class Ping extends Command {
|
|
11
|
+
constructor(callback) {
|
|
12
|
+
super();
|
|
13
|
+
this.onResult = callback;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
start(packet, connection) {
|
|
17
|
+
const ping = new Packet(
|
|
18
|
+
0,
|
|
19
|
+
Buffer.from([1, 0, 0, 0, CommandCode.PING]),
|
|
20
|
+
0,
|
|
21
|
+
5
|
|
22
|
+
);
|
|
23
|
+
connection.writePacket(ping);
|
|
24
|
+
return Ping.prototype.pingResponse;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
pingResponse() {
|
|
28
|
+
// TODO: check it's OK packet. error check already done in caller
|
|
29
|
+
if (this.onResult) {
|
|
30
|
+
process.nextTick(this.onResult.bind(this));
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module.exports = Ping;
|