123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076 |
- /*
- TODO:
- * Replace `buffer._pos` usage in keyParser.js and elsewhere
- * Utilize optional "writev" support when writing packets from
- cipher.encrypt()
- * Built-in support for automatic re-keying, on by default
- * Revisit receiving unexpected/unknown packets
- * Error (fatal or otherwise) or ignore or pass on to user (in some or all
- cases)?
- * Including server/client check for single directional packet types?
- * Check packets for validity or bail as early as possible?
- * Automatic re-key every 2**31 packets after the last key exchange (sent or
- received), as suggested by RFC4344. OpenSSH currently does this.
- * Automatic re-key every so many blocks depending on cipher. RFC4344:
- Because of a birthday property of block ciphers and some modes of
- operation, implementations must be careful not to encrypt too many
- blocks with the same encryption key.
- Let L be the block length (in bits) of an SSH encryption method's
- block cipher (e.g., 128 for AES). If L is at least 128, then, after
- rekeying, an SSH implementation SHOULD NOT encrypt more than 2**(L/4)
- blocks before rekeying again. If L is at least 128, then SSH
- implementations should also attempt to force a rekey before receiving
- more than 2**(L/4) blocks. If L is less than 128 (which is the case
- for older ciphers such as 3DES, Blowfish, CAST-128, and IDEA), then,
- although it may be too expensive to rekey every 2**(L/4) blocks, it
- is still advisable for SSH implementations to follow the original
- recommendation in [RFC4253]: rekey at least once for every gigabyte
- of transmitted data.
- Note that if L is less than or equal to 128, then the recommendation
- in this subsection supersedes the recommendation in Section 3.1. If
- an SSH implementation uses a block cipher with a larger block size
- (e.g., Rijndael with 256-bit blocks), then the recommendations in
- Section 3.1 may supersede the recommendations in this subsection
- (depending on the lengths of the packets).
- */
- 'use strict';
- const { inspect } = require('util');
- const { bindingAvailable, NullCipher, NullDecipher } = require('./crypto.js');
- const {
- COMPAT_CHECKS,
- DISCONNECT_REASON,
- MESSAGE,
- SIGNALS,
- TERMINAL_MODE,
- } = require('./constants.js');
- const {
- DEFAULT_KEXINIT,
- KexInit,
- kexinit,
- onKEXPayload,
- } = require('./kex.js');
- const {
- parseKey,
- } = require('./keyParser.js');
- const MESSAGE_HANDLERS = require('./handlers.js');
- const {
- bufferCopy,
- bufferFill,
- bufferSlice,
- convertSignature,
- sendPacket,
- writeUInt32BE,
- } = require('./utils.js');
- const {
- PacketReader,
- PacketWriter,
- ZlibPacketReader,
- ZlibPacketWriter,
- } = require('./zlib.js');
- const MODULE_VER = require('../../package.json').version;
- const VALID_DISCONNECT_REASONS = new Map(
- Object.values(DISCONNECT_REASON).map((n) => [n, 1])
- );
- const IDENT_RAW = Buffer.from(`SSH-2.0-ssh2js${MODULE_VER}`);
- const IDENT = Buffer.from(`${IDENT_RAW}\r\n`);
- const MAX_LINE_LEN = 8192;
- const MAX_LINES = 1024;
- const PING_PAYLOAD = Buffer.from([
- MESSAGE.GLOBAL_REQUEST,
- // "keepalive@openssh.com"
- 0, 0, 0, 21,
- 107, 101, 101, 112, 97, 108, 105, 118, 101, 64, 111, 112, 101, 110, 115,
- 115, 104, 46, 99, 111, 109,
- // Request a reply
- 1,
- ]);
- const NO_TERMINAL_MODES_BUFFER = Buffer.from([ TERMINAL_MODE.TTY_OP_END ]);
- function noop() {}
- /*
- Inbound:
- * kexinit payload (needed only until exchange hash is generated)
- * raw ident
- * rekey packet queue
- * expected packet (implemented as separate _parse() function?)
- Outbound:
- * kexinit payload (needed only until exchange hash is generated)
- * rekey packet queue
- * kex secret (needed only until NEWKEYS)
- * exchange hash (needed only until NEWKEYS)
- * session ID (set to exchange hash from initial handshake)
- */
- class Protocol {
- constructor(config) {
- const onWrite = config.onWrite;
- if (typeof onWrite !== 'function')
- throw new Error('Missing onWrite function');
- this._onWrite = (data) => { onWrite(data); };
- const onError = config.onError;
- if (typeof onError !== 'function')
- throw new Error('Missing onError function');
- this._onError = (err) => { onError(err); };
- const debug = config.debug;
- this._debug = (typeof debug === 'function'
- ? (msg) => { debug(msg); }
- : undefined);
- const onHeader = config.onHeader;
- this._onHeader = (typeof onHeader === 'function'
- ? (...args) => { onHeader(...args); }
- : noop);
- const onPacket = config.onPacket;
- this._onPacket = (typeof onPacket === 'function'
- ? () => { onPacket(); }
- : noop);
- let onHandshakeComplete = config.onHandshakeComplete;
- if (typeof onHandshakeComplete !== 'function')
- onHandshakeComplete = noop;
- this._onHandshakeComplete = (...args) => {
- this._debug && this._debug('Handshake completed');
- // Process packets queued during a rekey where necessary
- const oldQueue = this._queue;
- if (oldQueue) {
- this._queue = undefined;
- this._debug && this._debug(
- `Draining outbound queue (${oldQueue.length}) ...`
- );
- for (let i = 0; i < oldQueue.length; ++i) {
- const data = oldQueue[i];
- // data === payload only
- // XXX: hacky
- let finalized = this._packetRW.write.finalize(data);
- if (finalized === data) {
- const packet = this._cipher.allocPacket(data.length);
- packet.set(data, 5);
- finalized = packet;
- }
- sendPacket(this, finalized);
- }
- this._debug && this._debug('... finished draining outbound queue');
- }
- onHandshakeComplete(...args);
- };
- this._queue = undefined;
- const messageHandlers = config.messageHandlers;
- if (typeof messageHandlers === 'object' && messageHandlers !== null)
- this._handlers = messageHandlers;
- else
- this._handlers = {};
- this._onPayload = onPayload.bind(this);
- this._server = !!config.server;
- this._banner = undefined;
- let greeting;
- if (this._server) {
- if (typeof config.hostKeys !== 'object' || config.hostKeys === null)
- throw new Error('Missing server host key(s)');
- this._hostKeys = config.hostKeys;
- // Greeting displayed before the ssh identification string is sent, this
- // is usually ignored by most clients
- if (typeof config.greeting === 'string' && config.greeting.length) {
- greeting = (config.greeting.slice(-2) === '\r\n'
- ? config.greeting
- : `${config.greeting}\r\n`);
- }
- // Banner shown after the handshake completes, but before user
- // authentication begins
- if (typeof config.banner === 'string' && config.banner.length) {
- this._banner = (config.banner.slice(-2) === '\r\n'
- ? config.banner
- : `${config.banner}\r\n`);
- }
- } else {
- this._hostKeys = undefined;
- }
- let offer = config.offer;
- if (typeof offer !== 'object' || offer === null)
- offer = DEFAULT_KEXINIT;
- else if (offer.constructor !== KexInit)
- offer = new KexInit(offer);
- this._kex = undefined;
- this._kexinit = undefined;
- this._offer = offer;
- this._cipher = new NullCipher(0, this._onWrite);
- this._decipher = undefined;
- this._skipNextInboundPacket = false;
- this._packetRW = {
- read: new PacketReader(),
- write: new PacketWriter(this),
- };
- this._hostVerifier = (!this._server
- && typeof config.hostVerifier === 'function'
- ? config.hostVerifier
- : undefined);
- this._parse = parseHeader;
- this._buffer = undefined;
- this._authsQueue = [];
- this._authenticated = false;
- this._remoteIdentRaw = undefined;
- let sentIdent;
- if (typeof config.ident === 'string') {
- this._identRaw = Buffer.from(`SSH-2.0-${config.ident}`);
- sentIdent = Buffer.allocUnsafe(this._identRaw.length + 2);
- sentIdent.set(this._identRaw, 0);
- sentIdent[sentIdent.length - 2] = 13; // '\r'
- sentIdent[sentIdent.length - 1] = 10; // '\n'
- } else if (Buffer.isBuffer(config.ident)) {
- const fullIdent = Buffer.allocUnsafe(8 + config.ident.length);
- fullIdent.latin1Write('SSH-2.0-', 0, 8);
- fullIdent.set(config.ident, 8);
- this._identRaw = fullIdent;
- sentIdent = Buffer.allocUnsafe(fullIdent.length + 2);
- sentIdent.set(fullIdent, 0);
- sentIdent[sentIdent.length - 2] = 13; // '\r'
- sentIdent[sentIdent.length - 1] = 10; // '\n'
- } else {
- this._identRaw = IDENT_RAW;
- sentIdent = IDENT;
- }
- this._compatFlags = 0;
- if (this._debug) {
- if (bindingAvailable)
- this._debug('Custom crypto binding available');
- else
- this._debug('Custom crypto binding not available');
- }
- process.nextTick(() => {
- this._debug && this._debug(
- `Local ident: ${inspect(this._identRaw.toString())}`
- );
- if (greeting)
- this._onWrite(greeting);
- this._onWrite(sentIdent);
- });
- }
- _destruct(reason) {
- this._packetRW.read.cleanup();
- this._packetRW.write.cleanup();
- this._cipher && this._cipher.free();
- this._decipher && this._decipher.free();
- if (typeof reason !== 'string' || reason.length === 0)
- reason = 'fatal error';
- this.parse = () => {
- throw new Error(`Instance unusable after ${reason}`);
- };
- this._onWrite = () => {
- throw new Error(`Instance unusable after ${reason}`);
- };
- this._destruct = undefined;
- }
- cleanup() {
- this._destruct && this._destruct();
- }
- parse(chunk, i, len) {
- while (i < len)
- i = this._parse(chunk, i, len);
- }
- // Protocol message API
- // ===========================================================================
- // Common/Shared =============================================================
- // ===========================================================================
- // Global
- // ------
- disconnect(reason) {
- const pktLen = 1 + 4 + 4 + 4;
- // 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})`);
- sendPacket(this, this._packetRW.write.finalize(packet, true), true);
- }
- ping() {
- const p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(PING_PAYLOAD.length);
- packet.set(PING_PAYLOAD, p);
- this._debug && this._debug(
- 'Outbound: Sending ping (GLOBAL_REQUEST: keepalive@openssh.com)'
- );
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- rekey() {
- if (this._kexinit === undefined) {
- this._debug && this._debug('Outbound: Initiated explicit rekey');
- this._queue = [];
- kexinit(this);
- } else {
- this._debug && this._debug('Outbound: Ignoring rekey during handshake');
- }
- }
- // 'ssh-connection' service-specific
- // ---------------------------------
- requestSuccess(data) {
- let p = this._packetRW.write.allocStart;
- let packet;
- if (Buffer.isBuffer(data)) {
- packet = this._packetRW.write.alloc(1 + data.length);
- packet[p] = MESSAGE.REQUEST_SUCCESS;
- packet.set(data, ++p);
- } else {
- packet = this._packetRW.write.alloc(1);
- packet[p] = MESSAGE.REQUEST_SUCCESS;
- }
- this._debug && this._debug('Outbound: Sending REQUEST_SUCCESS');
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- requestFailure() {
- const p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(1);
- packet[p] = MESSAGE.REQUEST_FAILURE;
- this._debug && this._debug('Outbound: Sending REQUEST_FAILURE');
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- channelSuccess(chan) {
- // Does not consume window space
- let p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(1 + 4);
- packet[p] = MESSAGE.CHANNEL_SUCCESS;
- writeUInt32BE(packet, chan, ++p);
- this._debug && this._debug(`Outbound: Sending CHANNEL_SUCCESS (r:${chan})`);
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- channelFailure(chan) {
- // Does not consume window space
- let p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(1 + 4);
- packet[p] = MESSAGE.CHANNEL_FAILURE;
- writeUInt32BE(packet, chan, ++p);
- this._debug && this._debug(`Outbound: Sending CHANNEL_FAILURE (r:${chan})`);
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- channelEOF(chan) {
- // Does not consume window space
- let p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(1 + 4);
- packet[p] = MESSAGE.CHANNEL_EOF;
- writeUInt32BE(packet, chan, ++p);
- this._debug && this._debug(`Outbound: Sending CHANNEL_EOF (r:${chan})`);
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- channelClose(chan) {
- // Does not consume window space
- let p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(1 + 4);
- packet[p] = MESSAGE.CHANNEL_CLOSE;
- writeUInt32BE(packet, chan, ++p);
- this._debug && this._debug(`Outbound: Sending CHANNEL_CLOSE (r:${chan})`);
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- channelWindowAdjust(chan, amount) {
- // Does not consume window space
- let p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(1 + 4 + 4);
- packet[p] = MESSAGE.CHANNEL_WINDOW_ADJUST;
- writeUInt32BE(packet, chan, ++p);
- writeUInt32BE(packet, amount, p += 4);
- this._debug && this._debug(
- `Outbound: Sending CHANNEL_WINDOW_ADJUST (r:${chan}, ${amount})`
- );
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- channelData(chan, data) {
- const isBuffer = Buffer.isBuffer(data);
- const dataLen = (isBuffer ? data.length : Buffer.byteLength(data));
- let p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(1 + 4 + 4 + dataLen);
- packet[p] = MESSAGE.CHANNEL_DATA;
- writeUInt32BE(packet, chan, ++p);
- writeUInt32BE(packet, dataLen, p += 4);
- if (isBuffer)
- packet.set(data, p += 4);
- else
- packet.utf8Write(data, p += 4, dataLen);
- this._debug && this._debug(
- `Outbound: Sending CHANNEL_DATA (r:${chan}, ${dataLen})`
- );
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- channelExtData(chan, data, type) {
- const isBuffer = Buffer.isBuffer(data);
- const dataLen = (isBuffer ? data.length : Buffer.byteLength(data));
- let p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(1 + 4 + 4 + 4 + dataLen);
- packet[p] = MESSAGE.CHANNEL_EXTENDED_DATA;
- writeUInt32BE(packet, chan, ++p);
- writeUInt32BE(packet, type, p += 4);
- writeUInt32BE(packet, dataLen, p += 4);
- if (isBuffer)
- packet.set(data, p += 4);
- else
- packet.utf8Write(data, p += 4, dataLen);
- this._debug
- && this._debug(`Outbound: Sending CHANNEL_EXTENDED_DATA (r:${chan})`);
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- channelOpenConfirm(remote, local, initWindow, maxPacket) {
- let p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(1 + 4 + 4 + 4 + 4);
- packet[p] = MESSAGE.CHANNEL_OPEN_CONFIRMATION;
- writeUInt32BE(packet, remote, ++p);
- writeUInt32BE(packet, local, p += 4);
- writeUInt32BE(packet, initWindow, p += 4);
- writeUInt32BE(packet, maxPacket, p += 4);
- this._debug && this._debug(
- `Outbound: Sending CHANNEL_OPEN_CONFIRMATION (r:${remote}, l:${local})`
- );
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- channelOpenFail(remote, reason, desc) {
- if (typeof desc !== 'string')
- desc = '';
- const descLen = Buffer.byteLength(desc);
- let p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(1 + 4 + 4 + 4 + descLen + 4);
- packet[p] = MESSAGE.CHANNEL_OPEN_FAILURE;
- writeUInt32BE(packet, remote, ++p);
- writeUInt32BE(packet, reason, p += 4);
- writeUInt32BE(packet, descLen, p += 4);
- p += 4;
- if (descLen) {
- packet.utf8Write(desc, p, descLen);
- p += descLen;
- }
- writeUInt32BE(packet, 0, p); // Empty language tag
- this._debug
- && this._debug(`Outbound: Sending CHANNEL_OPEN_FAILURE (r:${remote})`);
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- // ===========================================================================
- // Client-specific ===========================================================
- // ===========================================================================
- // Global
- // ------
- service(name) {
- if (this._server)
- throw new Error('Client-only method called in server mode');
- const nameLen = Buffer.byteLength(name);
- let p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(1 + 4 + nameLen);
- packet[p] = MESSAGE.SERVICE_REQUEST;
- writeUInt32BE(packet, nameLen, ++p);
- packet.utf8Write(name, p += 4, nameLen);
- this._debug && this._debug(`Outbound: Sending SERVICE_REQUEST (${name})`);
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- // 'ssh-userauth' service-specific
- // -------------------------------
- authPassword(username, password, newPassword) {
- if (this._server)
- throw new Error('Client-only method called in server mode');
- const userLen = Buffer.byteLength(username);
- const passLen = Buffer.byteLength(password);
- const newPassLen = (newPassword ? Buffer.byteLength(newPassword) : 0);
- let p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(
- 1 + 4 + userLen + 4 + 14 + 4 + 8 + 1 + 4 + passLen
- + (newPassword ? 4 + newPassLen : 0)
- );
- packet[p] = MESSAGE.USERAUTH_REQUEST;
- writeUInt32BE(packet, userLen, ++p);
- packet.utf8Write(username, p += 4, userLen);
- writeUInt32BE(packet, 14, p += userLen);
- packet.utf8Write('ssh-connection', p += 4, 14);
- writeUInt32BE(packet, 8, p += 14);
- packet.utf8Write('password', p += 4, 8);
- packet[p += 8] = (newPassword ? 1 : 0);
- writeUInt32BE(packet, passLen, ++p);
- if (Buffer.isBuffer(password))
- bufferCopy(password, packet, 0, passLen, p += 4);
- else
- packet.utf8Write(password, p += 4, passLen);
- if (newPassword) {
- writeUInt32BE(packet, newPassLen, p += passLen);
- if (Buffer.isBuffer(newPassword))
- bufferCopy(newPassword, packet, 0, newPassLen, p += 4);
- else
- packet.utf8Write(newPassword, p += 4, newPassLen);
- this._debug && this._debug(
- 'Outbound: Sending USERAUTH_REQUEST (changed password)'
- );
- } else {
- this._debug && this._debug(
- 'Outbound: Sending USERAUTH_REQUEST (password)'
- );
- }
- this._authsQueue.push('password');
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- authPK(username, pubKey, cbSign) {
- if (this._server)
- throw new Error('Client-only method called in server mode');
- pubKey = parseKey(pubKey);
- if (pubKey instanceof Error)
- throw new Error('Invalid key');
- const keyType = pubKey.type;
- pubKey = pubKey.getPublicSSH();
- const userLen = Buffer.byteLength(username);
- const algoLen = Buffer.byteLength(keyType);
- const pubKeyLen = pubKey.length;
- const sessionID = this._kex.sessionID;
- const sesLen = sessionID.length;
- const payloadLen =
- (cbSign ? 4 + sesLen : 0)
- + 1 + 4 + userLen + 4 + 14 + 4 + 9 + 1 + 4 + algoLen + 4 + pubKeyLen;
- let packet;
- let p;
- if (cbSign) {
- packet = Buffer.allocUnsafe(payloadLen);
- p = 0;
- writeUInt32BE(packet, sesLen, p);
- packet.set(sessionID, p += 4);
- p += sesLen;
- } else {
- packet = this._packetRW.write.alloc(payloadLen);
- p = this._packetRW.write.allocStart;
- }
- packet[p] = MESSAGE.USERAUTH_REQUEST;
- writeUInt32BE(packet, userLen, ++p);
- packet.utf8Write(username, p += 4, userLen);
- writeUInt32BE(packet, 14, p += userLen);
- packet.utf8Write('ssh-connection', p += 4, 14);
- writeUInt32BE(packet, 9, p += 14);
- packet.utf8Write('publickey', p += 4, 9);
- packet[p += 9] = (cbSign ? 1 : 0);
- writeUInt32BE(packet, algoLen, ++p);
- packet.utf8Write(keyType, p += 4, algoLen);
- writeUInt32BE(packet, pubKeyLen, p += algoLen);
- packet.set(pubKey, p += 4);
- if (!cbSign) {
- this._authsQueue.push('publickey');
- this._debug && this._debug(
- 'Outbound: Sending USERAUTH_REQUEST (publickey -- check)'
- );
- sendPacket(this, this._packetRW.write.finalize(packet));
- return;
- }
- cbSign(packet, (signature) => {
- signature = convertSignature(signature, keyType);
- if (signature === false)
- throw new Error('Error while converting handshake signature');
- const sigLen = signature.length;
- p = this._packetRW.write.allocStart;
- packet = this._packetRW.write.alloc(
- 1 + 4 + userLen + 4 + 14 + 4 + 9 + 1 + 4 + algoLen + 4 + pubKeyLen + 4
- + 4 + algoLen + 4 + sigLen
- );
- // TODO: simply copy from original "packet" to new `packet` to avoid
- // having to write each individual field a second time?
- packet[p] = MESSAGE.USERAUTH_REQUEST;
- writeUInt32BE(packet, userLen, ++p);
- packet.utf8Write(username, p += 4, userLen);
- writeUInt32BE(packet, 14, p += userLen);
- packet.utf8Write('ssh-connection', p += 4, 14);
- writeUInt32BE(packet, 9, p += 14);
- packet.utf8Write('publickey', p += 4, 9);
- packet[p += 9] = 1;
- writeUInt32BE(packet, algoLen, ++p);
- packet.utf8Write(keyType, p += 4, algoLen);
- writeUInt32BE(packet, pubKeyLen, p += algoLen);
- packet.set(pubKey, p += 4);
- writeUInt32BE(packet, 4 + algoLen + 4 + sigLen, p += pubKeyLen);
- writeUInt32BE(packet, algoLen, p += 4);
- packet.utf8Write(keyType, p += 4, algoLen);
- writeUInt32BE(packet, sigLen, p += algoLen);
- packet.set(signature, p += 4);
- // Servers shouldn't send packet type 60 in response to signed publickey
- // attempts, but if they do, interpret as type 60.
- this._authsQueue.push('publickey');
- this._debug && this._debug(
- 'Outbound: Sending USERAUTH_REQUEST (publickey)'
- );
- sendPacket(this, this._packetRW.write.finalize(packet));
- });
- }
- authHostbased(username, pubKey, hostname, userlocal, cbSign) {
- // TODO: Make DRY by sharing similar code with authPK()
- if (this._server)
- throw new Error('Client-only method called in server mode');
- pubKey = parseKey(pubKey);
- if (pubKey instanceof Error)
- throw new Error('Invalid key');
- const keyType = pubKey.type;
- pubKey = pubKey.getPublicSSH();
- const userLen = Buffer.byteLength(username);
- const algoLen = Buffer.byteLength(keyType);
- const pubKeyLen = pubKey.length;
- const sessionID = this._kex.sessionID;
- const sesLen = sessionID.length;
- const hostnameLen = Buffer.byteLength(hostname);
- const userlocalLen = Buffer.byteLength(userlocal);
- const data = Buffer.allocUnsafe(
- 4 + sesLen + 1 + 4 + userLen + 4 + 14 + 4 + 9 + 4 + algoLen
- + 4 + pubKeyLen + 4 + hostnameLen + 4 + userlocalLen
- );
- let p = 0;
- writeUInt32BE(data, sesLen, p);
- data.set(sessionID, p += 4);
- data[p += sesLen] = MESSAGE.USERAUTH_REQUEST;
- writeUInt32BE(data, userLen, ++p);
- data.utf8Write(username, p += 4, userLen);
- writeUInt32BE(data, 14, p += userLen);
- data.utf8Write('ssh-connection', p += 4, 14);
- writeUInt32BE(data, 9, p += 14);
- data.utf8Write('hostbased', p += 4, 9);
- writeUInt32BE(data, algoLen, p += 9);
- data.utf8Write(keyType, p += 4, algoLen);
- writeUInt32BE(data, pubKeyLen, p += algoLen);
- data.set(pubKey, p += 4);
- writeUInt32BE(data, hostnameLen, p += pubKeyLen);
- data.utf8Write(hostname, p += 4, hostnameLen);
- writeUInt32BE(data, userlocalLen, p += hostnameLen);
- data.utf8Write(userlocal, p += 4, userlocalLen);
- cbSign(data, (signature) => {
- signature = convertSignature(signature, keyType);
- if (!signature)
- throw new Error('Error while converting handshake signature');
- const sigLen = signature.length;
- const reqDataLen = (data.length - sesLen - 4);
- p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(
- reqDataLen + 4 + 4 + algoLen + 4 + sigLen
- );
- bufferCopy(data, packet, 4 + sesLen, data.length, p);
- writeUInt32BE(packet, 4 + algoLen + 4 + sigLen, p += reqDataLen);
- writeUInt32BE(packet, algoLen, p += 4);
- packet.utf8Write(keyType, p += 4, algoLen);
- writeUInt32BE(packet, sigLen, p += algoLen);
- packet.set(signature, p += 4);
- this._authsQueue.push('hostbased');
- this._debug && this._debug(
- 'Outbound: Sending USERAUTH_REQUEST (hostbased)'
- );
- sendPacket(this, this._packetRW.write.finalize(packet));
- });
- }
- authKeyboard(username) {
- if (this._server)
- throw new Error('Client-only method called in server mode');
- const userLen = Buffer.byteLength(username);
- let p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(
- 1 + 4 + userLen + 4 + 14 + 4 + 20 + 4 + 4
- );
- packet[p] = MESSAGE.USERAUTH_REQUEST;
- writeUInt32BE(packet, userLen, ++p);
- packet.utf8Write(username, p += 4, userLen);
- writeUInt32BE(packet, 14, p += userLen);
- packet.utf8Write('ssh-connection', p += 4, 14);
- writeUInt32BE(packet, 20, p += 14);
- packet.utf8Write('keyboard-interactive', p += 4, 20);
- writeUInt32BE(packet, 0, p += 20);
- writeUInt32BE(packet, 0, p += 4);
- this._authsQueue.push('keyboard-interactive');
- this._debug && this._debug(
- 'Outbound: Sending USERAUTH_REQUEST (keyboard-interactive)'
- );
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- authNone(username) {
- if (this._server)
- throw new Error('Client-only method called in server mode');
- const userLen = Buffer.byteLength(username);
- let p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(1 + 4 + userLen + 4 + 14 + 4 + 4);
- packet[p] = MESSAGE.USERAUTH_REQUEST;
- writeUInt32BE(packet, userLen, ++p);
- packet.utf8Write(username, p += 4, userLen);
- writeUInt32BE(packet, 14, p += userLen);
- packet.utf8Write('ssh-connection', p += 4, 14);
- writeUInt32BE(packet, 4, p += 14);
- packet.utf8Write('none', p += 4, 4);
- this._authsQueue.push('none');
- this._debug && this._debug('Outbound: Sending USERAUTH_REQUEST (none)');
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- authInfoRes(responses) {
- if (this._server)
- throw new Error('Client-only method called in server mode');
- let responsesTotalLen = 0;
- let responseLens;
- if (responses) {
- responseLens = new Array(responses.length);
- for (let i = 0; i < responses.length; ++i) {
- const len = Buffer.byteLength(responses[i]);
- responseLens[i] = len;
- responsesTotalLen += 4 + len;
- }
- }
- let p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(1 + 4 + responsesTotalLen);
- packet[p] = MESSAGE.USERAUTH_INFO_RESPONSE;
- if (responses) {
- writeUInt32BE(packet, responses.length, ++p);
- p += 4;
- for (let i = 0; i < responses.length; ++i) {
- const len = responseLens[i];
- writeUInt32BE(packet, len, p);
- p += 4;
- if (len) {
- packet.utf8Write(responses[i], p, len);
- p += len;
- }
- }
- } else {
- writeUInt32BE(packet, 0, ++p);
- }
- this._debug && this._debug('Outbound: Sending USERAUTH_INFO_RESPONSE');
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- // 'ssh-connection' service-specific
- // ---------------------------------
- tcpipForward(bindAddr, bindPort, wantReply) {
- if (this._server)
- throw new Error('Client-only method called in server mode');
- const addrLen = Buffer.byteLength(bindAddr);
- let p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(1 + 4 + 13 + 1 + 4 + addrLen + 4);
- packet[p] = MESSAGE.GLOBAL_REQUEST;
- writeUInt32BE(packet, 13, ++p);
- packet.utf8Write('tcpip-forward', p += 4, 13);
- packet[p += 13] = (wantReply === undefined || wantReply === true ? 1 : 0);
- writeUInt32BE(packet, addrLen, ++p);
- packet.utf8Write(bindAddr, p += 4, addrLen);
- writeUInt32BE(packet, bindPort, p += addrLen);
- this._debug
- && this._debug('Outbound: Sending GLOBAL_REQUEST (tcpip-forward)');
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- cancelTcpipForward(bindAddr, bindPort, wantReply) {
- if (this._server)
- throw new Error('Client-only method called in server mode');
- const addrLen = Buffer.byteLength(bindAddr);
- let p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(1 + 4 + 20 + 1 + 4 + addrLen + 4);
- packet[p] = MESSAGE.GLOBAL_REQUEST;
- writeUInt32BE(packet, 20, ++p);
- packet.utf8Write('cancel-tcpip-forward', p += 4, 20);
- packet[p += 20] = (wantReply === undefined || wantReply === true ? 1 : 0);
- writeUInt32BE(packet, addrLen, ++p);
- packet.utf8Write(bindAddr, p += 4, addrLen);
- writeUInt32BE(packet, bindPort, p += addrLen);
- this._debug
- && this._debug('Outbound: Sending GLOBAL_REQUEST (cancel-tcpip-forward)');
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- openssh_streamLocalForward(socketPath, wantReply) {
- if (this._server)
- throw new Error('Client-only method called in server mode');
- const socketPathLen = Buffer.byteLength(socketPath);
- let p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(
- 1 + 4 + 31 + 1 + 4 + socketPathLen
- );
- packet[p] = MESSAGE.GLOBAL_REQUEST;
- writeUInt32BE(packet, 31, ++p);
- packet.utf8Write('streamlocal-forward@openssh.com', p += 4, 31);
- packet[p += 31] = (wantReply === undefined || wantReply === true ? 1 : 0);
- writeUInt32BE(packet, socketPathLen, ++p);
- packet.utf8Write(socketPath, p += 4, socketPathLen);
- this._debug && this._debug(
- 'Outbound: Sending GLOBAL_REQUEST (streamlocal-forward@openssh.com)'
- );
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- openssh_cancelStreamLocalForward(socketPath, wantReply) {
- if (this._server)
- throw new Error('Client-only method called in server mode');
- const socketPathLen = Buffer.byteLength(socketPath);
- let p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(
- 1 + 4 + 38 + 1 + 4 + socketPathLen
- );
- packet[p] = MESSAGE.GLOBAL_REQUEST;
- writeUInt32BE(packet, 38, ++p);
- packet.utf8Write('cancel-streamlocal-forward@openssh.com', p += 4, 38);
- packet[p += 38] = (wantReply === undefined || wantReply === true ? 1 : 0);
- writeUInt32BE(packet, socketPathLen, ++p);
- packet.utf8Write(socketPath, p += 4, socketPathLen);
- if (this._debug) {
- this._debug(
- 'Outbound: Sending GLOBAL_REQUEST '
- + '(cancel-streamlocal-forward@openssh.com)'
- );
- }
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- directTcpip(chan, initWindow, maxPacket, cfg) {
- if (this._server)
- throw new Error('Client-only method called in server mode');
- const srcLen = Buffer.byteLength(cfg.srcIP);
- const dstLen = Buffer.byteLength(cfg.dstIP);
- let p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(
- 1 + 4 + 12 + 4 + 4 + 4 + 4 + srcLen + 4 + 4 + dstLen + 4
- );
- packet[p] = MESSAGE.CHANNEL_OPEN;
- writeUInt32BE(packet, 12, ++p);
- packet.utf8Write('direct-tcpip', p += 4, 12);
- writeUInt32BE(packet, chan, p += 12);
- writeUInt32BE(packet, initWindow, p += 4);
- writeUInt32BE(packet, maxPacket, p += 4);
- writeUInt32BE(packet, dstLen, p += 4);
- packet.utf8Write(cfg.dstIP, p += 4, dstLen);
- writeUInt32BE(packet, cfg.dstPort, p += dstLen);
- writeUInt32BE(packet, srcLen, p += 4);
- packet.utf8Write(cfg.srcIP, p += 4, srcLen);
- writeUInt32BE(packet, cfg.srcPort, p += srcLen);
- this._debug && this._debug(
- `Outbound: Sending CHANNEL_OPEN (r:${chan}, direct-tcpip)`
- );
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- openssh_directStreamLocal(chan, initWindow, maxPacket, cfg) {
- if (this._server)
- throw new Error('Client-only method called in server mode');
- const pathLen = Buffer.byteLength(cfg.socketPath);
- let p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(
- 1 + 4 + 30 + 4 + 4 + 4 + 4 + pathLen + 4 + 4
- );
- packet[p] = MESSAGE.CHANNEL_OPEN;
- writeUInt32BE(packet, 30, ++p);
- packet.utf8Write('direct-streamlocal@openssh.com', p += 4, 30);
- writeUInt32BE(packet, chan, p += 30);
- writeUInt32BE(packet, initWindow, p += 4);
- writeUInt32BE(packet, maxPacket, p += 4);
- writeUInt32BE(packet, pathLen, p += 4);
- packet.utf8Write(cfg.socketPath, p += 4, pathLen);
- // zero-fill reserved fields (string and uint32)
- bufferFill(packet, 0, p += pathLen, p + 8);
- if (this._debug) {
- this._debug(
- 'Outbound: Sending CHANNEL_OPEN '
- + `(r:${chan}, direct-streamlocal@openssh.com)`
- );
- }
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- openssh_noMoreSessions(wantReply) {
- if (this._server)
- throw new Error('Client-only method called in server mode');
- let p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(1 + 4 + 28 + 1);
- packet[p] = MESSAGE.GLOBAL_REQUEST;
- writeUInt32BE(packet, 28, ++p);
- packet.utf8Write('no-more-sessions@openssh.com', p += 4, 28);
- packet[p += 28] = (wantReply === undefined || wantReply === true ? 1 : 0);
- this._debug && this._debug(
- 'Outbound: Sending GLOBAL_REQUEST (no-more-sessions@openssh.com)'
- );
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- session(chan, initWindow, maxPacket) {
- if (this._server)
- throw new Error('Client-only method called in server mode');
- // Does not consume window space
- let p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(1 + 4 + 7 + 4 + 4 + 4);
- packet[p] = MESSAGE.CHANNEL_OPEN;
- writeUInt32BE(packet, 7, ++p);
- packet.utf8Write('session', p += 4, 7);
- writeUInt32BE(packet, chan, p += 7);
- writeUInt32BE(packet, initWindow, p += 4);
- writeUInt32BE(packet, maxPacket, p += 4);
- this._debug
- && this._debug(`Outbound: Sending CHANNEL_OPEN (r:${chan}, session)`);
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- windowChange(chan, rows, cols, height, width) {
- if (this._server)
- throw new Error('Client-only method called in server mode');
- // Does not consume window space
- let p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(
- 1 + 4 + 4 + 13 + 1 + 4 + 4 + 4 + 4
- );
- packet[p] = MESSAGE.CHANNEL_REQUEST;
- writeUInt32BE(packet, chan, ++p);
- writeUInt32BE(packet, 13, p += 4);
- packet.utf8Write('window-change', p += 4, 13);
- packet[p += 13] = 0;
- writeUInt32BE(packet, cols, ++p);
- writeUInt32BE(packet, rows, p += 4);
- writeUInt32BE(packet, width, p += 4);
- writeUInt32BE(packet, height, p += 4);
- this._debug && this._debug(
- `Outbound: Sending CHANNEL_REQUEST (r:${chan}, window-change)`
- );
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- pty(chan, rows, cols, height, width, term, modes, wantReply) {
- if (this._server)
- throw new Error('Client-only method called in server mode');
- // Does not consume window space
- if (!term || !term.length)
- term = 'vt100';
- if (modes
- && !Buffer.isBuffer(modes)
- && !Array.isArray(modes)
- && typeof modes === 'object'
- && modes !== null) {
- modes = modesToBytes(modes);
- }
- if (!modes || !modes.length)
- modes = NO_TERMINAL_MODES_BUFFER;
- const termLen = term.length;
- const modesLen = modes.length;
- let p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(
- 1 + 4 + 4 + 7 + 1 + 4 + termLen + 4 + 4 + 4 + 4 + 4 + modesLen
- );
- packet[p] = MESSAGE.CHANNEL_REQUEST;
- writeUInt32BE(packet, chan, ++p);
- writeUInt32BE(packet, 7, p += 4);
- packet.utf8Write('pty-req', p += 4, 7);
- packet[p += 7] = (wantReply === undefined || wantReply === true ? 1 : 0);
- writeUInt32BE(packet, termLen, ++p);
- packet.utf8Write(term, p += 4, termLen);
- writeUInt32BE(packet, cols, p += termLen);
- writeUInt32BE(packet, rows, p += 4);
- writeUInt32BE(packet, width, p += 4);
- writeUInt32BE(packet, height, p += 4);
- writeUInt32BE(packet, modesLen, p += 4);
- p += 4;
- if (Array.isArray(modes)) {
- for (let i = 0; i < modesLen; ++i)
- packet[p++] = modes[i];
- } else if (Buffer.isBuffer(modes)) {
- packet.set(modes, p);
- }
- this._debug
- && this._debug(`Outbound: Sending CHANNEL_REQUEST (r:${chan}, pty-req)`);
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- shell(chan, wantReply) {
- if (this._server)
- throw new Error('Client-only method called in server mode');
- // Does not consume window space
- let p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(1 + 4 + 4 + 5 + 1);
- packet[p] = MESSAGE.CHANNEL_REQUEST;
- writeUInt32BE(packet, chan, ++p);
- writeUInt32BE(packet, 5, p += 4);
- packet.utf8Write('shell', p += 4, 5);
- packet[p += 5] = (wantReply === undefined || wantReply === true ? 1 : 0);
- this._debug
- && this._debug(`Outbound: Sending CHANNEL_REQUEST (r:${chan}, shell)`);
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- exec(chan, cmd, wantReply) {
- if (this._server)
- throw new Error('Client-only method called in server mode');
- // Does not consume window space
- const isBuf = Buffer.isBuffer(cmd);
- const cmdLen = (isBuf ? cmd.length : Buffer.byteLength(cmd));
- let p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(1 + 4 + 4 + 4 + 1 + 4 + cmdLen);
- packet[p] = MESSAGE.CHANNEL_REQUEST;
- writeUInt32BE(packet, chan, ++p);
- writeUInt32BE(packet, 4, p += 4);
- packet.utf8Write('exec', p += 4, 4);
- packet[p += 4] = (wantReply === undefined || wantReply === true ? 1 : 0);
- writeUInt32BE(packet, cmdLen, ++p);
- if (isBuf)
- packet.set(cmd, p += 4);
- else
- packet.utf8Write(cmd, p += 4, cmdLen);
- this._debug && this._debug(
- `Outbound: Sending CHANNEL_REQUEST (r:${chan}, exec: ${cmd})`
- );
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- signal(chan, signal) {
- if (this._server)
- throw new Error('Client-only method called in server mode');
- // Does not consume window space
- const origSignal = signal;
- signal = signal.toUpperCase();
- if (signal.slice(0, 3) === 'SIG')
- signal = signal.slice(3);
- if (SIGNALS[signal] !== 1)
- throw new Error(`Invalid signal: ${origSignal}`);
- const signalLen = signal.length;
- let p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(
- 1 + 4 + 4 + 6 + 1 + 4 + signalLen
- );
- packet[p] = MESSAGE.CHANNEL_REQUEST;
- writeUInt32BE(packet, chan, ++p);
- writeUInt32BE(packet, 6, p += 4);
- packet.utf8Write('signal', p += 4, 6);
- packet[p += 6] = 0;
- writeUInt32BE(packet, signalLen, ++p);
- packet.utf8Write(signal, p += 4, signalLen);
- this._debug && this._debug(
- `Outbound: Sending CHANNEL_REQUEST (r:${chan}, signal: ${signal})`
- );
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- env(chan, key, val, wantReply) {
- if (this._server)
- throw new Error('Client-only method called in server mode');
- // Does not consume window space
- const keyLen = Buffer.byteLength(key);
- const isBuf = Buffer.isBuffer(val);
- const valLen = (isBuf ? val.length : Buffer.byteLength(val));
- let p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(
- 1 + 4 + 4 + 3 + 1 + 4 + keyLen + 4 + valLen
- );
- packet[p] = MESSAGE.CHANNEL_REQUEST;
- writeUInt32BE(packet, chan, ++p);
- writeUInt32BE(packet, 3, p += 4);
- packet.utf8Write('env', p += 4, 3);
- packet[p += 3] = (wantReply === undefined || wantReply === true ? 1 : 0);
- writeUInt32BE(packet, keyLen, ++p);
- packet.utf8Write(key, p += 4, keyLen);
- writeUInt32BE(packet, valLen, p += keyLen);
- if (isBuf)
- packet.set(val, p += 4);
- else
- packet.utf8Write(val, p += 4, valLen);
- this._debug && this._debug(
- `Outbound: Sending CHANNEL_REQUEST (r:${chan}, env: ${key}=${val})`
- );
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- x11Forward(chan, cfg, wantReply) {
- if (this._server)
- throw new Error('Client-only method called in server mode');
- // Does not consume window space
- const protocol = cfg.protocol;
- const cookie = cfg.cookie;
- const isBufProto = Buffer.isBuffer(protocol);
- const protoLen = (isBufProto
- ? protocol.length
- : Buffer.byteLength(protocol));
- const isBufCookie = Buffer.isBuffer(cookie);
- const cookieLen = (isBufCookie
- ? cookie.length
- : Buffer.byteLength(cookie));
- let p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(
- 1 + 4 + 4 + 7 + 1 + 1 + 4 + protoLen + 4 + cookieLen + 4
- );
- packet[p] = MESSAGE.CHANNEL_REQUEST;
- writeUInt32BE(packet, chan, ++p);
- writeUInt32BE(packet, 7, p += 4);
- packet.utf8Write('x11-req', p += 4, 7);
- packet[p += 7] = (wantReply === undefined || wantReply === true ? 1 : 0);
- packet[++p] = (cfg.single ? 1 : 0);
- writeUInt32BE(packet, protoLen, ++p);
- if (isBufProto)
- packet.set(protocol, p += 4);
- else
- packet.utf8Write(protocol, p += 4, protoLen);
- writeUInt32BE(packet, cookieLen, p += protoLen);
- if (isBufCookie)
- packet.set(cookie, p += 4);
- else
- packet.latin1Write(cookie, p += 4, cookieLen);
- writeUInt32BE(packet, (cfg.screen || 0), p += cookieLen);
- this._debug
- && this._debug(`Outbound: Sending CHANNEL_REQUEST (r:${chan}, x11-req)`);
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- subsystem(chan, name, wantReply) {
- if (this._server)
- throw new Error('Client-only method called in server mode');
- // Does not consume window space
- const nameLen = Buffer.byteLength(name);
- let p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(1 + 4 + 4 + 9 + 1 + 4 + nameLen);
- packet[p] = MESSAGE.CHANNEL_REQUEST;
- writeUInt32BE(packet, chan, ++p);
- writeUInt32BE(packet, 9, p += 4);
- packet.utf8Write('subsystem', p += 4, 9);
- packet[p += 9] = (wantReply === undefined || wantReply === true ? 1 : 0);
- writeUInt32BE(packet, nameLen, ++p);
- packet.utf8Write(name, p += 4, nameLen);
- this._debug && this._debug(
- `Outbound: Sending CHANNEL_REQUEST (r:${chan}, subsystem: ${name})`
- );
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- openssh_agentForward(chan, wantReply) {
- if (this._server)
- throw new Error('Client-only method called in server mode');
- // Does not consume window space
- let p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(1 + 4 + 4 + 26 + 1);
- packet[p] = MESSAGE.CHANNEL_REQUEST;
- writeUInt32BE(packet, chan, ++p);
- writeUInt32BE(packet, 26, p += 4);
- packet.utf8Write('auth-agent-req@openssh.com', p += 4, 26);
- packet[p += 26] = (wantReply === undefined || wantReply === true ? 1 : 0);
- if (this._debug) {
- this._debug(
- 'Outbound: Sending CHANNEL_REQUEST '
- + `(r:${chan}, auth-agent-req@openssh.com)`
- );
- }
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- openssh_hostKeysProve(keys) {
- if (this._server)
- throw new Error('Client-only method called in server mode');
- let keysTotal = 0;
- const publicKeys = [];
- for (const key of keys) {
- const publicKey = key.getPublicSSH();
- keysTotal += 4 + publicKey.length;
- publicKeys.push(publicKey);
- }
- let p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(1 + 4 + 29 + 1 + keysTotal);
- packet[p] = MESSAGE.GLOBAL_REQUEST;
- writeUInt32BE(packet, 29, ++p);
- packet.utf8Write('hostkeys-prove-00@openssh.com', p += 4, 29);
- packet[p += 29] = 1; // want reply
- ++p;
- for (const buf of publicKeys) {
- writeUInt32BE(packet, buf.length, p);
- bufferCopy(buf, packet, 0, buf.length, p += 4);
- p += buf.length;
- }
- if (this._debug) {
- this._debug(
- 'Outbound: Sending GLOBAL_REQUEST (hostkeys-prove-00@openssh.com)'
- );
- }
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- // ===========================================================================
- // Server-specific ===========================================================
- // ===========================================================================
- // Global
- // ------
- serviceAccept(svcName) {
- if (!this._server)
- throw new Error('Server-only method called in client mode');
- const svcNameLen = Buffer.byteLength(svcName);
- let p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(1 + 4 + svcNameLen);
- packet[p] = MESSAGE.SERVICE_ACCEPT;
- writeUInt32BE(packet, svcNameLen, ++p);
- packet.utf8Write(svcName, p += 4, svcNameLen);
- this._debug && this._debug(`Outbound: Sending SERVICE_ACCEPT (${svcName})`);
- sendPacket(this, this._packetRW.write.finalize(packet));
- if (this._server && this._banner && svcName === 'ssh-userauth') {
- const banner = this._banner;
- this._banner = undefined; // Prevent banner from being displayed again
- const bannerLen = Buffer.byteLength(banner);
- p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(1 + 4 + bannerLen + 4);
- packet[p] = MESSAGE.USERAUTH_BANNER;
- writeUInt32BE(packet, bannerLen, ++p);
- packet.utf8Write(banner, p += 4, bannerLen);
- writeUInt32BE(packet, 0, p += bannerLen); // Empty language tag
- this._debug && this._debug('Outbound: Sending USERAUTH_BANNER');
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- }
- // 'ssh-connection' service-specific
- forwardedTcpip(chan, initWindow, maxPacket, cfg) {
- if (!this._server)
- throw new Error('Server-only method called in client mode');
- const boundAddrLen = Buffer.byteLength(cfg.boundAddr);
- const remoteAddrLen = Buffer.byteLength(cfg.remoteAddr);
- let p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(
- 1 + 4 + 15 + 4 + 4 + 4 + 4 + boundAddrLen + 4 + 4 + remoteAddrLen + 4
- );
- packet[p] = MESSAGE.CHANNEL_OPEN;
- writeUInt32BE(packet, 15, ++p);
- packet.utf8Write('forwarded-tcpip', p += 4, 15);
- writeUInt32BE(packet, chan, p += 15);
- writeUInt32BE(packet, initWindow, p += 4);
- writeUInt32BE(packet, maxPacket, p += 4);
- writeUInt32BE(packet, boundAddrLen, p += 4);
- packet.utf8Write(cfg.boundAddr, p += 4, boundAddrLen);
- writeUInt32BE(packet, cfg.boundPort, p += boundAddrLen);
- writeUInt32BE(packet, remoteAddrLen, p += 4);
- packet.utf8Write(cfg.remoteAddr, p += 4, remoteAddrLen);
- writeUInt32BE(packet, cfg.remotePort, p += remoteAddrLen);
- this._debug && this._debug(
- `Outbound: Sending CHANNEL_OPEN (r:${chan}, forwarded-tcpip)`
- );
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- x11(chan, initWindow, maxPacket, cfg) {
- if (!this._server)
- throw new Error('Server-only method called in client mode');
- const addrLen = Buffer.byteLength(cfg.originAddr);
- let p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(
- 1 + 4 + 3 + 4 + 4 + 4 + 4 + addrLen + 4
- );
- packet[p] = MESSAGE.CHANNEL_OPEN;
- writeUInt32BE(packet, 3, ++p);
- packet.utf8Write('x11', p += 4, 3);
- writeUInt32BE(packet, chan, p += 3);
- writeUInt32BE(packet, initWindow, p += 4);
- writeUInt32BE(packet, maxPacket, p += 4);
- writeUInt32BE(packet, addrLen, p += 4);
- packet.utf8Write(cfg.originAddr, p += 4, addrLen);
- writeUInt32BE(packet, cfg.originPort, p += addrLen);
- this._debug && this._debug(
- `Outbound: Sending CHANNEL_OPEN (r:${chan}, x11)`
- );
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- openssh_authAgent(chan, initWindow, maxPacket) {
- if (!this._server)
- throw new Error('Server-only method called in client mode');
- let p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(1 + 4 + 22 + 4 + 4 + 4);
- packet[p] = MESSAGE.CHANNEL_OPEN;
- writeUInt32BE(packet, 22, ++p);
- packet.utf8Write('auth-agent@openssh.com', p += 4, 22);
- writeUInt32BE(packet, chan, p += 22);
- writeUInt32BE(packet, initWindow, p += 4);
- writeUInt32BE(packet, maxPacket, p += 4);
- this._debug && this._debug(
- `Outbound: Sending CHANNEL_OPEN (r:${chan}, auth-agent@openssh.com)`
- );
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- openssh_forwardedStreamLocal(chan, initWindow, maxPacket, cfg) {
- if (!this._server)
- throw new Error('Server-only method called in client mode');
- const pathLen = Buffer.byteLength(cfg.socketPath);
- let p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(
- 1 + 4 + 33 + 4 + 4 + 4 + 4 + pathLen + 4
- );
- packet[p] = MESSAGE.CHANNEL_OPEN;
- writeUInt32BE(packet, 33, ++p);
- packet.utf8Write('forwarded-streamlocal@openssh.com', p += 4, 33);
- writeUInt32BE(packet, chan, p += 33);
- writeUInt32BE(packet, initWindow, p += 4);
- writeUInt32BE(packet, maxPacket, p += 4);
- writeUInt32BE(packet, pathLen, p += 4);
- packet.utf8Write(cfg.socketPath, p += 4, pathLen);
- writeUInt32BE(packet, 0, p += pathLen);
- if (this._debug) {
- this._debug(
- 'Outbound: Sending CHANNEL_OPEN '
- + `(r:${chan}, forwarded-streamlocal@openssh.com)`
- );
- }
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- exitStatus(chan, status) {
- if (!this._server)
- throw new Error('Server-only method called in client mode');
- // Does not consume window space
- let p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(1 + 4 + 4 + 11 + 1 + 4);
- packet[p] = MESSAGE.CHANNEL_REQUEST;
- writeUInt32BE(packet, chan, ++p);
- writeUInt32BE(packet, 11, p += 4);
- packet.utf8Write('exit-status', p += 4, 11);
- packet[p += 11] = 0;
- writeUInt32BE(packet, status, ++p);
- this._debug && this._debug(
- `Outbound: Sending CHANNEL_REQUEST (r:${chan}, exit-status: ${status})`
- );
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- exitSignal(chan, name, coreDumped, msg) {
- if (!this._server)
- throw new Error('Server-only method called in client mode');
- // Does not consume window space
- const origSignal = name;
- if (typeof origSignal !== 'string' || !origSignal)
- throw new Error(`Invalid signal: ${origSignal}`);
- let signal = name.toUpperCase();
- if (signal.slice(0, 3) === 'SIG')
- signal = signal.slice(3);
- if (SIGNALS[signal] !== 1)
- throw new Error(`Invalid signal: ${origSignal}`);
- const nameLen = Buffer.byteLength(signal);
- const msgLen = (msg ? Buffer.byteLength(msg) : 0);
- let p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(
- 1 + 4 + 4 + 11 + 1 + 4 + nameLen + 1 + 4 + msgLen + 4
- );
- packet[p] = MESSAGE.CHANNEL_REQUEST;
- writeUInt32BE(packet, chan, ++p);
- writeUInt32BE(packet, 11, p += 4);
- packet.utf8Write('exit-signal', p += 4, 11);
- packet[p += 11] = 0;
- writeUInt32BE(packet, nameLen, ++p);
- packet.utf8Write(signal, p += 4, nameLen);
- packet[p += nameLen] = (coreDumped ? 1 : 0);
- writeUInt32BE(packet, msgLen, ++p);
- p += 4;
- if (msgLen) {
- packet.utf8Write(msg, p, msgLen);
- p += msgLen;
- }
- writeUInt32BE(packet, 0, p);
- this._debug && this._debug(
- `Outbound: Sending CHANNEL_REQUEST (r:${chan}, exit-signal: ${name})`
- );
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- // 'ssh-userauth' service-specific
- authFailure(authMethods, isPartial) {
- if (!this._server)
- throw new Error('Server-only method called in client mode');
- if (this._authsQueue.length === 0)
- throw new Error('No auth in progress');
- let methods;
- if (typeof authMethods === 'boolean') {
- isPartial = authMethods;
- authMethods = undefined;
- }
- if (authMethods) {
- methods = [];
- for (let i = 0; i < authMethods.length; ++i) {
- if (authMethods[i].toLowerCase() === 'none')
- continue;
- methods.push(authMethods[i]);
- }
- methods = methods.join(',');
- } else {
- methods = '';
- }
- const methodsLen = methods.length;
- let p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(1 + 4 + methodsLen + 1);
- packet[p] = MESSAGE.USERAUTH_FAILURE;
- writeUInt32BE(packet, methodsLen, ++p);
- packet.utf8Write(methods, p += 4, methodsLen);
- packet[p += methodsLen] = (isPartial === true ? 1 : 0);
- this._authsQueue.shift();
- this._debug && this._debug('Outbound: Sending USERAUTH_FAILURE');
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- authSuccess() {
- if (!this._server)
- throw new Error('Server-only method called in client mode');
- if (this._authsQueue.length === 0)
- throw new Error('No auth in progress');
- const p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(1);
- packet[p] = MESSAGE.USERAUTH_SUCCESS;
- this._authsQueue.shift();
- this._authenticated = true;
- this._debug && this._debug('Outbound: Sending USERAUTH_SUCCESS');
- sendPacket(this, this._packetRW.write.finalize(packet));
- if (this._kex.negotiated.cs.compress === 'zlib@openssh.com')
- this._packetRW.read = new ZlibPacketReader();
- if (this._kex.negotiated.sc.compress === 'zlib@openssh.com')
- this._packetRW.write = new ZlibPacketWriter(this);
- }
- authPKOK(keyAlgo, key) {
- if (!this._server)
- throw new Error('Server-only method called in client mode');
- if (this._authsQueue.length === 0 || this._authsQueue[0] !== 'publickey')
- throw new Error('"publickey" auth not in progress');
- // TODO: support parsed key for `key`
- const keyAlgoLen = Buffer.byteLength(keyAlgo);
- const keyLen = key.length;
- let p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(1 + 4 + keyAlgoLen + 4 + keyLen);
- packet[p] = MESSAGE.USERAUTH_PK_OK;
- writeUInt32BE(packet, keyAlgoLen, ++p);
- packet.utf8Write(keyAlgo, p += 4, keyAlgoLen);
- writeUInt32BE(packet, keyLen, p += keyAlgoLen);
- packet.set(key, p += 4);
- this._authsQueue.shift();
- this._debug && this._debug('Outbound: Sending USERAUTH_PK_OK');
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- authPasswdChg(prompt) {
- if (!this._server)
- throw new Error('Server-only method called in client mode');
- const promptLen = Buffer.byteLength(prompt);
- let p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(1 + 4 + promptLen + 4);
- packet[p] = MESSAGE.USERAUTH_PASSWD_CHANGEREQ;
- writeUInt32BE(packet, promptLen, ++p);
- packet.utf8Write(prompt, p += 4, promptLen);
- writeUInt32BE(packet, 0, p += promptLen); // Empty language tag
- this._debug && this._debug('Outbound: Sending USERAUTH_PASSWD_CHANGEREQ');
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- authInfoReq(name, instructions, prompts) {
- if (!this._server)
- throw new Error('Server-only method called in client mode');
- let promptsLen = 0;
- const nameLen = name ? Buffer.byteLength(name) : 0;
- const instrLen = instructions ? Buffer.byteLength(instructions) : 0;
- for (let i = 0; i < prompts.length; ++i)
- promptsLen += 4 + Buffer.byteLength(prompts[i].prompt) + 1;
- let p = this._packetRW.write.allocStart;
- const packet = this._packetRW.write.alloc(
- 1 + 4 + nameLen + 4 + instrLen + 4 + 4 + promptsLen
- );
- packet[p] = MESSAGE.USERAUTH_INFO_REQUEST;
- writeUInt32BE(packet, nameLen, ++p);
- p += 4;
- if (name) {
- packet.utf8Write(name, p, nameLen);
- p += nameLen;
- }
- writeUInt32BE(packet, instrLen, p);
- p += 4;
- if (instructions) {
- packet.utf8Write(instructions, p, instrLen);
- p += instrLen;
- }
- writeUInt32BE(packet, 0, p);
- writeUInt32BE(packet, prompts.length, p += 4);
- p += 4;
- for (let i = 0; i < prompts.length; ++i) {
- const prompt = prompts[i];
- const promptLen = Buffer.byteLength(prompt.prompt);
- writeUInt32BE(packet, promptLen, p);
- p += 4;
- if (promptLen) {
- packet.utf8Write(prompt.prompt, p, promptLen);
- p += promptLen;
- }
- packet[p++] = (prompt.echo ? 1 : 0);
- }
- this._debug && this._debug('Outbound: Sending USERAUTH_INFO_REQUEST');
- sendPacket(this, this._packetRW.write.finalize(packet));
- }
- }
- // SSH-protoversion-softwareversion (SP comments) CR LF
- const RE_IDENT = /^SSH-(2\.0|1\.99)-([^ ]+)(?: (.*))?$/;
- // TODO: optimize this by starting n bytes from the end of this._buffer instead
- // of the beginning
- function parseHeader(chunk, p, len) {
- let data;
- let chunkOffset;
- if (this._buffer) {
- data = Buffer.allocUnsafe(this._buffer.length + (len - p));
- data.set(this._buffer, 0);
- if (p === 0) {
- data.set(chunk, this._buffer.length);
- } else {
- data.set(new Uint8Array(chunk.buffer,
- chunk.byteOffset + p,
- (len - p)),
- this._buffer.length);
- }
- chunkOffset = this._buffer.length;
- p = 0;
- } else {
- data = chunk;
- chunkOffset = 0;
- }
- const op = p;
- let start = p;
- let end = p;
- let needNL = false;
- let lineLen = 0;
- let lines = 0;
- for (; p < data.length; ++p) {
- const ch = data[p];
- if (ch === 13 /* '\r' */) {
- needNL = true;
- continue;
- }
- if (ch === 10 /* '\n' */) {
- if (end > start
- && end - start > 4
- && data[start] === 83 /* 'S' */
- && data[start + 1] === 83 /* 'S' */
- && data[start + 2] === 72 /* 'H' */
- && data[start + 3] === 45 /* '-' */) {
- const full = data.latin1Slice(op, end + 1);
- const identRaw = (start === op ? full : full.slice(start - op));
- const m = RE_IDENT.exec(identRaw);
- if (!m)
- throw new Error('Invalid identification string');
- const header = {
- greeting: (start === op ? '' : full.slice(0, start - op)),
- identRaw,
- versions: {
- protocol: m[1],
- software: m[2],
- },
- comments: m[3]
- };
- // Needed during handshake
- this._remoteIdentRaw = Buffer.from(identRaw);
- this._debug && this._debug(`Remote ident: ${inspect(identRaw)}`);
- this._compatFlags = getCompatFlags(header);
- this._buffer = undefined;
- this._decipher =
- new NullDecipher(0, onKEXPayload.bind(this, { firstPacket: true }));
- this._parse = parsePacket;
- this._onHeader(header);
- if (!this._destruct) {
- // We disconnected inside _onHeader
- return len;
- }
- kexinit(this);
- return p + 1 - chunkOffset;
- }
- // Only allow pre-ident greetings when we're a client
- if (this._server)
- throw new Error('Greetings from clients not permitted');
- if (++lines > MAX_LINES)
- throw new Error('Max greeting lines exceeded');
- needNL = false;
- start = p + 1;
- lineLen = 0;
- } else if (needNL) {
- throw new Error('Invalid header: expected newline');
- } else if (++lineLen >= MAX_LINE_LEN) {
- throw new Error('Header line too long');
- }
- end = p;
- }
- if (!this._buffer)
- this._buffer = bufferSlice(data, op);
- return p - chunkOffset;
- }
- function parsePacket(chunk, p, len) {
- return this._decipher.decrypt(chunk, p, len);
- }
- function onPayload(payload) {
- // XXX: move this to the Decipher implementations?
- this._onPacket();
- if (payload.length === 0) {
- this._debug && this._debug('Inbound: Skipping empty packet payload');
- return;
- }
- payload = this._packetRW.read.read(payload);
- const type = payload[0];
- if (type === MESSAGE.USERAUTH_SUCCESS
- && !this._server
- && !this._authenticated) {
- this._authenticated = true;
- if (this._kex.negotiated.cs.compress === 'zlib@openssh.com')
- this._packetRW.write = new ZlibPacketWriter(this);
- if (this._kex.negotiated.sc.compress === 'zlib@openssh.com')
- this._packetRW.read = new ZlibPacketReader();
- }
- const handler = MESSAGE_HANDLERS[type];
- if (handler === undefined) {
- this._debug && this._debug(`Inbound: Unsupported message type: ${type}`);
- return;
- }
- return handler(this, payload);
- }
- function getCompatFlags(header) {
- const software = header.versions.software;
- let flags = 0;
- for (const rule of COMPAT_CHECKS) {
- if (typeof rule[0] === 'string') {
- if (software === rule[0])
- flags |= rule[1];
- } else if (rule[0].test(software)) {
- flags |= rule[1];
- }
- }
- return flags;
- }
- function modesToBytes(modes) {
- const keys = Object.keys(modes);
- const bytes = Buffer.allocUnsafe((5 * keys.length) + 1);
- let b = 0;
- for (let i = 0; i < keys.length; ++i) {
- const key = keys[i];
- if (key === 'TTY_OP_END')
- continue;
- const opcode = TERMINAL_MODE[key];
- if (opcode === undefined)
- continue;
- const val = modes[key];
- if (typeof val === 'number' && isFinite(val)) {
- bytes[b++] = opcode;
- bytes[b++] = val >>> 24;
- bytes[b++] = val >>> 16;
- bytes[b++] = val >>> 8;
- bytes[b++] = val;
- }
- }
- bytes[b++] = TERMINAL_MODE.TTY_OP_END;
- if (b < bytes.length)
- return bufferSlice(bytes, 0, b);
- return bytes;
- }
- module.exports = Protocol;
|