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.authPluginName = args.authPluginName; | ||
|  |   } | ||
|  | 
 | ||
|  |   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.authPluginName = packet.readNullTerminatedString('ascii'); | ||
|  |     } | ||
|  | 
 | ||
|  |     return new Handshake(args); | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | module.exports = Handshake; |