handshake.js 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. 'use strict';
  2. const Packet = require('../packets/packet');
  3. const ClientConstants = require('../constants/client.js');
  4. // https://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::Handshake
  5. class Handshake {
  6. constructor(args) {
  7. this.protocolVersion = args.protocolVersion;
  8. this.serverVersion = args.serverVersion;
  9. this.capabilityFlags = args.capabilityFlags;
  10. this.connectionId = args.connectionId;
  11. this.authPluginData1 = args.authPluginData1;
  12. this.authPluginData2 = args.authPluginData2;
  13. this.characterSet = args.characterSet;
  14. this.statusFlags = args.statusFlags;
  15. this.autPluginName = args.autPluginName;
  16. }
  17. setScrambleData(cb) {
  18. require('crypto').randomBytes(20, (err, data) => {
  19. if (err) {
  20. cb(err);
  21. return;
  22. }
  23. this.authPluginData1 = data.slice(0, 8);
  24. this.authPluginData2 = data.slice(8, 20);
  25. cb();
  26. });
  27. }
  28. toPacket(sequenceId) {
  29. const length = 68 + Buffer.byteLength(this.serverVersion, 'utf8');
  30. const buffer = Buffer.alloc(length + 4, 0); // zero fill, 10 bytes filler later needs to contain zeros
  31. const packet = new Packet(sequenceId, buffer, 0, length + 4);
  32. packet.offset = 4;
  33. packet.writeInt8(this.protocolVersion);
  34. packet.writeString(this.serverVersion, 'cesu8');
  35. packet.writeInt8(0);
  36. packet.writeInt32(this.connectionId);
  37. packet.writeBuffer(this.authPluginData1);
  38. packet.writeInt8(0);
  39. const capabilityFlagsBuffer = Buffer.allocUnsafe(4);
  40. capabilityFlagsBuffer.writeUInt32LE(this.capabilityFlags, 0);
  41. packet.writeBuffer(capabilityFlagsBuffer.slice(0, 2));
  42. packet.writeInt8(this.characterSet);
  43. packet.writeInt16(this.statusFlags);
  44. packet.writeBuffer(capabilityFlagsBuffer.slice(2, 4));
  45. packet.writeInt8(21); // authPluginDataLength
  46. packet.skip(10);
  47. packet.writeBuffer(this.authPluginData2);
  48. packet.writeInt8(0);
  49. packet.writeString('mysql_native_password', 'latin1');
  50. packet.writeInt8(0);
  51. return packet;
  52. }
  53. static fromPacket(packet) {
  54. const args = {};
  55. args.protocolVersion = packet.readInt8();
  56. args.serverVersion = packet.readNullTerminatedString('cesu8');
  57. args.connectionId = packet.readInt32();
  58. args.authPluginData1 = packet.readBuffer(8);
  59. packet.skip(1);
  60. const capabilityFlagsBuffer = Buffer.allocUnsafe(4);
  61. capabilityFlagsBuffer[0] = packet.readInt8();
  62. capabilityFlagsBuffer[1] = packet.readInt8();
  63. if (packet.haveMoreData()) {
  64. args.characterSet = packet.readInt8();
  65. args.statusFlags = packet.readInt16();
  66. // upper 2 bytes
  67. capabilityFlagsBuffer[2] = packet.readInt8();
  68. capabilityFlagsBuffer[3] = packet.readInt8();
  69. args.capabilityFlags = capabilityFlagsBuffer.readUInt32LE(0);
  70. if (args.capabilityFlags & ClientConstants.PLUGIN_AUTH) {
  71. args.authPluginDataLength = packet.readInt8();
  72. } else {
  73. args.authPluginDataLength = 0;
  74. packet.skip(1);
  75. }
  76. packet.skip(10);
  77. } else {
  78. args.capabilityFlags = capabilityFlagsBuffer.readUInt16LE(0);
  79. }
  80. const isSecureConnection =
  81. args.capabilityFlags & ClientConstants.SECURE_CONNECTION;
  82. if (isSecureConnection) {
  83. const authPluginDataLength = args.authPluginDataLength;
  84. if (authPluginDataLength === 0) {
  85. // for Secure Password Authentication
  86. args.authPluginDataLength = 20;
  87. args.authPluginData2 = packet.readBuffer(12);
  88. packet.skip(1);
  89. } else {
  90. // length > 0
  91. // for Custom Auth Plugin (PLUGIN_AUTH)
  92. const len = Math.max(13, authPluginDataLength - 8);
  93. args.authPluginData2 = packet.readBuffer(len);
  94. }
  95. }
  96. if (args.capabilityFlags & ClientConstants.PLUGIN_AUTH) {
  97. args.autPluginName = packet.readNullTerminatedString('ascii');
  98. }
  99. return new Handshake(args);
  100. }
  101. }
  102. module.exports = Handshake;