-
-
Notifications
You must be signed in to change notification settings - Fork 980
Expand file tree
/
Copy pathChannelForwardedTcpip.cs
More file actions
215 lines (189 loc) · 7.52 KB
/
ChannelForwardedTcpip.cs
File metadata and controls
215 lines (189 loc) · 7.52 KB
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
using System;
using System.Net;
using System.Net.Sockets;
#if NET9_0_OR_GREATER
using System.Threading;
#endif
using Microsoft.Extensions.Logging;
using Renci.SshNet.Abstractions;
using Renci.SshNet.Common;
using Renci.SshNet.Messages.Connection;
namespace Renci.SshNet.Channels
{
/// <summary>
/// Implements "forwarded-tcpip" SSH channel.
/// </summary>
internal sealed class ChannelForwardedTcpip : ServerChannel, IChannelForwardedTcpip
{
private readonly Lock _socketShutdownAndCloseLock = new Lock();
private readonly ILogger _logger;
private Socket _socket;
private IForwardedPort _forwardedPort;
/// <summary>
/// Initializes a new instance of the <see cref="ChannelForwardedTcpip"/> class.
/// </summary>
/// <param name="session">The session.</param>
/// <param name="localChannelNumber">The local channel number.</param>
/// <param name="localWindowSize">Size of the window.</param>
/// <param name="localPacketSize">Size of the packet.</param>
/// <param name="remoteChannelNumber">The remote channel number.</param>
/// <param name="remoteWindowSize">The window size of the remote party.</param>
/// <param name="remotePacketSize">The maximum size of a data packet that we can send to the remote party.</param>
internal ChannelForwardedTcpip(ISession session,
uint localChannelNumber,
uint localWindowSize,
uint localPacketSize,
uint remoteChannelNumber,
uint remoteWindowSize,
uint remotePacketSize)
: base(session,
localChannelNumber,
localWindowSize,
localPacketSize,
remoteChannelNumber,
remoteWindowSize,
remotePacketSize)
{
_logger = session.SessionLoggerFactory.CreateLogger<ChannelForwardedTcpip>();
}
/// <summary>
/// Gets the type of the channel.
/// </summary>
/// <value>
/// The type of the channel.
/// </value>
public override ChannelTypes ChannelType
{
get { return ChannelTypes.ForwardedTcpip; }
}
/// <summary>
/// Binds the channel to the specified endpoint.
/// </summary>
/// <param name="remoteEndpoint">The endpoint to connect to.</param>
/// <param name="forwardedPort">The forwarded port for which the channel is opened.</param>
public void Bind(IPEndPoint remoteEndpoint, IForwardedPort forwardedPort)
{
if (!IsConnected)
{
throw new SshException("Session is not connected.");
}
_forwardedPort = forwardedPort;
_forwardedPort.Closing += ForwardedPort_Closing;
// Try to connect to the socket
try
{
_socket = SocketAbstraction.Connect(remoteEndpoint, ConnectionInfo.Timeout);
// Send channel open confirmation message
SendMessage(new ChannelOpenConfirmationMessage(RemoteChannelNumber, LocalWindowSize, LocalPacketSize, LocalChannelNumber));
}
catch (Exception exp)
{
// Send channel open failure message
SendMessage(new ChannelOpenFailureMessage(RemoteChannelNumber, exp.ToString(), ChannelOpenFailureMessage.ConnectFailed, "en"));
throw;
}
var buffer = new byte[RemotePacketSize];
SocketAbstraction.ReadContinuous(_socket, buffer, 0, buffer.Length, SendData);
}
protected override void OnErrorOccurred(Exception exp)
{
base.OnErrorOccurred(exp);
// signal to the server that we will not send anything anymore; this will also interrupt the
// blocking receive in Bind if the server sends FIN/ACK in time
//
// if the FIN/ACK is not sent in time, the socket will be closed in Close(bool)
ShutdownSocket(SocketShutdown.Send);
}
/// <summary>
/// Occurs as the forwarded port is being stopped.
/// </summary>
private void ForwardedPort_Closing(object sender, EventArgs eventArgs)
{
// signal to the server that we will not send anything anymore; this will also interrupt the
// blocking receive in Bind if the server sends FIN/ACK in time
//
// if the FIN/ACK is not sent in time, the socket will be closed in Close(bool)
ShutdownSocket(SocketShutdown.Send);
}
/// <summary>
/// Shuts down the socket.
/// </summary>
/// <param name="how">One of the <see cref="SocketShutdown"/> values that specifies the operation that will no longer be allowed.</param>
private void ShutdownSocket(SocketShutdown how)
{
if (_socket is null)
{
return;
}
lock (_socketShutdownAndCloseLock)
{
var socket = _socket;
if (!socket.IsConnected())
{
return;
}
try
{
socket.Shutdown(how);
}
catch (SocketException ex)
{
_logger.LogInformation(ex, "Failure shutting down socket");
}
}
}
/// <summary>
/// Closes the socket, hereby interrupting the blocking receive in <see cref="Bind(IPEndPoint,IForwardedPort)"/>.
/// </summary>
private void CloseSocket()
{
if (_socket is null)
{
return;
}
lock (_socketShutdownAndCloseLock)
{
var socket = _socket;
if (socket != null)
{
_socket = null;
socket.Dispose();
}
}
}
/// <summary>
/// Closes the channel waiting for the SSH_MSG_CHANNEL_CLOSE message to be received from the server.
/// </summary>
protected override void Close()
{
var forwardedPort = _forwardedPort;
if (forwardedPort != null)
{
forwardedPort.Closing -= ForwardedPort_Closing;
_forwardedPort = null;
}
// signal to the server that we will not send anything anymore; this will also interrupt the
// blocking receive in Bind if the server sends FIN/ACK in time
//
// if the FIN/ACK is not sent in time, the socket will be closed after the channel is closed
ShutdownSocket(SocketShutdown.Send);
// close the SSH channel, and mark the channel closed
base.Close();
// close the socket
CloseSocket();
}
/// <summary>
/// Called when channel data is received.
/// </summary>
/// <param name="data">The data.</param>
protected override void OnData(ArraySegment<byte> data)
{
base.OnData(data);
var socket = _socket;
if (socket.IsConnected())
{
SocketAbstraction.Send(socket, data.Array, data.Offset, data.Count);
}
}
}
}