12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571 |
- var crypto = require('crypto');
- var Socket = require('net').Socket;
- var dnsLookup = require('dns').lookup;
- var EventEmitter = require('events').EventEmitter;
- var inherits = require('util').inherits;
- var HASHES = crypto.getHashes();
-
- var ssh2_streams = require('ssh2-streams');
- var SSH2Stream = ssh2_streams.SSH2Stream;
- var SFTPStream = ssh2_streams.SFTPStream;
- var consts = ssh2_streams.constants;
- var BUGS = consts.BUGS;
- var ALGORITHMS = consts.ALGORITHMS;
- var EDDSA_SUPPORTED = consts.EDDSA_SUPPORTED;
- var parseKey = ssh2_streams.utils.parseKey;
-
- var HTTPAgents = require('./http-agents');
- var Channel = require('./Channel');
- var agentQuery = require('./agent');
- var SFTPWrapper = require('./SFTPWrapper');
- var readUInt32BE = require('./buffer-helpers').readUInt32BE;
-
- var MAX_CHANNEL = Math.pow(2, 32) - 1;
- var RE_OPENSSH = /^OpenSSH_(?:(?![0-4])\d)|(?:\d{2,})/;
- var DEBUG_NOOP = function(msg) {};
-
- function Client() {
- if (!(this instanceof Client))
- return new Client();
-
- EventEmitter.call(this);
-
- this.config = {
- host: undefined,
- port: undefined,
- localAddress: undefined,
- localPort: undefined,
- forceIPv4: undefined,
- forceIPv6: undefined,
- keepaliveCountMax: undefined,
- keepaliveInterval: undefined,
- readyTimeout: undefined,
-
- username: undefined,
- password: undefined,
- privateKey: undefined,
- tryKeyboard: undefined,
- agent: undefined,
- allowAgentFwd: undefined,
- authHandler: undefined,
-
- hostHashAlgo: undefined,
- hostHashCb: undefined,
- strictVendor: undefined,
- debug: undefined
- };
-
- this._readyTimeout = undefined;
- this._channels = undefined;
- this._callbacks = undefined;
- this._forwarding = undefined;
- this._forwardingUnix = undefined;
- this._acceptX11 = undefined;
- this._agentFwdEnabled = undefined;
- this._curChan = undefined;
- this._remoteVer = undefined;
-
- this._sshstream = undefined;
- this._sock = undefined;
- this._resetKA = undefined;
- }
- inherits(Client, EventEmitter);
-
- Client.prototype.connect = function(cfg) {
- var self = this;
-
- if (this._sock && this._sock.writable) {
- this.once('close', function() {
- self.connect(cfg);
- });
- this.end();
- return;
- }
-
- this.config.host = cfg.hostname || cfg.host || 'localhost';
- this.config.port = cfg.port || 22;
- this.config.localAddress = (typeof cfg.localAddress === 'string'
- ? cfg.localAddress
- : undefined);
- this.config.localPort = (typeof cfg.localPort === 'string'
- || typeof cfg.localPort === 'number'
- ? cfg.localPort
- : undefined);
- this.config.forceIPv4 = cfg.forceIPv4 || false;
- this.config.forceIPv6 = cfg.forceIPv6 || false;
- this.config.keepaliveCountMax = (typeof cfg.keepaliveCountMax === 'number'
- && cfg.keepaliveCountMax >= 0
- ? cfg.keepaliveCountMax
- : 3);
- this.config.keepaliveInterval = (typeof cfg.keepaliveInterval === 'number'
- && cfg.keepaliveInterval > 0
- ? cfg.keepaliveInterval
- : 0);
- this.config.readyTimeout = (typeof cfg.readyTimeout === 'number'
- && cfg.readyTimeout >= 0
- ? cfg.readyTimeout
- : 20000);
-
- var algorithms = {
- kex: undefined,
- kexBuf: undefined,
- cipher: undefined,
- cipherBuf: undefined,
- serverHostKey: undefined,
- serverHostKeyBuf: undefined,
- hmac: undefined,
- hmacBuf: undefined,
- compress: undefined,
- compressBuf: undefined
- };
- var i;
- if (typeof cfg.algorithms === 'object' && cfg.algorithms !== null) {
- var algosSupported;
- var algoList;
-
- algoList = cfg.algorithms.kex;
- if (Array.isArray(algoList) && algoList.length > 0) {
- algosSupported = ALGORITHMS.SUPPORTED_KEX;
- for (i = 0; i < algoList.length; ++i) {
- if (algosSupported.indexOf(algoList[i]) === -1)
- throw new Error('Unsupported key exchange algorithm: ' + algoList[i]);
- }
- algorithms.kex = algoList;
- }
-
- algoList = cfg.algorithms.cipher;
- if (Array.isArray(algoList) && algoList.length > 0) {
- algosSupported = ALGORITHMS.SUPPORTED_CIPHER;
- for (i = 0; i < algoList.length; ++i) {
- if (algosSupported.indexOf(algoList[i]) === -1)
- throw new Error('Unsupported cipher algorithm: ' + algoList[i]);
- }
- algorithms.cipher = algoList;
- }
-
- algoList = cfg.algorithms.serverHostKey;
- if (Array.isArray(algoList) && algoList.length > 0) {
- algosSupported = ALGORITHMS.SUPPORTED_SERVER_HOST_KEY;
- for (i = 0; i < algoList.length; ++i) {
- if (algosSupported.indexOf(algoList[i]) === -1) {
- throw new Error('Unsupported server host key algorithm: '
- + algoList[i]);
- }
- }
- algorithms.serverHostKey = algoList;
- }
-
- algoList = cfg.algorithms.hmac;
- if (Array.isArray(algoList) && algoList.length > 0) {
- algosSupported = ALGORITHMS.SUPPORTED_HMAC;
- for (i = 0; i < algoList.length; ++i) {
- if (algosSupported.indexOf(algoList[i]) === -1)
- throw new Error('Unsupported HMAC algorithm: ' + algoList[i]);
- }
- algorithms.hmac = algoList;
- }
-
- algoList = cfg.algorithms.compress;
- if (Array.isArray(algoList) && algoList.length > 0) {
- algosSupported = ALGORITHMS.SUPPORTED_COMPRESS;
- for (i = 0; i < algoList.length; ++i) {
- if (algosSupported.indexOf(algoList[i]) === -1)
- throw new Error('Unsupported compression algorithm: ' + algoList[i]);
- }
- algorithms.compress = algoList;
- }
- }
- if (algorithms.compress === undefined) {
- if (cfg.compress) {
- algorithms.compress = ['zlib@openssh.com', 'zlib'];
- if (cfg.compress !== 'force')
- algorithms.compress.push('none');
- } else if (cfg.compress === false)
- algorithms.compress = ['none'];
- }
-
- if (typeof cfg.username === 'string')
- this.config.username = cfg.username;
- else if (typeof cfg.user === 'string')
- this.config.username = cfg.user;
- else
- throw new Error('Invalid username');
-
- this.config.password = (typeof cfg.password === 'string'
- ? cfg.password
- : undefined);
- this.config.privateKey = (typeof cfg.privateKey === 'string'
- || Buffer.isBuffer(cfg.privateKey)
- ? cfg.privateKey
- : undefined);
- this.config.localHostname = (typeof cfg.localHostname === 'string'
- && cfg.localHostname.length
- ? cfg.localHostname
- : undefined);
- this.config.localUsername = (typeof cfg.localUsername === 'string'
- && cfg.localUsername.length
- ? cfg.localUsername
- : undefined);
- this.config.tryKeyboard = (cfg.tryKeyboard === true);
- this.config.agent = (typeof cfg.agent === 'string' && cfg.agent.length
- ? cfg.agent
- : undefined);
- this.config.allowAgentFwd = (cfg.agentForward === true
- && this.config.agent !== undefined);
- var authHandler = this.config.authHandler = (
- typeof cfg.authHandler === 'function' ? cfg.authHandler : undefined
- );
-
- this.config.strictVendor = (typeof cfg.strictVendor === 'boolean'
- ? cfg.strictVendor
- : true);
-
- var debug = this.config.debug = (typeof cfg.debug === 'function'
- ? cfg.debug
- : DEBUG_NOOP);
-
- if (cfg.agentForward === true && !this.config.allowAgentFwd)
- throw new Error('You must set a valid agent path to allow agent forwarding');
-
- var callbacks = this._callbacks = [];
- this._channels = {};
- this._forwarding = {};
- this._forwardingUnix = {};
- this._acceptX11 = 0;
- this._agentFwdEnabled = false;
- this._curChan = -1;
- this._remoteVer = undefined;
- var privateKey;
-
- if (this.config.privateKey) {
- privateKey = parseKey(this.config.privateKey, cfg.passphrase);
- if (privateKey instanceof Error)
- throw new Error('Cannot parse privateKey: ' + privateKey.message);
- if (Array.isArray(privateKey))
- privateKey = privateKey[0]; // OpenSSH's newer format only stores 1 key for now
- if (privateKey.getPrivatePEM() === null)
- throw new Error('privateKey value does not contain a (valid) private key');
- }
-
- var stream = this._sshstream = new SSH2Stream({
- algorithms: algorithms,
- debug: (debug === DEBUG_NOOP ? undefined : debug)
- });
- var sock = this._sock = (cfg.sock || new Socket());
-
- // drain stderr if we are connection hopping using an exec stream
- if (this._sock.stderr && typeof this._sock.stderr.resume === 'function')
- this._sock.stderr.resume();
-
- // keepalive-related
- var kainterval = this.config.keepaliveInterval;
- var kacountmax = this.config.keepaliveCountMax;
- var kacount = 0;
- var katimer;
- function sendKA() {
- if (++kacount > kacountmax) {
- clearInterval(katimer);
- if (sock.readable) {
- var err = new Error('Keepalive timeout');
- err.level = 'client-timeout';
- self.emit('error', err);
- sock.destroy();
- }
- return;
- }
- if (sock.writable) {
- // append dummy callback to keep correct callback order
- callbacks.push(resetKA);
- stream.ping();
- } else
- clearInterval(katimer);
- }
- function resetKA() {
- if (kainterval > 0) {
- kacount = 0;
- clearInterval(katimer);
- if (sock.writable)
- katimer = setInterval(sendKA, kainterval);
- }
- }
- this._resetKA = resetKA;
-
- stream.on('USERAUTH_BANNER', function(msg) {
- self.emit('banner', msg);
- });
-
- sock.on('connect', function() {
- debug('DEBUG: Client: Connected');
- self.emit('connect');
- if (!cfg.sock)
- stream.pipe(sock).pipe(stream);
- }).on('timeout', function() {
- self.emit('timeout');
- }).on('error', function(err) {
- clearTimeout(self._readyTimeout);
- err.level = 'client-socket';
- self.emit('error', err);
- }).on('end', function() {
- stream.unpipe(sock);
- clearTimeout(self._readyTimeout);
- clearInterval(katimer);
- self.emit('end');
- }).on('close', function() {
- stream.unpipe(sock);
- clearTimeout(self._readyTimeout);
- clearInterval(katimer);
- self.emit('close');
-
- // notify outstanding channel requests of disconnection ...
- var callbacks_ = callbacks;
- var err = new Error('No response from server');
- callbacks = self._callbacks = [];
- for (i = 0; i < callbacks_.length; ++i)
- callbacks_[i](err);
-
- // simulate error for any channels waiting to be opened. this is safe
- // against successfully opened channels because the success and failure
- // event handlers are automatically removed when a success/failure response
- // is received
- var channels = self._channels;
- var chanNos = Object.keys(channels);
- self._channels = {};
- for (i = 0; i < chanNos.length; ++i) {
- var ev1 = stream.emit('CHANNEL_OPEN_FAILURE:' + chanNos[i], err);
- // emitting CHANNEL_CLOSE should be safe too and should help for any
- // special channels which might otherwise keep the process alive, such
- // as agent forwarding channels which have open unix sockets ...
- var ev2 = stream.emit('CHANNEL_CLOSE:' + chanNos[i]);
- var earlyCb;
- if (!ev1 && !ev2 && (earlyCb = channels[chanNos[i]])
- && typeof earlyCb === 'function') {
- earlyCb(err);
- }
- }
- });
- stream.on('drain', function() {
- self.emit('drain');
- }).once('header', function(header) {
- self._remoteVer = header.versions.software;
- if (header.greeting)
- self.emit('greeting', header.greeting);
- }).on('continue', function() {
- self.emit('continue');
- }).on('error', function(err) {
- if (err.level === undefined)
- err.level = 'protocol';
- else if (err.level === 'handshake')
- clearTimeout(self._readyTimeout);
- self.emit('error', err);
- }).on('end', function() {
- sock.resume();
- });
-
- if (typeof cfg.hostVerifier === 'function') {
- if (HASHES.indexOf(cfg.hostHash) === -1)
- throw new Error('Invalid host hash algorithm: ' + cfg.hostHash);
- var hashCb = cfg.hostVerifier;
- var hasher = crypto.createHash(cfg.hostHash);
- stream.once('fingerprint', function(key, verify) {
- hasher.update(key);
- var ret = hashCb(hasher.digest('hex'), verify);
- if (ret !== undefined)
- verify(ret);
- });
- }
-
- // begin authentication handling =============================================
- var curAuth;
- var curPartial = null;
- var curAuthsLeft = null;
- var agentKeys;
- var agentKeyPos = 0;
- var authsAllowed = ['none'];
- if (this.config.password !== undefined)
- authsAllowed.push('password');
- if (privateKey !== undefined)
- authsAllowed.push('publickey');
- if (this.config.agent !== undefined)
- authsAllowed.push('agent');
- if (this.config.tryKeyboard)
- authsAllowed.push('keyboard-interactive');
- if (privateKey !== undefined
- && this.config.localHostname !== undefined
- && this.config.localUsername !== undefined) {
- authsAllowed.push('hostbased');
- }
-
- if (authHandler === undefined) {
- var authPos = 0;
- authHandler = function authHandler(authsLeft, partial, cb) {
- if (authPos === authsAllowed.length)
- return false;
- return authsAllowed[authPos++];
- };
- }
-
- var hasSentAuth = false;
- function doNextAuth(authName) {
- hasSentAuth = true;
- if (authName === false) {
- stream.removeListener('USERAUTH_FAILURE', onUSERAUTH_FAILURE);
- stream.removeListener('USERAUTH_PK_OK', onUSERAUTH_PK_OK);
- var err = new Error('All configured authentication methods failed');
- err.level = 'client-authentication';
- self.emit('error', err);
- if (stream.writable)
- self.end();
- return;
- }
- if (authsAllowed.indexOf(authName) === -1)
- throw new Error('Authentication method not allowed: ' + authName);
- curAuth = authName;
- switch (curAuth) {
- case 'password':
- stream.authPassword(self.config.username, self.config.password);
- break;
- case 'publickey':
- stream.authPK(self.config.username, privateKey);
- stream.once('USERAUTH_PK_OK', onUSERAUTH_PK_OK);
- break;
- case 'hostbased':
- function hostbasedCb(buf, cb) {
- var signature = privateKey.sign(buf);
- if (signature instanceof Error) {
- signature.message = 'Error while signing data with privateKey: '
- + signature.message;
- signature.level = 'client-authentication';
- self.emit('error', signature);
- return tryNextAuth();
- }
-
- cb(signature);
- }
- stream.authHostbased(self.config.username,
- privateKey,
- self.config.localHostname,
- self.config.localUsername,
- hostbasedCb);
- break;
- case 'agent':
- agentQuery(self.config.agent, function(err, keys) {
- if (err) {
- err.level = 'agent';
- self.emit('error', err);
- agentKeys = undefined;
- return tryNextAuth();
- } else if (keys.length === 0) {
- debug('DEBUG: Agent: No keys stored in agent');
- agentKeys = undefined;
- return tryNextAuth();
- }
-
- agentKeys = keys;
- agentKeyPos = 0;
-
- stream.authPK(self.config.username, keys[0]);
- stream.once('USERAUTH_PK_OK', onUSERAUTH_PK_OK);
- });
- break;
- case 'keyboard-interactive':
- stream.authKeyboard(self.config.username);
- stream.on('USERAUTH_INFO_REQUEST', onUSERAUTH_INFO_REQUEST);
- break;
- case 'none':
- stream.authNone(self.config.username);
- break;
- }
- }
- function tryNextAuth() {
- hasSentAuth = false;
- var auth = authHandler(curAuthsLeft, curPartial, doNextAuth);
- if (hasSentAuth || auth === undefined)
- return;
- doNextAuth(auth);
- }
- function tryNextAgentKey() {
- if (curAuth === 'agent') {
- if (agentKeyPos >= agentKeys.length)
- return;
- if (++agentKeyPos >= agentKeys.length) {
- debug('DEBUG: Agent: No more keys left to try');
- debug('DEBUG: Client: agent auth failed');
- agentKeys = undefined;
- tryNextAuth();
- } else {
- debug('DEBUG: Agent: Trying key #' + (agentKeyPos + 1));
- stream.authPK(self.config.username, agentKeys[agentKeyPos]);
- stream.once('USERAUTH_PK_OK', onUSERAUTH_PK_OK);
- }
- }
- }
- function onUSERAUTH_INFO_REQUEST(name, instructions, lang, prompts) {
- var nprompts = (Array.isArray(prompts) ? prompts.length : 0);
- if (nprompts === 0) {
- debug('DEBUG: Client: Sending automatic USERAUTH_INFO_RESPONSE');
- return stream.authInfoRes();
- }
- // we sent a keyboard-interactive user authentication request and now the
- // server is sending us the prompts we need to present to the user
- self.emit('keyboard-interactive',
- name,
- instructions,
- lang,
- prompts,
- function(answers) {
- stream.authInfoRes(answers);
- }
- );
- }
- function onUSERAUTH_PK_OK() {
- if (curAuth === 'agent') {
- var agentKey = agentKeys[agentKeyPos];
- var keyLen = readUInt32BE(agentKey, 0);
- var pubKeyFullType = agentKey.toString('ascii', 4, 4 + keyLen);
- var pubKeyType = pubKeyFullType.slice(4);
- // Check that we support the key type first
- // TODO: move key type checking logic to ssh2-streams
- switch (pubKeyFullType) {
- case 'ssh-rsa':
- case 'ssh-dss':
- case 'ecdsa-sha2-nistp256':
- case 'ecdsa-sha2-nistp384':
- case 'ecdsa-sha2-nistp521':
- break;
- default:
- if (EDDSA_SUPPORTED && pubKeyFullType === 'ssh-ed25519')
- break;
- debug('DEBUG: Agent: Skipping unsupported key type: '
- + pubKeyFullType);
- return tryNextAgentKey();
- }
- stream.authPK(self.config.username,
- agentKey,
- function(buf, cb) {
- agentQuery(self.config.agent,
- agentKey,
- pubKeyType,
- buf,
- function(err, signed) {
- if (err) {
- err.level = 'agent';
- self.emit('error', err);
- } else {
- var sigFullTypeLen = readUInt32BE(signed, 0);
- if (4 + sigFullTypeLen + 4 < signed.length) {
- var sigFullType = signed.toString('ascii', 4, 4 + sigFullTypeLen);
- if (sigFullType !== pubKeyFullType) {
- err = new Error('Agent key/signature type mismatch');
- err.level = 'agent';
- self.emit('error', err);
- } else {
- // skip algoLen + algo + sigLen
- return cb(signed.slice(4 + sigFullTypeLen + 4));
- }
- }
- }
-
- tryNextAgentKey();
- });
- });
- } else if (curAuth === 'publickey') {
- stream.authPK(self.config.username, privateKey, function(buf, cb) {
- var signature = privateKey.sign(buf);
- if (signature instanceof Error) {
- signature.message = 'Error while signing data with privateKey: '
- + signature.message;
- signature.level = 'client-authentication';
- self.emit('error', signature);
- return tryNextAuth();
- }
- cb(signature);
- });
- }
- }
- function onUSERAUTH_FAILURE(authsLeft, partial) {
- stream.removeListener('USERAUTH_PK_OK', onUSERAUTH_PK_OK);
- stream.removeListener('USERAUTH_INFO_REQUEST', onUSERAUTH_INFO_REQUEST);
- if (curAuth === 'agent') {
- debug('DEBUG: Client: Agent key #' + (agentKeyPos + 1) + ' failed');
- return tryNextAgentKey();
- } else {
- debug('DEBUG: Client: ' + curAuth + ' auth failed');
- }
-
- curPartial = partial;
- curAuthsLeft = authsLeft;
- tryNextAuth();
- }
- stream.once('USERAUTH_SUCCESS', function() {
- stream.removeListener('USERAUTH_FAILURE', onUSERAUTH_FAILURE);
- stream.removeListener('USERAUTH_INFO_REQUEST', onUSERAUTH_INFO_REQUEST);
-
- // start keepalive mechanism
- resetKA();
-
- clearTimeout(self._readyTimeout);
-
- self.emit('ready');
- }).on('USERAUTH_FAILURE', onUSERAUTH_FAILURE);
- // end authentication handling ===============================================
-
- // handle initial handshake completion
- stream.once('ready', function() {
- stream.service('ssh-userauth');
- stream.once('SERVICE_ACCEPT', function(svcName) {
- if (svcName === 'ssh-userauth')
- tryNextAuth();
- });
- });
-
- // handle incoming requests from server, typically a forwarded TCP or X11
- // connection
- stream.on('CHANNEL_OPEN', function(info) {
- onCHANNEL_OPEN(self, info);
- });
-
- // handle responses for tcpip-forward and other global requests
- stream.on('REQUEST_SUCCESS', function(data) {
- if (callbacks.length)
- callbacks.shift()(false, data);
- }).on('REQUEST_FAILURE', function() {
- if (callbacks.length)
- callbacks.shift()(true);
- });
-
- stream.on('GLOBAL_REQUEST', function(name, wantReply, data) {
- // auto-reject all global requests, this can be especially useful if the
- // server is sending us dummy keepalive global requests
- if (wantReply)
- stream.requestFailure();
- });
-
- if (!cfg.sock) {
- var host = this.config.host;
- var forceIPv4 = this.config.forceIPv4;
- var forceIPv6 = this.config.forceIPv6;
-
- debug('DEBUG: Client: Trying '
- + host
- + ' on port '
- + this.config.port
- + ' ...');
-
- function doConnect() {
- startTimeout();
- self._sock.connect({
- host: host,
- port: self.config.port,
- localAddress: self.config.localAddress,
- localPort: self.config.localPort
- });
- self._sock.setNoDelay(true);
- self._sock.setMaxListeners(0);
- self._sock.setTimeout(typeof cfg.timeout === 'number' ? cfg.timeout : 0);
- }
-
- if ((!forceIPv4 && !forceIPv6) || (forceIPv4 && forceIPv6))
- doConnect();
- else {
- dnsLookup(host, (forceIPv4 ? 4 : 6), function(err, address, family) {
- if (err) {
- var error = new Error('Error while looking up '
- + (forceIPv4 ? 'IPv4' : 'IPv6')
- + ' address for host '
- + host
- + ': ' + err);
- clearTimeout(self._readyTimeout);
- error.level = 'client-dns';
- self.emit('error', error);
- self.emit('close');
- return;
- }
- host = address;
- doConnect();
- });
- }
- } else {
- startTimeout();
- stream.pipe(sock).pipe(stream);
- }
-
- function startTimeout() {
- if (self.config.readyTimeout > 0) {
- self._readyTimeout = setTimeout(function() {
- var err = new Error('Timed out while waiting for handshake');
- err.level = 'client-timeout';
- self.emit('error', err);
- sock.destroy();
- }, self.config.readyTimeout);
- }
- }
- };
-
- Client.prototype.end = function() {
- if (this._sock
- && this._sock.writable
- && this._sshstream
- && this._sshstream.writable)
- return this._sshstream.disconnect();
- return false;
- };
-
- Client.prototype.destroy = function() {
- this._sock && this._sock.destroy();
- };
-
- Client.prototype.exec = function(cmd, opts, cb) {
- if (!this._sock
- || !this._sock.writable
- || !this._sshstream
- || !this._sshstream.writable)
- throw new Error('Not connected');
-
- if (typeof opts === 'function') {
- cb = opts;
- opts = {};
- }
-
- var self = this;
- var extraOpts = { allowHalfOpen: (opts.allowHalfOpen !== false) };
-
- return openChannel(this, 'session', extraOpts, function(err, chan) {
- if (err)
- return cb(err);
-
- var todo = [];
-
- function reqCb(err) {
- if (err) {
- chan.close();
- return cb(err);
- }
- if (todo.length)
- todo.shift()();
- }
-
- if (self.config.allowAgentFwd === true
- || (opts
- && opts.agentForward === true
- && self.config.agent !== undefined)) {
- todo.push(function() {
- reqAgentFwd(chan, reqCb);
- });
- }
-
- if (typeof opts === 'object' && opts !== null) {
- if (typeof opts.env === 'object' && opts.env !== null)
- reqEnv(chan, opts.env);
- if ((typeof opts.pty === 'object' && opts.pty !== null)
- || opts.pty === true) {
- todo.push(function() { reqPty(chan, opts.pty, reqCb); });
- }
- if ((typeof opts.x11 === 'object' && opts.x11 !== null)
- || opts.x11 === 'number'
- || opts.x11 === true) {
- todo.push(function() { reqX11(chan, opts.x11, reqCb); });
- }
- }
-
- todo.push(function() { reqExec(chan, cmd, opts, cb); });
- todo.shift()();
- });
- };
-
- Client.prototype.shell = function(wndopts, opts, cb) {
- if (!this._sock
- || !this._sock.writable
- || !this._sshstream
- || !this._sshstream.writable)
- throw new Error('Not connected');
-
- // start an interactive terminal/shell session
- var self = this;
-
- if (typeof wndopts === 'function') {
- cb = wndopts;
- wndopts = opts = undefined;
- } else if (typeof opts === 'function') {
- cb = opts;
- opts = undefined;
- }
- if (wndopts && (wndopts.x11 !== undefined || wndopts.env !== undefined)) {
- opts = wndopts;
- wndopts = undefined;
- }
-
- return openChannel(this, 'session', function(err, chan) {
- if (err)
- return cb(err);
-
- var todo = [];
-
- function reqCb(err) {
- if (err) {
- chan.close();
- return cb(err);
- }
- if (todo.length)
- todo.shift()();
- }
-
- if (self.config.allowAgentFwd === true
- || (opts
- && opts.agentForward === true
- && self.config.agent !== undefined)) {
- todo.push(function() { reqAgentFwd(chan, reqCb); });
- }
-
- if (wndopts !== false)
- todo.push(function() { reqPty(chan, wndopts, reqCb); });
-
- if (typeof opts === 'object' && opts !== null) {
- if (typeof opts.env === 'object' && opts.env !== null)
- reqEnv(chan, opts.env);
- if ((typeof opts.x11 === 'object' && opts.x11 !== null)
- || opts.x11 === 'number'
- || opts.x11 === true) {
- todo.push(function() { reqX11(chan, opts.x11, reqCb); });
- }
- }
-
- todo.push(function() { reqShell(chan, cb); });
- todo.shift()();
- });
- };
-
- Client.prototype.subsys = function(name, cb) {
- if (!this._sock
- || !this._sock.writable
- || !this._sshstream
- || !this._sshstream.writable)
- throw new Error('Not connected');
-
- return openChannel(this, 'session', function(err, chan) {
- if (err)
- return cb(err);
-
- reqSubsystem(chan, name, function(err, stream) {
- if (err)
- return cb(err);
-
- cb(undefined, stream);
- });
- });
- };
-
- Client.prototype.sftp = function(cb) {
- if (!this._sock
- || !this._sock.writable
- || !this._sshstream
- || !this._sshstream.writable)
- throw new Error('Not connected');
-
- var self = this;
-
- // start an SFTP session
- return openChannel(this, 'session', function(err, chan) {
- if (err)
- return cb(err);
-
- reqSubsystem(chan, 'sftp', function(err, stream) {
- if (err)
- return cb(err);
-
- var serverIdentRaw = self._sshstream._state.incoming.identRaw;
- var cfg = { debug: self.config.debug };
- var sftp = new SFTPStream(cfg, serverIdentRaw);
-
- function onError(err) {
- sftp.removeListener('ready', onReady);
- stream.removeListener('exit', onExit);
- cb(err);
- }
-
- function onReady() {
- sftp.removeListener('error', onError);
- stream.removeListener('exit', onExit);
- cb(undefined, new SFTPWrapper(sftp));
- }
-
- function onExit(code, signal) {
- sftp.removeListener('ready', onReady);
- sftp.removeListener('error', onError);
- var msg;
- if (typeof code === 'number') {
- msg = 'Received exit code '
- + code
- + ' while establishing SFTP session';
- } else {
- msg = 'Received signal '
- + signal
- + ' while establishing SFTP session';
- }
- var err = new Error(msg);
- err.code = code;
- err.signal = signal;
- cb(err);
- }
-
- sftp.once('error', onError)
- .once('ready', onReady)
- .once('close', function() {
- stream.end();
- });
-
- // OpenSSH server sends an exit-status if there was a problem spinning up
- // an sftp server child process, so we listen for that here in order to
- // properly raise an error.
- stream.once('exit', onExit);
-
- sftp.pipe(stream).pipe(sftp);
- });
- });
- };
-
- Client.prototype.forwardIn = function(bindAddr, bindPort, cb) {
- if (!this._sock
- || !this._sock.writable
- || !this._sshstream
- || !this._sshstream.writable)
- throw new Error('Not connected');
-
- // send a request for the server to start forwarding TCP connections to us
- // on a particular address and port
-
- var self = this;
- var wantReply = (typeof cb === 'function');
-
- if (wantReply) {
- this._callbacks.push(function(had_err, data) {
- if (had_err) {
- return cb(had_err !== true
- ? had_err
- : new Error('Unable to bind to ' + bindAddr + ':' + bindPort));
- }
-
- var realPort = bindPort;
- if (bindPort === 0 && data && data.length >= 4) {
- realPort = readUInt32BE(data, 0);
- if (!(self._sshstream.remoteBugs & BUGS.DYN_RPORT_BUG))
- bindPort = realPort;
- }
-
- self._forwarding[bindAddr + ':' + bindPort] = realPort;
-
- cb(undefined, realPort);
- });
- }
-
- return this._sshstream.tcpipForward(bindAddr, bindPort, wantReply);
- };
-
- Client.prototype.unforwardIn = function(bindAddr, bindPort, cb) {
- if (!this._sock
- || !this._sock.writable
- || !this._sshstream
- || !this._sshstream.writable)
- throw new Error('Not connected');
-
- // send a request to stop forwarding us new connections for a particular
- // address and port
-
- var self = this;
- var wantReply = (typeof cb === 'function');
-
- if (wantReply) {
- this._callbacks.push(function(had_err) {
- if (had_err) {
- return cb(had_err !== true
- ? had_err
- : new Error('Unable to unbind from '
- + bindAddr + ':' + bindPort));
- }
-
- delete self._forwarding[bindAddr + ':' + bindPort];
-
- cb();
- });
- }
-
- return this._sshstream.cancelTcpipForward(bindAddr, bindPort, wantReply);
- };
-
- Client.prototype.forwardOut = function(srcIP, srcPort, dstIP, dstPort, cb) {
- if (!this._sock
- || !this._sock.writable
- || !this._sshstream
- || !this._sshstream.writable)
- throw new Error('Not connected');
-
- // send a request to forward a TCP connection to the server
-
- var cfg = {
- srcIP: srcIP,
- srcPort: srcPort,
- dstIP: dstIP,
- dstPort: dstPort
- };
-
- return openChannel(this, 'direct-tcpip', cfg, cb);
- };
-
- Client.prototype.openssh_noMoreSessions = function(cb) {
- if (!this._sock
- || !this._sock.writable
- || !this._sshstream
- || !this._sshstream.writable)
- throw new Error('Not connected');
-
- var wantReply = (typeof cb === 'function');
-
- if (!this.config.strictVendor
- || (this.config.strictVendor && RE_OPENSSH.test(this._remoteVer))) {
- if (wantReply) {
- this._callbacks.push(function(had_err) {
- if (had_err) {
- return cb(had_err !== true
- ? had_err
- : new Error('Unable to disable future sessions'));
- }
-
- cb();
- });
- }
-
- return this._sshstream.openssh_noMoreSessions(wantReply);
- } else if (wantReply) {
- process.nextTick(function() {
- cb(new Error('strictVendor enabled and server is not OpenSSH or compatible version'));
- });
- }
-
- return true;
- };
-
- Client.prototype.openssh_forwardInStreamLocal = function(socketPath, cb) {
- if (!this._sock
- || !this._sock.writable
- || !this._sshstream
- || !this._sshstream.writable)
- throw new Error('Not connected');
-
- var wantReply = (typeof cb === 'function');
- var self = this;
-
- if (!this.config.strictVendor
- || (this.config.strictVendor && RE_OPENSSH.test(this._remoteVer))) {
- if (wantReply) {
- this._callbacks.push(function(had_err) {
- if (had_err) {
- return cb(had_err !== true
- ? had_err
- : new Error('Unable to bind to ' + socketPath));
- }
- self._forwardingUnix[socketPath] = true;
- cb();
- });
- }
-
- return this._sshstream.openssh_streamLocalForward(socketPath, wantReply);
- } else if (wantReply) {
- process.nextTick(function() {
- cb(new Error('strictVendor enabled and server is not OpenSSH or compatible version'));
- });
- }
-
- return true;
- };
-
- Client.prototype.openssh_unforwardInStreamLocal = function(socketPath, cb) {
- if (!this._sock
- || !this._sock.writable
- || !this._sshstream
- || !this._sshstream.writable)
- throw new Error('Not connected');
-
- var wantReply = (typeof cb === 'function');
- var self = this;
-
- if (!this.config.strictVendor
- || (this.config.strictVendor && RE_OPENSSH.test(this._remoteVer))) {
- if (wantReply) {
- this._callbacks.push(function(had_err) {
- if (had_err) {
- return cb(had_err !== true
- ? had_err
- : new Error('Unable to unbind on ' + socketPath));
- }
- delete self._forwardingUnix[socketPath];
- cb();
- });
- }
-
- return this._sshstream.openssh_cancelStreamLocalForward(socketPath,
- wantReply);
- } else if (wantReply) {
- process.nextTick(function() {
- cb(new Error('strictVendor enabled and server is not OpenSSH or compatible version'));
- });
- }
-
- return true;
- };
-
- Client.prototype.openssh_forwardOutStreamLocal = function(socketPath, cb) {
- if (!this._sock
- || !this._sock.writable
- || !this._sshstream
- || !this._sshstream.writable)
- throw new Error('Not connected');
-
- if (!this.config.strictVendor
- || (this.config.strictVendor && RE_OPENSSH.test(this._remoteVer))) {
- var cfg = { socketPath: socketPath };
- return openChannel(this, 'direct-streamlocal@openssh.com', cfg, cb);
- } else {
- process.nextTick(function() {
- cb(new Error('strictVendor enabled and server is not OpenSSH or compatible version'));
- });
- }
-
- return true;
- };
-
- function openChannel(self, type, opts, cb) {
- // ask the server to open a channel for some purpose
- // (e.g. session (sftp, exec, shell), or forwarding a TCP connection
- var localChan = nextChannel(self);
- var initWindow = Channel.MAX_WINDOW;
- var maxPacket = Channel.PACKET_SIZE;
- var ret = true;
-
- if (localChan === false)
- return cb(new Error('No free channels available'));
-
- if (typeof opts === 'function') {
- cb = opts;
- opts = {};
- }
-
- self._channels[localChan] = cb;
-
- var sshstream = self._sshstream;
- sshstream.once('CHANNEL_OPEN_CONFIRMATION:' + localChan, onSuccess)
- .once('CHANNEL_OPEN_FAILURE:' + localChan, onFailure)
- .once('CHANNEL_CLOSE:' + localChan, onFailure);
-
- if (type === 'session')
- ret = sshstream.session(localChan, initWindow, maxPacket);
- else if (type === 'direct-tcpip')
- ret = sshstream.directTcpip(localChan, initWindow, maxPacket, opts);
- else if (type === 'direct-streamlocal@openssh.com') {
- ret = sshstream.openssh_directStreamLocal(localChan,
- initWindow,
- maxPacket,
- opts);
- }
-
- return ret;
-
- function onSuccess(info) {
- sshstream.removeListener('CHANNEL_OPEN_FAILURE:' + localChan, onFailure);
- sshstream.removeListener('CHANNEL_CLOSE:' + localChan, onFailure);
-
- var chaninfo = {
- type: type,
- incoming: {
- id: localChan,
- window: initWindow,
- packetSize: maxPacket,
- state: 'open'
- },
- outgoing: {
- id: info.sender,
- window: info.window,
- packetSize: info.packetSize,
- state: 'open'
- }
- };
- cb(undefined, new Channel(chaninfo, self));
- }
-
- function onFailure(info) {
- sshstream.removeListener('CHANNEL_OPEN_CONFIRMATION:' + localChan,
- onSuccess);
- sshstream.removeListener('CHANNEL_OPEN_FAILURE:' + localChan, onFailure);
- sshstream.removeListener('CHANNEL_CLOSE:' + localChan, onFailure);
-
- delete self._channels[localChan];
-
- var err;
- if (info instanceof Error)
- err = info;
- else if (typeof info === 'object' && info !== null) {
- err = new Error('(SSH) Channel open failure: ' + info.description);
- err.reason = info.reason;
- err.lang = info.lang;
- } else {
- err = new Error('(SSH) Channel open failure: '
- + 'server closed channel unexpectedly');
- err.reason = err.lang = '';
- }
- cb(err);
- }
- }
-
- function nextChannel(self) {
- // get the next available channel number
-
- // optimized path
- if (self._curChan < MAX_CHANNEL)
- return ++self._curChan;
-
- // slower lookup path
- for (var i = 0, channels = self._channels; i < MAX_CHANNEL; ++i)
- if (!channels[i])
- return i;
-
- return false;
- }
-
- function reqX11(chan, screen, cb) {
- // asks server to start sending us X11 connections
- var cfg = {
- single: false,
- protocol: 'MIT-MAGIC-COOKIE-1',
- cookie: undefined,
- screen: 0
- };
-
- if (typeof screen === 'function') {
- cb = screen;
- } else if (typeof screen === 'object' && screen !== null) {
- if (typeof screen.single === 'boolean')
- cfg.single = screen.single;
- if (typeof screen.screen === 'number')
- cfg.screen = screen.screen;
- if (typeof screen.protocol === 'string')
- cfg.protocol = screen.protocol;
- if (typeof screen.cookie === 'string')
- cfg.cookie = screen.cookie;
- else if (Buffer.isBuffer(screen.cookie))
- cfg.cookie = screen.cookie.toString('hex');
- }
- if (cfg.cookie === undefined)
- cfg.cookie = randomCookie();
-
- var wantReply = (typeof cb === 'function');
-
- if (chan.outgoing.state !== 'open') {
- wantReply && cb(new Error('Channel is not open'));
- return true;
- }
-
- if (wantReply) {
- chan._callbacks.push(function(had_err) {
- if (had_err) {
- return cb(had_err !== true
- ? had_err
- : new Error('Unable to request X11'));
- }
-
- chan._hasX11 = true;
- ++chan._client._acceptX11;
- chan.once('close', function() {
- if (chan._client._acceptX11)
- --chan._client._acceptX11;
- });
-
- cb();
- });
- }
-
- return chan._client._sshstream.x11Forward(chan.outgoing.id, cfg, wantReply);
- }
-
- function reqPty(chan, opts, cb) {
- var rows = 24;
- var cols = 80;
- var width = 640;
- var height = 480;
- var term = 'vt100';
- var modes = null;
-
- if (typeof opts === 'function')
- cb = opts;
- else if (typeof opts === 'object' && opts !== null) {
- if (typeof opts.rows === 'number')
- rows = opts.rows;
- if (typeof opts.cols === 'number')
- cols = opts.cols;
- if (typeof opts.width === 'number')
- width = opts.width;
- if (typeof opts.height === 'number')
- height = opts.height;
- if (typeof opts.term === 'string')
- term = opts.term;
- if (typeof opts.modes === 'object')
- modes = opts.modes;
- }
-
- var wantReply = (typeof cb === 'function');
-
- if (chan.outgoing.state !== 'open') {
- wantReply && cb(new Error('Channel is not open'));
- return true;
- }
-
- if (wantReply) {
- chan._callbacks.push(function(had_err) {
- if (had_err) {
- return cb(had_err !== true
- ? had_err
- : new Error('Unable to request a pseudo-terminal'));
- }
- cb();
- });
- }
-
- return chan._client._sshstream.pty(chan.outgoing.id,
- rows,
- cols,
- height,
- width,
- term,
- modes,
- wantReply);
- }
-
- function reqAgentFwd(chan, cb) {
- var wantReply = (typeof cb === 'function');
-
- if (chan.outgoing.state !== 'open') {
- wantReply && cb(new Error('Channel is not open'));
- return true;
- } else if (chan._client._agentFwdEnabled) {
- wantReply && cb(false);
- return true;
- }
-
- chan._client._agentFwdEnabled = true;
-
- chan._callbacks.push(function(had_err) {
- if (had_err) {
- chan._client._agentFwdEnabled = false;
- wantReply && cb(had_err !== true
- ? had_err
- : new Error('Unable to request agent forwarding'));
- return;
- }
-
- wantReply && cb();
- });
-
- return chan._client._sshstream.openssh_agentForward(chan.outgoing.id, true);
- }
-
- function reqShell(chan, cb) {
- if (chan.outgoing.state !== 'open') {
- cb(new Error('Channel is not open'));
- return true;
- }
- chan._callbacks.push(function(had_err) {
- if (had_err) {
- return cb(had_err !== true
- ? had_err
- : new Error('Unable to open shell'));
- }
- chan.subtype = 'shell';
- cb(undefined, chan);
- });
-
- return chan._client._sshstream.shell(chan.outgoing.id, true);
- }
-
- function reqExec(chan, cmd, opts, cb) {
- if (chan.outgoing.state !== 'open') {
- cb(new Error('Channel is not open'));
- return true;
- }
- chan._callbacks.push(function(had_err) {
- if (had_err) {
- return cb(had_err !== true
- ? had_err
- : new Error('Unable to exec'));
- }
- chan.subtype = 'exec';
- chan.allowHalfOpen = (opts.allowHalfOpen !== false);
- cb(undefined, chan);
- });
-
- return chan._client._sshstream.exec(chan.outgoing.id, cmd, true);
- }
-
- function reqEnv(chan, env) {
- if (chan.outgoing.state !== 'open')
- return true;
- var ret = true;
- var keys = Object.keys(env || {});
- var key;
- var val;
-
- for (var i = 0, len = keys.length; i < len; ++i) {
- key = keys[i];
- val = env[key];
- ret = chan._client._sshstream.env(chan.outgoing.id, key, val, false);
- }
-
- return ret;
- }
-
- function reqSubsystem(chan, name, cb) {
- if (chan.outgoing.state !== 'open') {
- cb(new Error('Channel is not open'));
- return true;
- }
- chan._callbacks.push(function(had_err) {
- if (had_err) {
- return cb(had_err !== true
- ? had_err
- : new Error('Unable to start subsystem: ' + name));
- }
- chan.subtype = 'subsystem';
- cb(undefined, chan);
- });
-
- return chan._client._sshstream.subsystem(chan.outgoing.id, name, true);
- }
-
- function onCHANNEL_OPEN(self, info) {
- // the server is trying to open a channel with us, this is usually when
- // we asked the server to forward us connections on some port and now they
- // are asking us to accept/deny an incoming connection on their side
-
- var localChan = false;
- var reason;
-
- function accept() {
- var chaninfo = {
- type: info.type,
- incoming: {
- id: localChan,
- window: Channel.MAX_WINDOW,
- packetSize: Channel.PACKET_SIZE,
- state: 'open'
- },
- outgoing: {
- id: info.sender,
- window: info.window,
- packetSize: info.packetSize,
- state: 'open'
- }
- };
- var stream = new Channel(chaninfo, self);
-
- self._sshstream.channelOpenConfirm(info.sender,
- localChan,
- Channel.MAX_WINDOW,
- Channel.PACKET_SIZE);
- return stream;
- }
- function reject() {
- if (reason === undefined) {
- if (localChan === false)
- reason = consts.CHANNEL_OPEN_FAILURE.RESOURCE_SHORTAGE;
- else
- reason = consts.CHANNEL_OPEN_FAILURE.CONNECT_FAILED;
- }
-
- self._sshstream.channelOpenFail(info.sender, reason, '', '');
- }
-
- if (info.type === 'forwarded-tcpip'
- || info.type === 'x11'
- || info.type === 'auth-agent@openssh.com'
- || info.type === 'forwarded-streamlocal@openssh.com') {
-
- // check for conditions for automatic rejection
- var rejectConn = (
- (info.type === 'forwarded-tcpip'
- && self._forwarding[info.data.destIP
- + ':'
- + info.data.destPort] === undefined)
- || (info.type === 'forwarded-streamlocal@openssh.com'
- && self._forwardingUnix[info.data.socketPath] === undefined)
- || (info.type === 'x11' && self._acceptX11 === 0)
- || (info.type === 'auth-agent@openssh.com'
- && !self._agentFwdEnabled)
- );
-
- if (!rejectConn) {
- localChan = nextChannel(self);
-
- if (localChan === false) {
- self.config.debug('DEBUG: Client: Automatic rejection of incoming channel open: no channels available');
- rejectConn = true;
- } else
- self._channels[localChan] = true;
- } else {
- reason = consts.CHANNEL_OPEN_FAILURE.ADMINISTRATIVELY_PROHIBITED;
- self.config.debug('DEBUG: Client: Automatic rejection of incoming channel open: unexpected channel open for: '
- + info.type);
- }
-
- // TODO: automatic rejection after some timeout?
-
- if (rejectConn)
- reject();
-
- if (localChan !== false) {
- if (info.type === 'forwarded-tcpip') {
- if (info.data.destPort === 0) {
- info.data.destPort = self._forwarding[info.data.destIP
- + ':'
- + info.data.destPort];
- }
- self.emit('tcp connection', info.data, accept, reject);
- } else if (info.type === 'x11') {
- self.emit('x11', info.data, accept, reject);
- } else if (info.type === 'forwarded-streamlocal@openssh.com') {
- self.emit('unix connection', info.data, accept, reject);
- } else {
- agentQuery(self.config.agent, accept, reject);
- }
- }
- } else {
- // automatically reject any unsupported channel open requests
- self.config.debug('DEBUG: Client: Automatic rejection of incoming channel open: unsupported type: '
- + info.type);
- reason = consts.CHANNEL_OPEN_FAILURE.UNKNOWN_CHANNEL_TYPE;
- reject();
- }
- }
-
- var randomCookie = (function() {
- if (typeof crypto.randomFillSync === 'function') {
- var buffer = Buffer.alloc(16);
- return function randomCookie() {
- crypto.randomFillSync(buffer, 0, 16);
- return buffer.toString('hex');
- };
- } else {
- return function randomCookie() {
- return crypto.randomBytes(16).toString('hex');
- };
- }
- })();
-
- Client.Client = Client;
- Client.Server = require('./server');
- // pass some useful utilities on to end user (e.g. parseKey())
- Client.utils = ssh2_streams.utils;
- // expose useful SFTPStream constants for sftp server usage
- Client.SFTP_STATUS_CODE = SFTPStream.STATUS_CODE;
- Client.SFTP_OPEN_MODE = SFTPStream.OPEN_MODE;
- // expose http(s).Agent implementations to allow easy tunneling of HTTP(S)
- // requests
- Client.HTTPAgent = HTTPAgents.SSHTTPAgent;
- Client.HTTPSAgent = HTTPAgents.SSHTTPSAgent;
-
- module.exports = Client; // backwards compatibility
|