Comparing version
@@ -48,2 +48,3 @@ var inherits = require('util').inherits, | ||
this._callbacks = []; | ||
this._hasX11 = false; | ||
@@ -248,5 +249,4 @@ var onDrain = function() { | ||
if (had_err) | ||
cb(new Error('Unable to request a pseudo-terminal')); | ||
else | ||
cb(); | ||
return cb(new Error('Unable to request a pseudo-terminal')); | ||
cb(); | ||
}); | ||
@@ -388,2 +388,52 @@ | ||
Channel.prototype._sendX11 = function(cfg, cb) { | ||
// Note: CHANNEL_REQUEST does not consume window space | ||
/* | ||
byte SSH_MSG_CHANNEL_REQUEST | ||
uint32 recipient channel | ||
string "x11-req" | ||
boolean want reply | ||
boolean single connection | ||
string x11 authentication protocol | ||
string x11 authentication cookie | ||
uint32 x11 screen number | ||
*/ | ||
this._conn._debug&&this._conn._debug('DEBUG: Channel: Sent CHANNEL_REQUEST (x11)'); | ||
var self = this; | ||
var protolen = Buffer.byteLength(cfg.proto), | ||
cookielen = Buffer.byteLength(cfg.cookie), | ||
buf = new Buffer(1 + 4 + 4 + 7 + 1 + 1 + 4 + protolen + 4 + cookielen + 4); | ||
buf[0] = MESSAGE.CHANNEL_REQUEST; | ||
buf.writeUInt32BE(this.outgoing.id, 1, true); | ||
buf.writeUInt32BE(7, 5, true); | ||
buf.write('x11-req', 9, 7, 'ascii'); | ||
buf[16] = 1; | ||
buf[17] = (cfg.single ? 1 : 0); | ||
buf.writeUInt32BE(protolen, 18, true); | ||
var bp = 22; | ||
if (Buffer.isBuffer(cfg.proto)) | ||
cfg.proto.copy(buf, bp); | ||
else | ||
buf.write(cfg.proto, bp, protolen, 'utf8'); | ||
bp += protolen; | ||
buf.writeUInt32BE(cookielen, bp, true); | ||
bp += 4; | ||
if (Buffer.isBuffer(cfg.cookie)) | ||
cfg.cookie.copy(buf, bp); | ||
else | ||
buf.write(cfg.cookie, bp, cookielen, 'utf8'); | ||
bp += cookielen; | ||
buf.writeUInt32BE((cfg.screen || 0), bp, true); | ||
this._callbacks.push(function(had_err) { | ||
if (had_err) | ||
return cb(new Error('Unable to request X11')); | ||
self._hasX11 = true; | ||
++self._conn._acceptX11; | ||
cb(); | ||
}); | ||
return this._conn._send(buf); | ||
}; | ||
Channel.prototype._sendSubsystem = function(name, cb) { | ||
@@ -708,2 +758,4 @@ // Note: CHANNEL_REQUEST does not consume window space | ||
} | ||
if (this._channel._hasX11) | ||
--this._channel._conn._acceptX11; | ||
this._cleanup(); | ||
@@ -719,2 +771,4 @@ return ret; | ||
if (this._channel) { | ||
if (this._channel._hasX11) | ||
this._channel._hasX11 = false; | ||
this._channel._conn._sock.removeListener('end', this._sockOnEnd); | ||
@@ -721,0 +775,0 @@ this._channel._conn._sock.removeListener('close', this._sockOnEnd); |
@@ -57,2 +57,6 @@ var i = 0, keys, len; | ||
exports.USERAUTH_INFO_RESPONSE = 61; | ||
exports.KEXDH_GEX_REQUEST = 34; | ||
exports.KEXDH_GEX_GROUP = 31; | ||
exports.KEXDH_GEX_INIT = 32; | ||
exports.KEXDH_GEX_REPLY = 33; | ||
@@ -220,2 +224,8 @@ var DISCONNECT_REASON = exports.DISCONNECT_REASON = { | ||
].concat(CIPHER); | ||
KEX = [ | ||
'diffie-hellman-group-exchange-sha256', | ||
'diffie-hellman-group-exchange-sha1' | ||
].concat(KEX); | ||
KEX_LIST = new Buffer(KEX.join(',')); | ||
} | ||
@@ -222,0 +232,0 @@ CIPHER = [ |
@@ -41,3 +41,10 @@ // TODO: * Filter control codes from strings | ||
var i = start, buffer, skipDecrypt = false, buf, self = this, p = i, r; | ||
var i = start, | ||
buffer, | ||
skipDecrypt = false, | ||
buf, | ||
self = this, | ||
p = i, | ||
r, | ||
doDecryptGCM = false; | ||
@@ -153,3 +160,3 @@ while (true) { | ||
this.debug&&this.debug('DEBUG: Parser: STATE_PACKET'); | ||
var doDecryptGCM = (this._decrypt && isGCM(this._decryptType)); | ||
doDecryptGCM = (this._decrypt && isGCM(this._decryptType)); | ||
if (this._decrypt && !isGCM(this._decryptType)) | ||
@@ -187,3 +194,3 @@ buffer = this.decrypt(buffer); | ||
this.debug&&this.debug('DEBUG: Parser: STATE_PACKETDATA'); | ||
var doDecryptGCM = (this._decrypt && isGCM(this._decryptType)); | ||
doDecryptGCM = (this._decrypt && isGCM(this._decryptType)); | ||
if (this._decrypt && !skipDecrypt && !doDecryptGCM) | ||
@@ -236,3 +243,7 @@ buffer = this.decrypt(buffer); | ||
this.debug('DEBUG: Parser: STATE_PACKETDATAAFTER, packet: USERAUTH_PK_OK'); | ||
} else { | ||
} else if (this._payload[0] === 31 && this._kexdh !== 'group') | ||
this.debug('DEBUG: Parser: STATE_PACKETDATAAFTER, packet: KEXDH_GEX_GROUP'); | ||
else if (this._payload[0] === 33 && this._kexdh !== 'group') | ||
this.debug('DEBUG: Parser: STATE_PACKETDATAAFTER, packet: KEXDH_GEX_REPLY'); | ||
else { | ||
this.debug('DEBUG: Parser: STATE_PACKETDATAAFTER, packet: ' | ||
@@ -443,21 +454,18 @@ + MESSAGE[this._payload[0]]); | ||
this.parseKEXInit(); | ||
else if (type === MESSAGE.KEXDH_REPLY) { | ||
/* | ||
byte SSH_MSG_KEXDH_REPLY | ||
string server public host key and certificates (K_S) | ||
mpint f | ||
string signature of H | ||
*/ | ||
info = { | ||
hostkey: readString(payload, 1), | ||
hostkey_format: undefined, | ||
pubkey: readString(payload, payload._pos), | ||
sig: readString(payload, payload._pos), | ||
sig_format: undefined | ||
}; | ||
info.hostkey_format = readString(info.hostkey, 0, 'ascii'); | ||
info.sig_format = readString(info.sig, 0, 'ascii'); | ||
this.emit('KEXDH_REPLY', info); | ||
} else if (type === MESSAGE.NEWKEYS) { | ||
else if (type === 31) { // key exchange method-specific message | ||
if (this._kexdh !== 'group') { | ||
/* | ||
byte SSH_MSG_KEX_DH_GEX_GROUP | ||
mpint p, safe prime | ||
mpint g, generator for subgroup in GF(p) | ||
*/ | ||
var prime = readString(payload, 1), | ||
gen = readString(payload, payload._pos); | ||
this.emit('KEXDH_GEX_GROUP', prime, gen); | ||
} else | ||
this.parseKEXDH_REPLY(); | ||
} else if (type === consts.KEXDH_GEX_REPLY) | ||
this.parseKEXDH_REPLY(); | ||
else if (type === MESSAGE.NEWKEYS) { | ||
/* | ||
byte SSH_MSG_NEW_KEYS | ||
@@ -527,2 +535,21 @@ */ | ||
this.emit('CHANNEL_OPEN', channel); | ||
} else if (chanType === 'x11') { | ||
/* | ||
string originator address (e.g., "192.168.7.38") | ||
uint32 originator port | ||
*/ | ||
var channel = { | ||
type: chanType, | ||
sender: payload.readUInt32BE(payload._pos, true), | ||
window: payload.readUInt32BE(payload._pos += 4, true), | ||
packetSize: payload.readUInt32BE(payload._pos += 4, true), | ||
data: { | ||
srcIP: readString(payload, payload._pos += 4, 'ascii'), | ||
srcPort: payload.readUInt32BE(payload._pos, true) | ||
} | ||
}; | ||
this.emit('CHANNEL_OPEN', channel); | ||
} else { | ||
// allow connection to reject unsupported channel open requests | ||
this.emit('CHANNEL_OPEN', { type: chanType }); | ||
} | ||
@@ -644,2 +671,22 @@ } else if (type === MESSAGE.CHANNEL_OPEN_CONFIRMATION) { | ||
Parser.prototype.parseKEXDH_REPLY = function() { | ||
var payload = this._payload; | ||
/* | ||
byte SSH_MSG_KEXDH_REPLY / SSH_MSG_KEX_DH_GEX_REPLY | ||
string server public host key and certificates (K_S) | ||
mpint f | ||
string signature of H | ||
*/ | ||
var info = { | ||
hostkey: readString(payload, 1), | ||
hostkey_format: undefined, | ||
pubkey: readString(payload, payload._pos), | ||
sig: readString(payload, payload._pos), | ||
sig_format: undefined | ||
}; | ||
info.hostkey_format = readString(info.hostkey, 0, 'ascii'); | ||
info.sig_format = readString(info.sig, 0, 'ascii'); | ||
this.emit('KEXDH_REPLY', info); | ||
}; | ||
Parser.prototype.hmacVerify = function(hmac) { | ||
@@ -716,2 +763,3 @@ this.debug&&this.debug('DEBUG: Parser: Verifying MAC'); | ||
this._kexinit = undefined; | ||
this._kexdh = undefined; | ||
}; | ||
@@ -718,0 +766,0 @@ |
{ "name": "ssh2", | ||
"version": "0.2.19", | ||
"version": "0.2.20", | ||
"author": "Brian White <mscdex@mscdex.net>", | ||
@@ -4,0 +4,0 @@ "description": "An SSH2 client module written in pure JavaScript for node.js", |
@@ -351,2 +351,42 @@ Description | ||
* Forward X11 connections (xeyes): | ||
```javascript | ||
var net = require('net'), | ||
Connection = require('ssh2'); | ||
var conn = new Connection(); | ||
conn.on('x11', function(info, accept, reject) { | ||
var xserversock = new net.Socket(); | ||
xserversock.on('connect', function() { | ||
var xclientsock = accept(); | ||
xclientsock.pipe(xserversock).pipe(xclientsock); | ||
}); | ||
// connects to localhost:0.0 | ||
xserversock.connect(6000, 'localhost'); | ||
}); | ||
conn.on('ready', function() { | ||
conn.exec('xeyes', { x11: true }, function(err, stream) { | ||
if (err) throw err; | ||
var code = 0; | ||
stream.on('end', function() { | ||
if (code !== 0) | ||
console.log('Do you have X11 forwarding enabled on your SSH server?'); | ||
conn.end(); | ||
}); | ||
stream.on('exit', function(exitcode) { | ||
code = exitcode; | ||
}); | ||
}); | ||
}); | ||
conn.connect({ | ||
host: '192.168.1.1', | ||
username: 'foo', | ||
password: 'bar' | ||
}); | ||
``` | ||
* Invoke an arbitrary subsystem (netconf in this example): | ||
@@ -417,2 +457,8 @@ | ||
* **x11**(< _object_ >details, < _function_ >accept, < _function_ >reject) - An incoming X11 connection is being requested. Calling `accept` accepts the connection and returns a `ChannelStream` object. Calling `reject` rejects the connection and no further action is needed. `details` contains: | ||
* **srcIP** - _string_ - The originating IP of the connection. | ||
* **srcPort** - _integer_ - The originating port of the connection. | ||
* **keyboard-interactive**(< _string_ >name, < _string_ >instructions, < _string_ >instructionsLang, < _array_ >prompts, < _function_ >finish) - The server is asking for replies to the given `prompts` for keyboard-interactive user authentication. `name` is generally what you'd use as a window title (for GUI apps). `prompts` is an array of `{ prompt: 'Password: ', echo: false }` style objects (here `echo` indicates whether user input should be displayed on the screen). The answers for all prompts must be provided as an array of strings and passed to `finish` when you are ready to continue. Note: It's possible for the server to come back and ask more questions. | ||
@@ -474,5 +520,11 @@ | ||
* **x11** - < _mixed_ > - Set to true to use defaults below, a number to specify a specific screen number, or an object with the following valid properties: | ||
* **single** - < _boolean_ > - Allow just a single connection? **Default:** false | ||
* **screen** - < _number_ > - Screen number to use **Default:** 0 | ||
`callback` has 2 parameters: < _Error_ >err, < _ChannelStream_ >stream. | ||
* **shell**([< _object_ >window,] < _function_ >callback) - _(void)_ - Starts an interactive shell session on the server, with optional `window` pseudo-tty settings (see 'Pseudo-TTY settings'). `callback` has 2 parameters: < _Error_ >err, < _ChannelStream_ >stream. | ||
* **shell**([[< _object_ >window,] < _object_ >options]< _function_ >callback) - _(void)_ - Starts an interactive shell session on the server, with optional `window` pseudo-tty settings (see 'Pseudo-TTY settings'). `options` supports the 'x11' option as described in exec(). `callback` has 2 parameters: < _Error_ >err, < _ChannelStream_ >stream. | ||
@@ -479,0 +531,0 @@ * **forwardIn**(< _string_ >remoteAddr, < _integer_ >remotePort, < _function_ >callback) - _(void)_ - Bind to `remoteAddr` on `remotePort` on the server and forward incoming connections. `callback` has 2 parameters: < _Error_ >err, < _integer_ >port (`port` is the assigned port number if `remotePort` was 0). Here are some special values for `remoteAddr` and their associated binding behaviors: |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
295414
4.27%6115
4.89%747
7.48%