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;
 |