145 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			145 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; |