@microsoft/dev-tunnels-ssh
Version:
SSH library for Dev Tunnels
261 lines • 8.88 kB
JavaScript
"use strict";
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
Object.defineProperty(exports, "__esModule", { value: true });
exports.formatBuffer = exports.SshDataWriter = exports.SshDataReader = void 0;
const buffer_1 = require("buffer");
const sshAlgorithms_1 = require("../algorithms/sshAlgorithms");
const bigInt_1 = require("./bigInt");
class SshDataReader {
constructor(buffer) {
this.buffer = buffer;
this.position = 0;
}
get available() {
return this.buffer.length - this.position;
}
read(length) {
if (this.available < length) {
throw new Error('Attempted to read past end of buffer.');
}
const data = this.buffer.slice(this.position, this.position + length);
this.position += length;
return data;
}
readByte() {
if (this.available === 0) {
throw new Error('Attempted to read past end of buffer.');
}
const value = this.buffer[this.position];
this.position++;
return value;
}
readBinary() {
const length = this.readUInt32();
if (this.available < length) {
throw new Error('Attempted to read past end of buffer.');
}
const data = this.buffer.slice(this.position, this.position + length);
this.position += length;
return data;
}
readString(encoding) {
const bytes = this.readBinary();
return bytes.toString();
}
readList(encoding) {
const stringList = this.readString(encoding);
return stringList.length === 0 ? [] : stringList.split(',');
}
readBoolean() {
return this.readByte() !== 0;
}
readUInt32() {
if (this.available < 4) {
throw new Error('Attempted to read past end of buffer.');
}
// Big-endian encoding
const value0 = this.buffer[this.position + 0];
const value1 = this.buffer[this.position + 1];
const value2 = this.buffer[this.position + 2];
const value3 = this.buffer[this.position + 3];
this.position += 4;
const value = ((value0 << 24) | (value1 << 16) | (value2 << 8) | value3) >>> 0;
return value;
}
readUInt64() {
if (this.available < 8) {
throw new Error('Attempted to read past end of buffer.');
}
// Big-endian encoding
const value0 = this.buffer[this.position + 0];
const value1 = this.buffer[this.position + 1];
const value2 = this.buffer[this.position + 2];
const value3 = this.buffer[this.position + 3];
const value4 = this.buffer[this.position + 4];
const value5 = this.buffer[this.position + 5];
const value6 = this.buffer[this.position + 6];
const value7 = this.buffer[this.position + 7];
this.position += 8;
const high = ((value0 << 24) | (value1 << 16) | (value2 << 8) | value3) >>> 0;
const low = ((value4 << 24) | (value5 << 16) | (value6 << 8) | value7) >>> 0;
return high * 0x100000000 + low;
}
readBigInt() {
const data = this.readBinary();
if (data.length === 0) {
return bigInt_1.BigInt.zero;
}
return bigInt_1.BigInt.fromBytes(data);
}
}
exports.SshDataReader = SshDataReader;
SshDataReader.mpintZero = buffer_1.Buffer.alloc(1);
class SshDataWriter {
constructor(buffer) {
this.buffer = buffer;
this.position = 0;
}
write(data) {
this.ensureCapacity(this.position + data.length);
data.copy(this.buffer, this.position);
this.position += data.length;
}
writeByte(value) {
this.ensureCapacity(this.position + 1);
this.buffer[this.position] = value;
this.position++;
}
writeBinary(data) {
this.ensureCapacity(this.position + 4 + data.length);
this.writeUInt32(data.length);
data.copy(this.buffer, this.position);
this.position += data.length;
}
writeString(value, encoding) {
this.writeBinary(buffer_1.Buffer.from(value));
}
writeList(value, encoding) {
this.writeString(value ? value.join(',') : '', encoding);
}
writeBoolean(value) {
this.writeByte(value ? 1 : 0);
}
writeUInt32(value) {
this.ensureCapacity(this.position + 4);
// Big-endian encoding
this.buffer[this.position + 0] = value >>> 24;
this.buffer[this.position + 1] = value >>> 16;
this.buffer[this.position + 2] = value >>> 8;
this.buffer[this.position + 3] = value >>> 0;
this.position += 4;
}
/* @internal */
static writeUInt32(buffer, offset, value) {
buffer[offset + 0] = value >>> 24;
buffer[offset + 1] = value >>> 16;
buffer[offset + 2] = value >>> 8;
buffer[offset + 3] = value >>> 0;
}
writeUInt64(value) {
this.ensureCapacity(this.position + 8);
const low = value & 0xffffffff;
const high = (value - low) / 0x100000000;
// Big-endian encoding
this.buffer[this.position + 0] = high >>> 24;
this.buffer[this.position + 1] = high >>> 16;
this.buffer[this.position + 2] = high >>> 8;
this.buffer[this.position + 3] = high >>> 0;
this.buffer[this.position + 4] = low >>> 24;
this.buffer[this.position + 5] = low >>> 16;
this.buffer[this.position + 6] = low >>> 8;
this.buffer[this.position + 7] = low >>> 0;
this.position += 8;
}
writeBigInt(value) {
const data = value.toBytes();
if (data.length === 1 && data[0] === 0) {
this.writeUInt32(0);
}
else {
this.writeBinary(data);
}
}
writeRandom(length) {
this.ensureCapacity(this.position + length);
const randomBuffer = this.buffer.slice(this.position, this.position + length);
sshAlgorithms_1.SshAlgorithms.random.getBytes(randomBuffer);
this.position += length;
}
skip(length) {
this.ensureCapacity(this.position + length);
this.position += length;
}
ensureCapacity(capacity) {
if (this.buffer.length < capacity) {
let newLength = Math.max(512, this.buffer.length * 2);
while (newLength < capacity)
newLength *= 2;
const newBuffer = buffer_1.Buffer.alloc(newLength);
this.buffer.copy(newBuffer, 0, 0, this.position);
this.buffer = newBuffer;
}
}
toBuffer() {
return this.buffer.slice(0, this.position);
}
}
exports.SshDataWriter = SshDataWriter;
function makeCrcTable() {
let c;
const table = [];
for (let n = 0; n < 256; n++) {
c = n;
for (let k = 0; k < 8; k++) {
c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
}
table[n] = c;
}
return table;
}
let crcTable;
function crc32(data) {
if (!crcTable) {
crcTable = makeCrcTable();
}
let crc = 0 ^ -1;
for (let i = 0; i < data.length; i++) {
crc = (crc >>> 8) ^ crcTable[(crc ^ data[i]) & 0xff];
}
const result = (crc ^ -1) >>> 0;
return (result + 0x100000000)
.toString(16)
.substr(-8)
.toUpperCase();
}
/**
* Formats a byte buffer using the same format as OpenSSH,
* useful for debugging and comparison in logs.
*/
function formatBuffer(data, name, formatData) {
let s = `${name === undefined ? 'Buffer' : name}[${data.length}] (${crc32(data)})\n`;
if (formatData === false) {
return s;
}
const max = Math.min(2048, data.length);
for (let lineOffset = 0; lineOffset < max; lineOffset += 16) {
if (lineOffset < 1000)
s += '0';
if (lineOffset < 100)
s += '0';
if (lineOffset < 10)
s += '0';
s += lineOffset + ':';
for (let i = lineOffset; i < lineOffset + 16; i++) {
if (i < max) {
s += ' ' + data.slice(i, i + 1).toString('hex');
}
else {
s += ' ';
}
}
s += ' ';
for (let i = lineOffset; i < lineOffset + 16; i++) {
if (i < max) {
const c = data[i];
s += c > 32 && c <= 127 ? data.slice(i, i + 1).toString() : '.';
}
else {
s += ' ';
}
}
s += '\n';
}
if (max < data.length) {
s += '...\n';
}
return s;
}
exports.formatBuffer = formatBuffer;
//# sourceMappingURL=sshData.js.map