diff --git a/lib/client.js b/lib/client.js index aa94ace0..4a64b47f 100644 --- a/lib/client.js +++ b/lib/client.js @@ -1176,9 +1176,10 @@ class Client extends EventEmitter { return this; } - end() { + end(reason = null, message = '', lang = '') { + reason = reason || DISCONNECT_REASON.BY_APPLICATION; if (this._sock && isWritable(this._sock)) { - this._protocol.disconnect(DISCONNECT_REASON.BY_APPLICATION); + this._protocol.disconnect(reason, message, lang); this._sock.end(); } return this; diff --git a/lib/protocol/Protocol.js b/lib/protocol/Protocol.js index 73024881..fcf5ca02 100644 --- a/lib/protocol/Protocol.js +++ b/lib/protocol/Protocol.js @@ -321,23 +321,37 @@ class Protocol { // Global // ------ - disconnect(reason) { - const pktLen = 1 + 4 + 4 + 4; + disconnect(reason, message = '', lang = '') { + const msgLen = Buffer.byteLength(message); + const langLen = Buffer.byteLength(lang); + const pktLen = 1 + 4 + 4 + msgLen + 4 + langLen; // We don't use _packetRW.write.* here because we need to make sure that // we always get a full packet allocated because this message can be sent // at any time -- even during a key exchange let p = this._packetRW.write.allocStartKEX; const packet = this._packetRW.write.alloc(pktLen, true); - const end = p + pktLen; if (!VALID_DISCONNECT_REASONS.has(reason)) reason = DISCONNECT_REASON.PROTOCOL_ERROR; packet[p] = MESSAGE.DISCONNECT; writeUInt32BE(packet, reason, ++p); - packet.fill(0, p += 4, end); - this._debug && this._debug(`Outbound: Sending DISCONNECT (${reason})`); + writeUInt32BE(packet, msgLen, p += 4); + if (msgLen > 0){ + packet.utf8Write(message, p += 4, msgLen); + } else { + // This is because the SSH protocol expects a 4-byte length field for the message, + // followed by the message data (which may be zero bytes). + // Advancing the pointer by 4 ensures the next field (the language tag) + // is written at the correct offset, maintaining the correct packet structure. + p += 4; + } + + writeUInt32BE(packet, langLen, p += msgLen); + if (langLen > 0) packet.utf8Write(lang, p += 4, langLen); + + this._debug && this._debug(`Outbound: Sending DISCONNECT (${reason}, message: ${message})`); sendPacket(this, this._packetRW.write.finalize(packet, true), true); } ping() { diff --git a/lib/server.js b/lib/server.js index 306d6584..bd09e59a 100644 --- a/lib/server.js +++ b/lib/server.js @@ -1278,9 +1278,10 @@ class Client extends EventEmitter { } } - end() { + end(reason = null, message = '', lang = '') { + reason = reason || DISCONNECT_REASON.BY_APPLICATION; if (this._sock && isWritable(this._sock)) { - this._protocol.disconnect(DISCONNECT_REASON.BY_APPLICATION); + this._protocol.disconnect(reason, message, lang); this._sock.end(); } return this;