@electerm/ssh2 0.8.11 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -1
- package/install.js +20 -0
- package/lib/Channel.js +236 -450
- package/lib/agent.js +1080 -376
- package/lib/client.js +1698 -1258
- package/lib/http-agents.js +72 -51
- package/lib/index.js +43 -0
- package/lib/protocol/Protocol.js +2077 -0
- package/lib/protocol/SFTP.js +3778 -0
- package/lib/protocol/constants.js +342 -0
- package/lib/protocol/crypto/binding.gyp +14 -0
- package/lib/protocol/crypto/poly1305.js +43 -0
- package/lib/protocol/crypto/src/binding.cc +2003 -0
- package/lib/protocol/crypto.js +1602 -0
- package/lib/protocol/handlers.js +16 -0
- package/lib/protocol/handlers.misc.js +1214 -0
- package/lib/protocol/kex.js +1831 -0
- package/lib/protocol/keyParser.js +1481 -0
- package/lib/protocol/node-fs-compat.js +115 -0
- package/lib/protocol/utils.js +356 -0
- package/lib/protocol/zlib.js +255 -0
- package/lib/server.js +1226 -1019
- package/lib/utils.js +336 -0
- package/package.json +42 -9
- package/lib/SFTPWrapper.js +0 -145
- package/lib/buffer-helpers.js +0 -22
- package/lib/keepalivemgr.js +0 -80
package/lib/Channel.js
CHANGED
|
@@ -1,508 +1,294 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
this.allowHalfOpen = streamOpts.allowHalfOpen;
|
|
30
|
-
|
|
31
|
-
DuplexStream.call(this, streamOpts);
|
|
32
|
-
|
|
33
|
-
var self = this;
|
|
34
|
-
var server = opts && opts.server;
|
|
35
|
-
|
|
36
|
-
this.server = server;
|
|
37
|
-
this.type = info.type;
|
|
38
|
-
this.subtype = undefined;
|
|
39
|
-
/*
|
|
40
|
-
incoming and outgoing contain these properties:
|
|
41
|
-
{
|
|
42
|
-
id: undefined,
|
|
43
|
-
window: undefined,
|
|
44
|
-
packetSize: undefined,
|
|
45
|
-
state: 'closed'
|
|
46
|
-
}
|
|
47
|
-
*/
|
|
48
|
-
var incoming = this.incoming = info.incoming;
|
|
49
|
-
var incomingId = incoming.id;
|
|
50
|
-
var outgoing = this.outgoing = info.outgoing;
|
|
51
|
-
var callbacks = this._callbacks = [];
|
|
52
|
-
var exitCode;
|
|
53
|
-
var exitSignal;
|
|
54
|
-
var exitDump;
|
|
55
|
-
var exitDesc;
|
|
56
|
-
var exitLang;
|
|
57
|
-
|
|
58
|
-
this._client = client;
|
|
59
|
-
this._hasX11 = false;
|
|
60
|
-
|
|
61
|
-
var channels = client._channels;
|
|
62
|
-
var sshstream = client._sshstream;
|
|
63
|
-
|
|
64
|
-
function ondrain() {
|
|
65
|
-
if (self._waitClientDrain) {
|
|
66
|
-
self._waitClientDrain = false;
|
|
67
|
-
if (!self._waitWindow) {
|
|
68
|
-
if (self._chunk)
|
|
69
|
-
self._write(self._chunk, null, self._chunkcb);
|
|
70
|
-
else if (self._chunkcb)
|
|
71
|
-
self._chunkcb();
|
|
72
|
-
else if (self._chunkErr)
|
|
73
|
-
self.stderr._write(self._chunkErr, null, self._chunkcbErr);
|
|
74
|
-
else if (self._chunkcbErr)
|
|
75
|
-
self._chunkcbErr();
|
|
76
|
-
}
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
Duplex: DuplexStream,
|
|
5
|
+
Readable: ReadableStream,
|
|
6
|
+
Writable: WritableStream,
|
|
7
|
+
} = require('stream');
|
|
8
|
+
|
|
9
|
+
const {
|
|
10
|
+
CHANNEL_EXTENDED_DATATYPE: { STDERR },
|
|
11
|
+
} = require('./protocol/constants.js');
|
|
12
|
+
const { bufferSlice } = require('./protocol/utils.js');
|
|
13
|
+
|
|
14
|
+
const PACKET_SIZE = 32 * 1024;
|
|
15
|
+
const MAX_WINDOW = 2 * 1024 * 1024;
|
|
16
|
+
const WINDOW_THRESHOLD = MAX_WINDOW / 2;
|
|
17
|
+
|
|
18
|
+
class ClientStderr extends ReadableStream {
|
|
19
|
+
constructor(channel, streamOpts) {
|
|
20
|
+
super(streamOpts);
|
|
21
|
+
|
|
22
|
+
this._channel = channel;
|
|
23
|
+
}
|
|
24
|
+
_read(n) {
|
|
25
|
+
if (this._channel._waitChanDrain) {
|
|
26
|
+
this._channel._waitChanDrain = false;
|
|
27
|
+
if (this._channel.incoming.window <= WINDOW_THRESHOLD)
|
|
28
|
+
windowAdjust(this._channel);
|
|
77
29
|
}
|
|
78
30
|
}
|
|
79
|
-
|
|
31
|
+
}
|
|
80
32
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
incoming.state = 'eof';
|
|
85
|
-
|
|
86
|
-
if (self.readable)
|
|
87
|
-
self.push(null);
|
|
88
|
-
if (!server && self.stderr.readable)
|
|
89
|
-
self.stderr.push(null);
|
|
90
|
-
}).once('CHANNEL_CLOSE:' + incomingId, function() {
|
|
91
|
-
if (incoming.state === 'closed')
|
|
92
|
-
return;
|
|
93
|
-
incoming.state = 'closed';
|
|
94
|
-
|
|
95
|
-
if (self.readable)
|
|
96
|
-
self.push(null);
|
|
97
|
-
if (server && self.stderr.writable)
|
|
98
|
-
self.stderr.end();
|
|
99
|
-
else if (!server && self.stderr.readable)
|
|
100
|
-
self.stderr.push(null);
|
|
101
|
-
|
|
102
|
-
if (outgoing.state === 'open' || outgoing.state === 'eof')
|
|
103
|
-
self.close();
|
|
104
|
-
if (outgoing.state === 'closing')
|
|
105
|
-
outgoing.state = 'closed';
|
|
106
|
-
|
|
107
|
-
delete channels[incomingId];
|
|
108
|
-
|
|
109
|
-
var state = self._writableState;
|
|
110
|
-
client._sock.removeListener('drain', ondrain);
|
|
111
|
-
if (!state.ending && !state.finished)
|
|
112
|
-
self.end();
|
|
113
|
-
|
|
114
|
-
// Take care of any outstanding channel requests
|
|
115
|
-
self._callbacks = [];
|
|
116
|
-
for (var i = 0; i < callbacks.length; ++i)
|
|
117
|
-
callbacks[i](true);
|
|
118
|
-
callbacks = self._callbacks;
|
|
119
|
-
|
|
120
|
-
if (!server) {
|
|
121
|
-
// align more with node child processes, where the close event gets the
|
|
122
|
-
// same arguments as the exit event
|
|
123
|
-
if (!self.readable) {
|
|
124
|
-
if (exitCode === null) {
|
|
125
|
-
self.emit('close', exitCode, exitSignal, exitDump, exitDesc,
|
|
126
|
-
exitLang);
|
|
127
|
-
} else
|
|
128
|
-
self.emit('close', exitCode);
|
|
129
|
-
} else {
|
|
130
|
-
self.once('end', function() {
|
|
131
|
-
if (exitCode === null) {
|
|
132
|
-
self.emit('close', exitCode, exitSignal, exitDump, exitDesc,
|
|
133
|
-
exitLang);
|
|
134
|
-
} else
|
|
135
|
-
self.emit('close', exitCode);
|
|
136
|
-
});
|
|
137
|
-
}
|
|
33
|
+
class ServerStderr extends WritableStream {
|
|
34
|
+
constructor(channel) {
|
|
35
|
+
super({ highWaterMark: MAX_WINDOW });
|
|
138
36
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
else {
|
|
142
|
-
self.stderr.once('end', function() {
|
|
143
|
-
self.stderr.emit('close');
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
} else { // Server mode
|
|
147
|
-
if (!self.readable)
|
|
148
|
-
self.emit('close');
|
|
149
|
-
else {
|
|
150
|
-
self.once('end', function() {
|
|
151
|
-
self.emit('close');
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
}
|
|
37
|
+
this._channel = channel;
|
|
38
|
+
}
|
|
155
39
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
40
|
+
_write(data, encoding, cb) {
|
|
41
|
+
const channel = this._channel;
|
|
42
|
+
const protocol = channel._client._protocol;
|
|
43
|
+
const outgoing = channel.outgoing;
|
|
44
|
+
const packetSize = outgoing.packetSize;
|
|
45
|
+
const id = outgoing.id;
|
|
46
|
+
let window = outgoing.window;
|
|
47
|
+
const len = data.length;
|
|
48
|
+
let p = 0;
|
|
49
|
+
|
|
50
|
+
if (outgoing.state !== 'open')
|
|
163
51
|
return;
|
|
164
52
|
|
|
165
|
-
|
|
53
|
+
while (len - p > 0 && window > 0) {
|
|
54
|
+
let sliceLen = len - p;
|
|
55
|
+
if (sliceLen > window)
|
|
56
|
+
sliceLen = window;
|
|
57
|
+
if (sliceLen > packetSize)
|
|
58
|
+
sliceLen = packetSize;
|
|
166
59
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
60
|
+
if (p === 0 && sliceLen === len)
|
|
61
|
+
protocol.channelExtData(id, data, STDERR);
|
|
62
|
+
else
|
|
63
|
+
protocol.channelExtData(id, bufferSlice(data, p, p + sliceLen), STDERR);
|
|
171
64
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}).on('CHANNEL_WINDOW_ADJUST:' + incomingId, function(amt) {
|
|
175
|
-
// the server is allowing us to send `amt` more bytes of data
|
|
176
|
-
outgoing.window += amt;
|
|
177
|
-
|
|
178
|
-
if (self._waitWindow) {
|
|
179
|
-
self._waitWindow = false;
|
|
180
|
-
if (!self._waitClientDrain) {
|
|
181
|
-
if (self._chunk)
|
|
182
|
-
self._write(self._chunk, null, self._chunkcb);
|
|
183
|
-
else if (self._chunkcb)
|
|
184
|
-
self._chunkcb();
|
|
185
|
-
else if (self._chunkErr)
|
|
186
|
-
self.stderr._write(self._chunkErr, null, self._chunkcbErr);
|
|
187
|
-
else if (self._chunkcbErr)
|
|
188
|
-
self._chunkcbErr();
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
}).on('CHANNEL_SUCCESS:' + incomingId, function() {
|
|
192
|
-
if (server) {
|
|
193
|
-
sshstream._kalast = Date.now();
|
|
194
|
-
sshstream._kacnt = 0;
|
|
195
|
-
} else
|
|
196
|
-
client._resetKA();
|
|
197
|
-
if (callbacks.length)
|
|
198
|
-
callbacks.shift()(false);
|
|
199
|
-
}).on('CHANNEL_FAILURE:' + incomingId, function() {
|
|
200
|
-
if (server) {
|
|
201
|
-
sshstream._kalast = Date.now();
|
|
202
|
-
sshstream._kacnt = 0;
|
|
203
|
-
} else
|
|
204
|
-
client._resetKA();
|
|
205
|
-
if (callbacks.length)
|
|
206
|
-
callbacks.shift()(true);
|
|
207
|
-
}).on('CHANNEL_REQUEST:' + incomingId, function(info) {
|
|
208
|
-
if (!server) {
|
|
209
|
-
if (info.request === 'exit-status') {
|
|
210
|
-
self.emit('exit', exitCode = info.code);
|
|
211
|
-
return;
|
|
212
|
-
} else if (info.request === 'exit-signal') {
|
|
213
|
-
self.emit('exit',
|
|
214
|
-
exitCode = null,
|
|
215
|
-
exitSignal = 'SIG' + info.signal,
|
|
216
|
-
exitDump = info.coredump,
|
|
217
|
-
exitDesc = info.description,
|
|
218
|
-
exitLang = info.lang);
|
|
219
|
-
return;
|
|
220
|
-
}
|
|
65
|
+
p += sliceLen;
|
|
66
|
+
window -= sliceLen;
|
|
221
67
|
}
|
|
222
68
|
|
|
223
|
-
|
|
224
|
-
// is a channel open
|
|
69
|
+
outgoing.window = window;
|
|
225
70
|
|
|
226
|
-
if (
|
|
227
|
-
|
|
228
|
-
|
|
71
|
+
if (len - p > 0) {
|
|
72
|
+
if (window === 0)
|
|
73
|
+
channel._waitWindow = true;
|
|
74
|
+
if (p > 0)
|
|
75
|
+
channel._chunkErr = bufferSlice(data, p, len);
|
|
76
|
+
else
|
|
77
|
+
channel._chunkErr = data;
|
|
78
|
+
channel._chunkcbErr = cb;
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
229
81
|
|
|
230
|
-
|
|
82
|
+
cb();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
231
85
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
86
|
+
class Channel extends DuplexStream {
|
|
87
|
+
constructor(client, info, opts) {
|
|
88
|
+
const streamOpts = {
|
|
89
|
+
highWaterMark: MAX_WINDOW,
|
|
90
|
+
allowHalfOpen: (!opts || (opts && opts.allowHalfOpen !== false)),
|
|
91
|
+
emitClose: false,
|
|
92
|
+
};
|
|
93
|
+
super(streamOpts);
|
|
94
|
+
this.allowHalfOpen = streamOpts.allowHalfOpen;
|
|
95
|
+
|
|
96
|
+
const server = !!(opts && opts.server);
|
|
97
|
+
|
|
98
|
+
this.server = server;
|
|
99
|
+
this.type = info.type;
|
|
100
|
+
this.subtype = undefined;
|
|
101
|
+
|
|
102
|
+
/*
|
|
103
|
+
incoming and outgoing contain these properties:
|
|
104
|
+
{
|
|
105
|
+
id: undefined,
|
|
106
|
+
window: undefined,
|
|
107
|
+
packetSize: undefined,
|
|
108
|
+
state: 'closed'
|
|
241
109
|
}
|
|
110
|
+
*/
|
|
111
|
+
this.incoming = info.incoming;
|
|
112
|
+
this.outgoing = info.outgoing;
|
|
113
|
+
this._callbacks = [];
|
|
114
|
+
|
|
115
|
+
this._client = client;
|
|
116
|
+
this._hasX11 = false;
|
|
117
|
+
this._exit = {
|
|
118
|
+
code: undefined,
|
|
119
|
+
signal: undefined,
|
|
120
|
+
dump: undefined,
|
|
121
|
+
desc: undefined,
|
|
242
122
|
};
|
|
243
123
|
|
|
244
|
-
|
|
245
|
-
function(type, data) {
|
|
246
|
-
// the remote party should not be sending us data if there is no window
|
|
247
|
-
// space available ...
|
|
248
|
-
// TODO: raise error on data with not enough window
|
|
249
|
-
if (incoming.window === 0)
|
|
250
|
-
return;
|
|
124
|
+
this.stdin = this.stdout = this;
|
|
251
125
|
|
|
252
|
-
|
|
126
|
+
if (server)
|
|
127
|
+
this.stderr = new ServerStderr(this);
|
|
128
|
+
else
|
|
129
|
+
this.stderr = new ClientStderr(this, streamOpts);
|
|
253
130
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
return;
|
|
257
|
-
}
|
|
131
|
+
// Outgoing data
|
|
132
|
+
this._waitWindow = false; // SSH-level backpressure
|
|
258
133
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
}
|
|
262
|
-
);
|
|
263
|
-
}
|
|
134
|
+
// Incoming data
|
|
135
|
+
this._waitChanDrain = false; // Channel Readable side backpressure
|
|
264
136
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
137
|
+
this._chunk = undefined;
|
|
138
|
+
this._chunkcb = undefined;
|
|
139
|
+
this._chunkErr = undefined;
|
|
140
|
+
this._chunkcbErr = undefined;
|
|
268
141
|
|
|
269
|
-
|
|
270
|
-
|
|
142
|
+
this.on('finish', onFinish)
|
|
143
|
+
.on('prefinish', onFinish); // For node v0.11+
|
|
271
144
|
|
|
272
|
-
|
|
273
|
-
this._chunkcb = undefined;
|
|
274
|
-
this._chunkErr = undefined;
|
|
275
|
-
this._chunkcbErr = undefined;
|
|
276
|
-
|
|
277
|
-
function onFinish() {
|
|
278
|
-
self.eof();
|
|
279
|
-
if (server || (!server && !self.allowHalfOpen))
|
|
280
|
-
self.close();
|
|
281
|
-
self.writable = false;
|
|
282
|
-
}
|
|
283
|
-
this.on('finish', onFinish)
|
|
284
|
-
.on('prefinish', onFinish); // for node v0.11+
|
|
285
|
-
function onEnd() {
|
|
286
|
-
self.readable = false;
|
|
145
|
+
this.on('end', onEnd).on('close', onEnd);
|
|
287
146
|
}
|
|
288
|
-
this.on('end', onEnd)
|
|
289
|
-
.on('close', onEnd);
|
|
290
|
-
}
|
|
291
|
-
inherits(Channel, DuplexStream);
|
|
292
|
-
|
|
293
|
-
Channel.prototype.eof = function() {
|
|
294
|
-
var ret = true;
|
|
295
|
-
var outgoing = this.outgoing;
|
|
296
147
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
148
|
+
_read(n) {
|
|
149
|
+
if (this._waitChanDrain) {
|
|
150
|
+
this._waitChanDrain = false;
|
|
151
|
+
if (this.incoming.window <= WINDOW_THRESHOLD)
|
|
152
|
+
windowAdjust(this);
|
|
153
|
+
}
|
|
300
154
|
}
|
|
301
155
|
|
|
302
|
-
|
|
303
|
-
|
|
156
|
+
_write(data, encoding, cb) {
|
|
157
|
+
const protocol = this._client._protocol;
|
|
158
|
+
const outgoing = this.outgoing;
|
|
159
|
+
const packetSize = outgoing.packetSize;
|
|
160
|
+
const id = outgoing.id;
|
|
161
|
+
let window = outgoing.window;
|
|
162
|
+
const len = data.length;
|
|
163
|
+
let p = 0;
|
|
304
164
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
var outgoing = this.outgoing;
|
|
165
|
+
if (outgoing.state !== 'open')
|
|
166
|
+
return;
|
|
308
167
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
168
|
+
while (len - p > 0 && window > 0) {
|
|
169
|
+
let sliceLen = len - p;
|
|
170
|
+
if (sliceLen > window)
|
|
171
|
+
sliceLen = window;
|
|
172
|
+
if (sliceLen > packetSize)
|
|
173
|
+
sliceLen = packetSize;
|
|
313
174
|
|
|
314
|
-
|
|
315
|
-
|
|
175
|
+
if (p === 0 && sliceLen === len)
|
|
176
|
+
protocol.channelData(id, data);
|
|
177
|
+
else
|
|
178
|
+
protocol.channelData(id, bufferSlice(data, p, p + sliceLen));
|
|
316
179
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
if (this.incoming.window <= WINDOW_THRESHOLD)
|
|
321
|
-
windowAdjust(this);
|
|
322
|
-
}
|
|
323
|
-
};
|
|
180
|
+
p += sliceLen;
|
|
181
|
+
window -= sliceLen;
|
|
182
|
+
}
|
|
324
183
|
|
|
325
|
-
|
|
326
|
-
var sshstream = this._client._sshstream;
|
|
327
|
-
var outgoing = this.outgoing;
|
|
328
|
-
var packetSize = outgoing.packetSize;
|
|
329
|
-
var id = outgoing.id;
|
|
330
|
-
var window = outgoing.window;
|
|
331
|
-
var len = data.length;
|
|
332
|
-
var p = 0;
|
|
333
|
-
var ret;
|
|
334
|
-
var buf;
|
|
335
|
-
var sliceLen;
|
|
336
|
-
|
|
337
|
-
if (outgoing.state !== 'open')
|
|
338
|
-
return;
|
|
184
|
+
outgoing.window = window;
|
|
339
185
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
186
|
+
if (len - p > 0) {
|
|
187
|
+
if (window === 0)
|
|
188
|
+
this._waitWindow = true;
|
|
189
|
+
if (p > 0)
|
|
190
|
+
this._chunk = bufferSlice(data, p, len);
|
|
191
|
+
else
|
|
192
|
+
this._chunk = data;
|
|
193
|
+
this._chunkcb = cb;
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
346
196
|
|
|
347
|
-
|
|
197
|
+
cb();
|
|
198
|
+
}
|
|
348
199
|
|
|
349
|
-
|
|
350
|
-
|
|
200
|
+
eof() {
|
|
201
|
+
if (this.outgoing.state === 'open') {
|
|
202
|
+
this.outgoing.state = 'eof';
|
|
203
|
+
this._client._protocol.channelEOF(this.outgoing.id);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
351
206
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
this.
|
|
355
|
-
this.
|
|
356
|
-
break;
|
|
207
|
+
close() {
|
|
208
|
+
if (this.outgoing.state === 'open' || this.outgoing.state === 'eof') {
|
|
209
|
+
this.outgoing.state = 'closing';
|
|
210
|
+
this._client._protocol.channelClose(this.outgoing.id);
|
|
357
211
|
}
|
|
358
212
|
}
|
|
359
213
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
if (window === 0)
|
|
364
|
-
this._waitWindow = true;
|
|
365
|
-
if (p > 0) {
|
|
366
|
-
// partial
|
|
367
|
-
buf = Buffer.allocUnsafe(len - p);
|
|
368
|
-
data.copy(buf, 0, p);
|
|
369
|
-
this._chunk = buf;
|
|
370
|
-
} else
|
|
371
|
-
this._chunk = data;
|
|
372
|
-
this._chunkcb = cb;
|
|
373
|
-
return;
|
|
214
|
+
destroy() {
|
|
215
|
+
this.end();
|
|
216
|
+
this.close();
|
|
374
217
|
}
|
|
375
218
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
219
|
+
// Session type-specific methods =============================================
|
|
220
|
+
setWindow(rows, cols, height, width) {
|
|
221
|
+
if (this.server)
|
|
222
|
+
throw new Error('Client-only method called in server mode');
|
|
223
|
+
|
|
224
|
+
if (this.type === 'session'
|
|
225
|
+
&& (this.subtype === 'shell' || this.subtype === 'exec')
|
|
226
|
+
&& this.writable
|
|
227
|
+
&& this.outgoing.state === 'open') {
|
|
228
|
+
this._client._protocol.windowChange(this.outgoing.id,
|
|
229
|
+
rows,
|
|
230
|
+
cols,
|
|
231
|
+
height,
|
|
232
|
+
width);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
379
235
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
236
|
+
signal(signalName) {
|
|
237
|
+
if (this.server)
|
|
238
|
+
throw new Error('Client-only method called in server mode');
|
|
383
239
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
if (this.type === 'session'
|
|
390
|
-
&& (this.subtype === 'shell' || this.subtype === 'exec')
|
|
391
|
-
&& this.writable
|
|
392
|
-
&& this.outgoing.state === 'open') {
|
|
393
|
-
return this._client._sshstream.windowChange(this.outgoing.id,
|
|
394
|
-
rows,
|
|
395
|
-
cols,
|
|
396
|
-
height,
|
|
397
|
-
width);
|
|
240
|
+
if (this.type === 'session'
|
|
241
|
+
&& this.writable
|
|
242
|
+
&& this.outgoing.state === 'open') {
|
|
243
|
+
this._client._protocol.signal(this.outgoing.id, signalName);
|
|
244
|
+
}
|
|
398
245
|
}
|
|
399
246
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
if (this.server)
|
|
404
|
-
throw new Error('Client-only method called in server mode');
|
|
247
|
+
exit(statusOrSignal, coreDumped, msg) {
|
|
248
|
+
if (!this.server)
|
|
249
|
+
throw new Error('Server-only method called in client mode');
|
|
405
250
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
if (this.type === 'session'
|
|
418
|
-
&& this.writable
|
|
419
|
-
&& this.outgoing.state === 'open') {
|
|
420
|
-
if (typeof name === 'number')
|
|
421
|
-
return this._client._sshstream.exitStatus(this.outgoing.id, name);
|
|
422
|
-
else {
|
|
423
|
-
return this._client._sshstream.exitSignal(this.outgoing.id,
|
|
424
|
-
name,
|
|
425
|
-
coreDumped,
|
|
426
|
-
msg);
|
|
251
|
+
if (this.type === 'session'
|
|
252
|
+
&& this.writable
|
|
253
|
+
&& this.outgoing.state === 'open') {
|
|
254
|
+
if (typeof statusOrSignal === 'number') {
|
|
255
|
+
this._client._protocol.exitStatus(this.outgoing.id, statusOrSignal);
|
|
256
|
+
} else {
|
|
257
|
+
this._client._protocol.exitSignal(this.outgoing.id,
|
|
258
|
+
statusOrSignal,
|
|
259
|
+
coreDumped,
|
|
260
|
+
msg);
|
|
261
|
+
}
|
|
427
262
|
}
|
|
428
263
|
}
|
|
429
264
|
|
|
430
|
-
|
|
431
|
-
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function onFinish() {
|
|
268
|
+
this.eof();
|
|
269
|
+
if (this.server || !this.allowHalfOpen)
|
|
270
|
+
this.close();
|
|
271
|
+
this.writable = false;
|
|
272
|
+
}
|
|
432
273
|
|
|
433
|
-
|
|
434
|
-
|
|
274
|
+
function onEnd() {
|
|
275
|
+
this.readable = false;
|
|
276
|
+
}
|
|
435
277
|
|
|
436
278
|
function windowAdjust(self) {
|
|
437
279
|
if (self.outgoing.state === 'closed')
|
|
438
|
-
return
|
|
439
|
-
|
|
280
|
+
return;
|
|
281
|
+
const amt = MAX_WINDOW - self.incoming.window;
|
|
440
282
|
if (amt <= 0)
|
|
441
|
-
return
|
|
283
|
+
return;
|
|
442
284
|
self.incoming.window += amt;
|
|
443
|
-
|
|
285
|
+
self._client._protocol.channelWindowAdjust(self.outgoing.id, amt);
|
|
444
286
|
}
|
|
445
287
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
ServerStderr.prototype._write = function(data, encoding, cb) {
|
|
453
|
-
var channel = this._channel;
|
|
454
|
-
var sshstream = channel._client._sshstream;
|
|
455
|
-
var outgoing = channel.outgoing;
|
|
456
|
-
var packetSize = outgoing.packetSize;
|
|
457
|
-
var id = outgoing.id;
|
|
458
|
-
var window = outgoing.window;
|
|
459
|
-
var len = data.length;
|
|
460
|
-
var p = 0;
|
|
461
|
-
var ret;
|
|
462
|
-
var buf;
|
|
463
|
-
var sliceLen;
|
|
464
|
-
|
|
465
|
-
if (channel.outgoing.state !== 'open')
|
|
466
|
-
return;
|
|
467
|
-
|
|
468
|
-
while (len - p > 0 && window > 0) {
|
|
469
|
-
sliceLen = len - p;
|
|
470
|
-
if (sliceLen > window)
|
|
471
|
-
sliceLen = window;
|
|
472
|
-
if (sliceLen > packetSize)
|
|
473
|
-
sliceLen = packetSize;
|
|
474
|
-
|
|
475
|
-
ret = sshstream.channelExtData(id, data.slice(p, p + sliceLen), STDERR);
|
|
476
|
-
|
|
477
|
-
p += sliceLen;
|
|
478
|
-
window -= sliceLen;
|
|
479
|
-
|
|
480
|
-
if (!ret) {
|
|
481
|
-
channel._waitClientDrain = true;
|
|
482
|
-
channel._chunkErr = undefined;
|
|
483
|
-
channel._chunkcbErr = cb;
|
|
484
|
-
break;
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
outgoing.window = window;
|
|
489
|
-
|
|
490
|
-
if (len - p > 0) {
|
|
491
|
-
if (window === 0)
|
|
492
|
-
channel._waitWindow = true;
|
|
493
|
-
if (p > 0) {
|
|
494
|
-
// partial
|
|
495
|
-
buf = Buffer.allocUnsafe(len - p);
|
|
496
|
-
data.copy(buf, 0, p);
|
|
497
|
-
channel._chunkErr = buf;
|
|
498
|
-
} else
|
|
499
|
-
channel._chunkErr = data;
|
|
500
|
-
channel._chunkcbErr = cb;
|
|
501
|
-
return;
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
if (!channel._waitClientDrain)
|
|
505
|
-
cb();
|
|
288
|
+
module.exports = {
|
|
289
|
+
Channel,
|
|
290
|
+
MAX_WINDOW,
|
|
291
|
+
PACKET_SIZE,
|
|
292
|
+
windowAdjust,
|
|
293
|
+
WINDOW_THRESHOLD,
|
|
506
294
|
};
|
|
507
|
-
|
|
508
|
-
module.exports = Channel;
|