113 lines
3.8 KiB
JavaScript
113 lines
3.8 KiB
JavaScript
'use strict';
|
|
|
|
const Packet = require('../packets/packet');
|
|
const ClientConstants = require('../constants/client.js');
|
|
|
|
// https://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::Handshake
|
|
|
|
class Handshake {
|
|
constructor(args) {
|
|
this.protocolVersion = args.protocolVersion;
|
|
this.serverVersion = args.serverVersion;
|
|
this.capabilityFlags = args.capabilityFlags;
|
|
this.connectionId = args.connectionId;
|
|
this.authPluginData1 = args.authPluginData1;
|
|
this.authPluginData2 = args.authPluginData2;
|
|
this.characterSet = args.characterSet;
|
|
this.statusFlags = args.statusFlags;
|
|
this.autPluginName = args.autPluginName;
|
|
}
|
|
|
|
setScrambleData(cb) {
|
|
require('crypto').randomBytes(20, (err, data) => {
|
|
if (err) {
|
|
cb(err);
|
|
return;
|
|
}
|
|
this.authPluginData1 = data.slice(0, 8);
|
|
this.authPluginData2 = data.slice(8, 20);
|
|
cb();
|
|
});
|
|
}
|
|
|
|
toPacket(sequenceId) {
|
|
const length = 68 + Buffer.byteLength(this.serverVersion, 'utf8');
|
|
const buffer = Buffer.alloc(length + 4, 0); // zero fill, 10 bytes filler later needs to contain zeros
|
|
const packet = new Packet(sequenceId, buffer, 0, length + 4);
|
|
packet.offset = 4;
|
|
packet.writeInt8(this.protocolVersion);
|
|
packet.writeString(this.serverVersion, 'cesu8');
|
|
packet.writeInt8(0);
|
|
packet.writeInt32(this.connectionId);
|
|
packet.writeBuffer(this.authPluginData1);
|
|
packet.writeInt8(0);
|
|
const capabilityFlagsBuffer = Buffer.allocUnsafe(4);
|
|
capabilityFlagsBuffer.writeUInt32LE(this.capabilityFlags, 0);
|
|
packet.writeBuffer(capabilityFlagsBuffer.slice(0, 2));
|
|
packet.writeInt8(this.characterSet);
|
|
packet.writeInt16(this.statusFlags);
|
|
packet.writeBuffer(capabilityFlagsBuffer.slice(2, 4));
|
|
packet.writeInt8(21); // authPluginDataLength
|
|
packet.skip(10);
|
|
packet.writeBuffer(this.authPluginData2);
|
|
packet.writeInt8(0);
|
|
packet.writeString('mysql_native_password', 'latin1');
|
|
packet.writeInt8(0);
|
|
return packet;
|
|
}
|
|
|
|
static fromPacket(packet) {
|
|
const args = {};
|
|
args.protocolVersion = packet.readInt8();
|
|
args.serverVersion = packet.readNullTerminatedString('cesu8');
|
|
args.connectionId = packet.readInt32();
|
|
args.authPluginData1 = packet.readBuffer(8);
|
|
packet.skip(1);
|
|
const capabilityFlagsBuffer = Buffer.allocUnsafe(4);
|
|
capabilityFlagsBuffer[0] = packet.readInt8();
|
|
capabilityFlagsBuffer[1] = packet.readInt8();
|
|
if (packet.haveMoreData()) {
|
|
args.characterSet = packet.readInt8();
|
|
args.statusFlags = packet.readInt16();
|
|
// upper 2 bytes
|
|
capabilityFlagsBuffer[2] = packet.readInt8();
|
|
capabilityFlagsBuffer[3] = packet.readInt8();
|
|
args.capabilityFlags = capabilityFlagsBuffer.readUInt32LE(0);
|
|
if (args.capabilityFlags & ClientConstants.PLUGIN_AUTH) {
|
|
args.authPluginDataLength = packet.readInt8();
|
|
} else {
|
|
args.authPluginDataLength = 0;
|
|
packet.skip(1);
|
|
}
|
|
packet.skip(10);
|
|
} else {
|
|
args.capabilityFlags = capabilityFlagsBuffer.readUInt16LE(0);
|
|
}
|
|
|
|
const isSecureConnection =
|
|
args.capabilityFlags & ClientConstants.SECURE_CONNECTION;
|
|
if (isSecureConnection) {
|
|
const authPluginDataLength = args.authPluginDataLength;
|
|
if (authPluginDataLength === 0) {
|
|
// for Secure Password Authentication
|
|
args.authPluginDataLength = 20;
|
|
args.authPluginData2 = packet.readBuffer(12);
|
|
packet.skip(1);
|
|
} else {
|
|
// length > 0
|
|
// for Custom Auth Plugin (PLUGIN_AUTH)
|
|
const len = Math.max(13, authPluginDataLength - 8);
|
|
args.authPluginData2 = packet.readBuffer(len);
|
|
}
|
|
}
|
|
|
|
if (args.capabilityFlags & ClientConstants.PLUGIN_AUTH) {
|
|
args.autPluginName = packet.readNullTerminatedString('ascii');
|
|
}
|
|
|
|
return new Handshake(args);
|
|
}
|
|
}
|
|
|
|
module.exports = Handshake;
|