Files
bare-operating-system/packages/bare-os-openssh/vendor/bare-ssh2/lib/protocol/zlib.js
T
2026-04-08 22:21:22 -04:00

275 lines
6.4 KiB
JavaScript

'use strict'
const { kMaxLength } = require('buffer')
const {
createInflate,
constants: {
DEFLATE,
INFLATE,
Z_DEFAULT_CHUNK,
Z_DEFAULT_COMPRESSION,
Z_DEFAULT_MEMLEVEL,
Z_DEFAULT_STRATEGY,
Z_DEFAULT_WINDOWBITS,
Z_PARTIAL_FLUSH
}
} = require('bare-node-zlib')
/**
* Node's `zlib` exposes `createInflate()._handle.constructor` for the sync
* binding. Bare's `bare-node-zlib` / `bare-zlib` often has no `_handle`, so
* reading `.constructor` at module load throws. Resolve lazily; callers that
* need zlib compression get a clear error if unavailable.
*/
let ZlibHandleCtor
function getZlibHandleCtor() {
if (ZlibHandleCtor !== undefined) return ZlibHandleCtor
try {
const inf = createInflate()
ZlibHandleCtor =
inf &&
inf._handle &&
typeof inf._handle.constructor === 'function'
? inf._handle.constructor
: null
} catch {
ZlibHandleCtor = null
}
return ZlibHandleCtor
}
function processCallback() {
throw new Error('Should not get here')
}
function zlibOnError(message, errno, code) {
const self = this._owner
// There is no way to cleanly recover.
// Continuing only obscures problems.
const error = new Error(message)
error.errno = errno
error.code = code
self._err = error
}
function _close(engine) {
// Caller may invoke .close after a zlib error (which will null _handle).
if (!engine._handle) return
engine._handle.close()
engine._handle = null
}
class Zlib {
constructor(mode) {
const windowBits = Z_DEFAULT_WINDOWBITS
const level = Z_DEFAULT_COMPRESSION
const memLevel = Z_DEFAULT_MEMLEVEL
const strategy = Z_DEFAULT_STRATEGY
const dictionary = undefined
this._err = undefined
this._writeState = new Uint32Array(2)
this._chunkSize = Z_DEFAULT_CHUNK
this._maxOutputLength = kMaxLength
this._outBuffer = Buffer.allocUnsafe(this._chunkSize)
this._outOffset = 0
const Handle = getZlibHandleCtor()
if (!Handle) {
throw new Error(
'bare-ssh2 zlib: native zlib _handle unavailable (use compression "none" only on Bare/Pear)'
)
}
this._handle = new Handle(mode)
this._handle._owner = this
this._handle.onerror = zlibOnError
this._handle.init(
windowBits,
level,
memLevel,
strategy,
this._writeState,
processCallback,
dictionary
)
}
writeSync(chunk, retChunks) {
const handle = this._handle
if (!handle) throw new Error('Invalid Zlib instance')
let availInBefore = chunk.length
let availOutBefore = this._chunkSize - this._outOffset
let inOff = 0
let availOutAfter
let availInAfter
let buffers
let nread = 0
const state = this._writeState
let buffer = this._outBuffer
let offset = this._outOffset
const chunkSize = this._chunkSize
while (true) {
handle.writeSync(
Z_PARTIAL_FLUSH,
chunk, // in
inOff, // in_off
availInBefore, // in_len
buffer, // out
offset, // out_off
availOutBefore
) // out_len
if (this._err) throw this._err
availOutAfter = state[0]
availInAfter = state[1]
const inDelta = availInBefore - availInAfter
const have = availOutBefore - availOutAfter
if (have > 0) {
const out =
offset === 0 && have === buffer.length ? buffer : buffer.slice(offset, offset + have)
offset += have
if (!buffers) buffers = out
else if (buffers.push === undefined) buffers = [buffers, out]
else buffers.push(out)
nread += out.byteLength
if (nread > this._maxOutputLength) {
_close(this)
throw new Error(`Output length exceeded maximum of ${this._maxOutputLength}`)
}
} else if (have !== 0) {
throw new Error('have should not go down')
}
// Exhausted the output buffer, or used all the input create a new one.
if (availOutAfter === 0 || offset >= chunkSize) {
availOutBefore = chunkSize
offset = 0
buffer = Buffer.allocUnsafe(chunkSize)
}
if (availOutAfter === 0) {
// Not actually done. Need to reprocess.
// Also, update the availInBefore to the availInAfter value,
// so that if we have to hit it a third (fourth, etc.) time,
// it'll have the correct byte counts.
inOff += inDelta
availInBefore = availInAfter
} else {
break
}
}
this._outBuffer = buffer
this._outOffset = offset
if (nread === 0) buffers = Buffer.alloc(0)
if (retChunks) {
buffers.totalLen = nread
return buffers
}
if (buffers.push === undefined) return buffers
const output = Buffer.allocUnsafe(nread)
for (let i = 0, p = 0; i < buffers.length; ++i) {
const buf = buffers[i]
output.set(buf, p)
p += buf.length
}
return output
}
}
class ZlibPacketWriter {
constructor(protocol) {
this.allocStart = 0
this.allocStartKEX = 0
this._protocol = protocol
this._zlib = new Zlib(DEFLATE)
}
cleanup() {
if (this._zlib) _close(this._zlib)
}
alloc(payloadSize, force) {
return Buffer.allocUnsafe(payloadSize)
}
finalize(payload, force) {
if (this._protocol._kexinit === undefined || force) {
const output = this._zlib.writeSync(payload, true)
const packet = this._protocol._cipher.allocPacket(output.totalLen)
if (output.push === undefined) {
packet.set(output, 5)
} else {
for (let i = 0, p = 5; i < output.length; ++i) {
const chunk = output[i]
packet.set(chunk, p)
p += chunk.length
}
}
return packet
}
return payload
}
}
class PacketWriter {
constructor(protocol) {
this.allocStart = 5
this.allocStartKEX = 5
this._protocol = protocol
}
cleanup() {}
alloc(payloadSize, force) {
if (this._protocol._kexinit === undefined || force)
return this._protocol._cipher.allocPacket(payloadSize)
return Buffer.allocUnsafe(payloadSize)
}
finalize(packet, force) {
return packet
}
}
class ZlibPacketReader {
constructor() {
this._zlib = new Zlib(INFLATE)
}
cleanup() {
if (this._zlib) _close(this._zlib)
}
read(data) {
return this._zlib.writeSync(data, false)
}
}
class PacketReader {
cleanup() {}
read(data) {
return data
}
}
module.exports = {
PacketReader,
PacketWriter,
ZlibPacketReader,
ZlibPacketWriter
}