146 lines
4.9 KiB
JavaScript
146 lines
4.9 KiB
JavaScript
|
'use strict';
|
||
|
|
||
|
const ClientConstants = require('../constants/client.js');
|
||
|
const CharsetToEncoding = require('../constants/charset_encodings.js');
|
||
|
const Packet = require('../packets/packet.js');
|
||
|
|
||
|
const auth41 = require('../auth_41.js');
|
||
|
|
||
|
class HandshakeResponse {
|
||
|
constructor(handshake) {
|
||
|
this.user = handshake.user || '';
|
||
|
this.database = handshake.database || '';
|
||
|
this.password = handshake.password || '';
|
||
|
this.passwordSha1 = handshake.passwordSha1;
|
||
|
this.authPluginData1 = handshake.authPluginData1;
|
||
|
this.authPluginData2 = handshake.authPluginData2;
|
||
|
this.compress = handshake.compress;
|
||
|
this.clientFlags = handshake.flags;
|
||
|
// TODO: pre-4.1 auth support
|
||
|
let authToken;
|
||
|
if (this.passwordSha1) {
|
||
|
authToken = auth41.calculateTokenFromPasswordSha(
|
||
|
this.passwordSha1,
|
||
|
this.authPluginData1,
|
||
|
this.authPluginData2
|
||
|
);
|
||
|
} else {
|
||
|
authToken = auth41.calculateToken(
|
||
|
this.password,
|
||
|
this.authPluginData1,
|
||
|
this.authPluginData2
|
||
|
);
|
||
|
}
|
||
|
this.authToken = authToken;
|
||
|
this.charsetNumber = handshake.charsetNumber;
|
||
|
this.encoding = CharsetToEncoding[handshake.charsetNumber];
|
||
|
this.connectAttributes = handshake.connectAttributes;
|
||
|
}
|
||
|
|
||
|
serializeResponse(buffer) {
|
||
|
const isSet = flag => this.clientFlags & ClientConstants[flag];
|
||
|
const packet = new Packet(0, buffer, 0, buffer.length);
|
||
|
packet.offset = 4;
|
||
|
packet.writeInt32(this.clientFlags);
|
||
|
packet.writeInt32(0); // max packet size. todo: move to config
|
||
|
packet.writeInt8(this.charsetNumber);
|
||
|
packet.skip(23);
|
||
|
const encoding = this.encoding;
|
||
|
packet.writeNullTerminatedString(this.user, encoding);
|
||
|
let k;
|
||
|
if (isSet('PLUGIN_AUTH_LENENC_CLIENT_DATA')) {
|
||
|
packet.writeLengthCodedNumber(this.authToken.length);
|
||
|
packet.writeBuffer(this.authToken);
|
||
|
} else if (isSet('SECURE_CONNECTION')) {
|
||
|
packet.writeInt8(this.authToken.length);
|
||
|
packet.writeBuffer(this.authToken);
|
||
|
} else {
|
||
|
packet.writeBuffer(this.authToken);
|
||
|
packet.writeInt8(0);
|
||
|
}
|
||
|
if (isSet('CONNECT_WITH_DB')) {
|
||
|
packet.writeNullTerminatedString(this.database, encoding);
|
||
|
}
|
||
|
if (isSet('PLUGIN_AUTH')) {
|
||
|
// TODO: pass from config
|
||
|
packet.writeNullTerminatedString('mysql_native_password', 'latin1');
|
||
|
}
|
||
|
if (isSet('CONNECT_ATTRS')) {
|
||
|
const connectAttributes = this.connectAttributes || {};
|
||
|
const attrNames = Object.keys(connectAttributes);
|
||
|
let keysLength = 0;
|
||
|
for (k = 0; k < attrNames.length; ++k) {
|
||
|
keysLength += Packet.lengthCodedStringLength(attrNames[k], encoding);
|
||
|
keysLength += Packet.lengthCodedStringLength(
|
||
|
connectAttributes[attrNames[k]],
|
||
|
encoding
|
||
|
);
|
||
|
}
|
||
|
packet.writeLengthCodedNumber(keysLength);
|
||
|
for (k = 0; k < attrNames.length; ++k) {
|
||
|
packet.writeLengthCodedString(attrNames[k], encoding);
|
||
|
packet.writeLengthCodedString(
|
||
|
connectAttributes[attrNames[k]],
|
||
|
encoding
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
return packet;
|
||
|
}
|
||
|
|
||
|
toPacket() {
|
||
|
if (typeof this.user !== 'string') {
|
||
|
throw new Error('"user" connection config property must be a string');
|
||
|
}
|
||
|
if (typeof this.database !== 'string') {
|
||
|
throw new Error('"database" connection config property must be a string');
|
||
|
}
|
||
|
// dry run: calculate resulting packet length
|
||
|
const p = this.serializeResponse(Packet.MockBuffer());
|
||
|
return this.serializeResponse(Buffer.alloc(p.offset));
|
||
|
}
|
||
|
static fromPacket(packet) {
|
||
|
const args = {};
|
||
|
args.clientFlags = packet.readInt32();
|
||
|
function isSet(flag) {
|
||
|
return args.clientFlags & ClientConstants[flag];
|
||
|
}
|
||
|
args.maxPacketSize = packet.readInt32();
|
||
|
args.charsetNumber = packet.readInt8();
|
||
|
const encoding = CharsetToEncoding[args.charsetNumber];
|
||
|
args.encoding = encoding;
|
||
|
packet.skip(23);
|
||
|
args.user = packet.readNullTerminatedString(encoding);
|
||
|
let authTokenLength;
|
||
|
if (isSet('PLUGIN_AUTH_LENENC_CLIENT_DATA')) {
|
||
|
authTokenLength = packet.readLengthCodedNumber(encoding);
|
||
|
args.authToken = packet.readBuffer(authTokenLength);
|
||
|
} else if (isSet('SECURE_CONNECTION')) {
|
||
|
authTokenLength = packet.readInt8();
|
||
|
args.authToken = packet.readBuffer(authTokenLength);
|
||
|
} else {
|
||
|
args.authToken = packet.readNullTerminatedString(encoding);
|
||
|
}
|
||
|
if (isSet('CONNECT_WITH_DB')) {
|
||
|
args.database = packet.readNullTerminatedString(encoding);
|
||
|
}
|
||
|
if (isSet('PLUGIN_AUTH')) {
|
||
|
args.authPluginName = packet.readNullTerminatedString(encoding);
|
||
|
}
|
||
|
if (isSet('CONNECT_ATTRS')) {
|
||
|
const keysLength = packet.readLengthCodedNumber(encoding);
|
||
|
const keysEnd = packet.offset + keysLength;
|
||
|
const attrs = {};
|
||
|
while (packet.offset < keysEnd) {
|
||
|
attrs[
|
||
|
packet.readLengthCodedString(encoding)
|
||
|
] = packet.readLengthCodedString(encoding);
|
||
|
}
|
||
|
args.connectAttributes = attrs;
|
||
|
}
|
||
|
return args;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
module.exports = HandshakeResponse;
|