This repository has been archived by the owner on Jan 2, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 11
/
ChannelSession.cs
executable file
·360 lines (294 loc) · 13.6 KB
/
ChannelSession.cs
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
using System.Diagnostics;
using System.Threading;
using Renci.SshNet.Common;
using Renci.SshNet.Messages.Connection;
using System.Globalization;
namespace Renci.SshNet.Channels
{
/// <summary>
/// Implements Session SSH channel.
/// </summary>
internal class ChannelSession : Channel
{
/// <summary>
/// Counts faile channel open attempts
/// </summary>
private int _failedOpenAttempts;
/// <summary>
/// Wait handle to signal when response was received to open the channel
/// </summary>
private EventWaitHandle _channelOpenResponseWaitHandle = new AutoResetEvent(false);
private EventWaitHandle _channelRequestResponse = new ManualResetEvent(false);
private bool _channelRequestSucces;
/// <summary>
/// Gets the type of the channel.
/// </summary>
/// <value>
/// The type of the channel.
/// </value>
public override ChannelTypes ChannelType
{
get { return ChannelTypes.Session; }
}
/// <summary>
/// Opens the channel.
/// </summary>
public virtual void Open()
{
if (!this.IsOpen)
{
// Try to open channel several times
while (this._failedOpenAttempts < this.ConnectionInfo.RetryAttempts && !this.IsOpen)
{
this.SendChannelOpenMessage();
this.WaitHandle(this._channelOpenResponseWaitHandle);
}
if (!this.IsOpen)
{
throw new SshException(string.Format(CultureInfo.CurrentCulture, "Failed to open a channel after {0} attempts.", this._failedOpenAttempts));
}
}
}
/// <summary>
/// Called when channel is opened by the server.
/// </summary>
/// <param name="remoteChannelNumber">The remote channel number.</param>
/// <param name="initialWindowSize">Initial size of the window.</param>
/// <param name="maximumPacketSize">Maximum size of the packet.</param>
protected override void OnOpenConfirmation(uint remoteChannelNumber, uint initialWindowSize, uint maximumPacketSize)
{
base.OnOpenConfirmation(remoteChannelNumber, initialWindowSize, maximumPacketSize);
this._channelOpenResponseWaitHandle.Set();
}
/// <summary>
/// Called when channel failed to open.
/// </summary>
/// <param name="reasonCode">The reason code.</param>
/// <param name="description">The description.</param>
/// <param name="language">The language.</param>
protected override void OnOpenFailure(uint reasonCode, string description, string language)
{
this._failedOpenAttempts++;
Debug.WriteLine(string.Format(CultureInfo.CurrentCulture, "Local channel: {0} attempts: {1}.", this.LocalChannelNumber, this._failedOpenAttempts));
this.SessionSemaphore.Release();
this._channelOpenResponseWaitHandle.Set();
}
/// <summary>
/// Called when channel is closed by the server.
/// </summary>
protected override void OnClose()
{
base.OnClose();
// This timeout needed since when channel is closed it does not immediately becomes available
// but it takes time for the server to clean up resource and allow new channels to be created.
Thread.Sleep(100);
this.SessionSemaphore.Release();
}
/// <summary>
/// Sends the pseudo terminal request.
/// </summary>
/// <param name="environmentVariable">The environment variable.</param>
/// <param name="columns">The columns.</param>
/// <param name="rows">The rows.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <param name="terminalMode">The terminal mode.</param>
/// <returns>true if request was successful; otherwise false.</returns>
public bool SendPseudoTerminalRequest(string environmentVariable, uint columns, uint rows, uint width, uint height, string terminalMode)
{
this._channelRequestResponse.Reset();
this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new PseudoTerminalRequestInfo(environmentVariable, columns, rows, width, height, terminalMode)));
this._channelRequestResponse.WaitOne();
return this._channelRequestSucces;
}
/// <summary>
/// Sends the X11 forwarding request.
/// </summary>
/// <param name="isSingleConnection">if set to <c>true</c> the it is single connection.</param>
/// <param name="protocol">The protocol.</param>
/// <param name="cookie">The cookie.</param>
/// <param name="screenNumber">The screen number.</param>
/// <returns>true if request was successful; otherwise false.</returns>
public bool SendX11ForwardingRequest(bool isSingleConnection, string protocol, byte[] cookie, uint screenNumber)
{
this._channelRequestResponse.Reset();
this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new X11ForwardingRequestInfo(isSingleConnection, protocol, cookie, screenNumber)));
this._channelRequestResponse.WaitOne();
return this._channelRequestSucces;
}
/// <summary>
/// Sends the environment variable request.
/// </summary>
/// <param name="variableName">Name of the variable.</param>
/// <param name="variableValue">The variable value.</param>
/// <returns>true if request was successful; otherwise false.</returns>
public bool SendEnvironmentVariableRequest(string variableName, string variableValue)
{
this._channelRequestResponse.Reset();
this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new EnvironmentVariableRequestInfo(variableName, variableValue)));
this._channelRequestResponse.WaitOne();
return this._channelRequestSucces;
}
/// <summary>
/// Sends the shell request.
/// </summary>
/// <returns>true if request was successful; otherwise false.</returns>
public bool SendShellRequest()
{
this._channelRequestResponse.Reset();
this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new ShellRequestInfo()));
this._channelRequestResponse.WaitOne();
return this._channelRequestSucces;
}
/// <summary>
/// Sends the exec request.
/// </summary>
/// <param name="command">The command.</param>
/// <returns>true if request was successful; otherwise false.</returns>
public bool SendExecRequest(string command)
{
this._channelRequestResponse.Reset();
this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new ExecRequestInfo(command)));
this._channelRequestResponse.WaitOne();
return this._channelRequestSucces;
}
/// <summary>
/// Sends the subsystem request.
/// </summary>
/// <param name="subsystem">The subsystem.</param>
/// <returns>true if request was successful; otherwise false.</returns>
public bool SendSubsystemRequest(string subsystem)
{
this._channelRequestResponse.Reset();
this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new SubsystemRequestInfo(subsystem)));
this._channelRequestResponse.WaitOne();
return this._channelRequestSucces;
}
/// <summary>
/// Sends the window change request.
/// </summary>
/// <param name="columns">The columns.</param>
/// <param name="rows">The rows.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <returns>true if request was successful; otherwise false.</returns>
public bool SendWindowChangeRequest(uint columns, uint rows, uint width, uint height)
{
this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new WindowChangeRequestInfo(columns, rows, width, height)));
return true;
}
/// <summary>
/// Sends the local flow request.
/// </summary>
/// <param name="clientCanDo">if set to <c>true</c> [client can do].</param>
/// <returns>true if request was successful; otherwise false.</returns>
public bool SendLocalFlowRequest(bool clientCanDo)
{
this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new XonXoffRequestInfo(clientCanDo)));
return true;
}
/// <summary>
/// Sends the signal request.
/// </summary>
/// <param name="signalName">Name of the signal.</param>
/// <returns>true if request was successful; otherwise false.</returns>
public bool SendSignalRequest(string signalName)
{
this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new SignalRequestInfo(signalName)));
return true;
}
/// <summary>
/// Sends the exit status request.
/// </summary>
/// <param name="exitStatus">The exit status.</param>
/// <returns>true if request was successful; otherwise false.</returns>
public bool SendExitStatusRequest(uint exitStatus)
{
this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new ExitStatusRequestInfo(exitStatus)));
return true;
}
/// <summary>
/// Sends the exit signal request.
/// </summary>
/// <param name="signalName">Name of the signal.</param>
/// <param name="coreDumped">if set to <c>true</c> [core dumped].</param>
/// <param name="errorMessage">The error message.</param>
/// <param name="language">The language.</param>
/// <returns>true if request was successful; otherwise false.</returns>
public bool SendExitSignalRequest(string signalName, bool coreDumped, string errorMessage, string language)
{
this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new ExitSignalRequestInfo(signalName, coreDumped, errorMessage, language)));
return true;
}
/// <summary>
/// Sends eow@openssh.com request.
/// </summary>
/// <returns>true if request was successful; otherwise false.</returns>
public bool SendEndOfWriteRequest()
{
this._channelRequestResponse.Reset();
this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new EndOfWriteRequestInfo()));
this._channelRequestResponse.WaitOne();
return this._channelRequestSucces;
}
/// <summary>
/// Sends keepalive@openssh.com request.
/// </summary>
/// <returns>true if request was successful; otherwise false.</returns>
public bool SendKeepAliveRequest()
{
this._channelRequestResponse.Reset();
this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new KeepAliveRequestInfo()));
this._channelRequestResponse.WaitOne();
return this._channelRequestSucces;
}
/// <summary>
/// Called when channel request was successful
/// </summary>
protected override void OnSuccess()
{
base.OnSuccess();
this._channelRequestSucces = true;
this._channelRequestResponse.Set();
}
/// <summary>
/// Called when channel request failed.
/// </summary>
protected override void OnFailure()
{
base.OnFailure();
this._channelRequestSucces = false;
this._channelRequestResponse.Set();
}
/// <summary>
/// Sends the channel open message.
/// </summary>
protected void SendChannelOpenMessage()
{
lock (this.SessionSemaphore)
{
// Ensure that channels are available
this.SessionSemaphore.Wait();
this.SendMessage(new ChannelOpenMessage(this.LocalChannelNumber, this.LocalWindowSize, this.PacketSize, new SessionChannelOpenInfo()));
}
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected override void Dispose(bool disposing)
{
if (this._channelOpenResponseWaitHandle != null)
{
this._channelOpenResponseWaitHandle.Dispose();
this._channelOpenResponseWaitHandle = null;
}
if (this._channelRequestResponse != null)
{
this._channelRequestResponse.Dispose();
this._channelRequestResponse = null;
}
base.Dispose(disposing);
}
}
}