mirror of
https://github.com/sshnet/SSH.NET.git
synced 2026-10-29 07:07:02 +08:00
Refactor PortForwarding
Fix ChannelDirectTcpip clean up after socket is close by remote host Add ForwardedPortDynamic to support dynamic forwarding, SOCKS4 and SOCKS5 are supported.
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Renci.SshNet
|
||||
{
|
||||
public partial class ForwardedPortDynamic
|
||||
{
|
||||
partial void ExecuteThread(Action action)
|
||||
{
|
||||
ThreadPool.QueueUserWorkItem((o) => { action(); });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,84 +11,9 @@ namespace Renci.SshNet
|
||||
/// </summary>
|
||||
public partial class ForwardedPortLocal
|
||||
{
|
||||
private TcpListener _listener;
|
||||
|
||||
partial void ExecuteThread(Action action)
|
||||
{
|
||||
ThreadPool.QueueUserWorkItem((o) => { action(); });
|
||||
}
|
||||
|
||||
partial void InternalStart()
|
||||
{
|
||||
// If port already started don't start it again
|
||||
if (this.IsStarted)
|
||||
return;
|
||||
|
||||
var ep = new IPEndPoint(Dns.GetHostAddresses(this.BoundHost)[0], (int)this.BoundPort);
|
||||
|
||||
this._listener = new TcpListener(ep);
|
||||
this._listener.Start();
|
||||
|
||||
this._listenerTaskCompleted = new ManualResetEvent(false);
|
||||
this.ExecuteThread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var socket = this._listener.AcceptSocket();
|
||||
|
||||
this.ExecuteThread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
IPEndPoint originatorEndPoint = socket.RemoteEndPoint as IPEndPoint;
|
||||
|
||||
this.RaiseRequestReceived(originatorEndPoint.Address.ToString(), (uint)originatorEndPoint.Port);
|
||||
|
||||
var channel = this.Session.CreateChannel<ChannelDirectTcpip>();
|
||||
|
||||
channel.Bind(this.Host, this.Port, socket);
|
||||
}
|
||||
catch (Exception exp)
|
||||
{
|
||||
this.RaiseExceptionEvent(exp);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (SocketException exp)
|
||||
{
|
||||
if (!(exp.SocketErrorCode == SocketError.Interrupted))
|
||||
{
|
||||
this.RaiseExceptionEvent(exp);
|
||||
}
|
||||
}
|
||||
catch (Exception exp)
|
||||
{
|
||||
this.RaiseExceptionEvent(exp);
|
||||
}
|
||||
finally
|
||||
{
|
||||
this._listenerTaskCompleted.Set();
|
||||
}
|
||||
});
|
||||
|
||||
this.IsStarted = true;
|
||||
}
|
||||
|
||||
partial void InternalStop()
|
||||
{
|
||||
// If port not started you cant stop it
|
||||
if (!this.IsStarted)
|
||||
return;
|
||||
|
||||
this._listener.Stop();
|
||||
this._listenerTaskCompleted.WaitOne(this.Session.ConnectionInfo.Timeout);
|
||||
this._listenerTaskCompleted.Dispose();
|
||||
this._listenerTaskCompleted = null;
|
||||
|
||||
this.IsStarted = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,9 +217,18 @@
|
||||
<Compile Include="..\Renci.SshNet\ForwardedPort.cs">
|
||||
<Link>ForwardedPort.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\ForwardedPortDynamic.cs">
|
||||
<Link>ForwardedPortDynamic.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\ForwardedPortDynamic.NET.cs">
|
||||
<Link>ForwardedPortDynamic.NET.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\ForwardedPortLocal.cs">
|
||||
<Link>ForwardedPortLocal.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\ForwardedPortLocal.NET.cs">
|
||||
<Link>ForwardedPortLocal.NET.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\ForwardedPortRemote.cs">
|
||||
<Link>ForwardedPortRemote.cs</Link>
|
||||
</Compile>
|
||||
@@ -729,6 +738,7 @@
|
||||
</Compile>
|
||||
<Compile Include="Channels\ChannelDirectTcpip.NET35.cs" />
|
||||
<Compile Include="Common\Extensions.NET35.cs" />
|
||||
<Compile Include="ForwardedPortDynamic.NET35.cs" />
|
||||
<Compile Include="ForwardedPortLocal.NET35.cs" />
|
||||
<Compile Include="ForwardedPortRemote.NET35.cs" />
|
||||
<Compile Include="KeyboardInteractiveConnectionInfo.NET35.cs" />
|
||||
@@ -742,7 +752,7 @@
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<ProjectExtensions>
|
||||
<VisualStudio>
|
||||
<UserProperties ProjectLinkReference="2f5f8c90-0bd1-424f-997c-7bc6280919d1" ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" />
|
||||
<UserProperties ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" ProjectLinkReference="2f5f8c90-0bd1-424f-997c-7bc6280919d1" />
|
||||
</VisualStudio>
|
||||
</ProjectExtensions>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
|
||||
@@ -201,6 +201,9 @@
|
||||
<Compile Include="..\Renci.SshNet\ForwardedPort.cs">
|
||||
<Link>ForwardedPort.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\ForwardedPortDynamic.cs">
|
||||
<Link>ForwardedPortDynamic.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\ForwardedPortLocal.cs">
|
||||
<Link>ForwardedPortLocal.cs</Link>
|
||||
</Compile>
|
||||
@@ -716,7 +719,7 @@
|
||||
<FlavorProperties GUID="{A1591282-1198-4647-A2B1-27E5FF5F6F3B}">
|
||||
<SilverlightProjectProperties />
|
||||
</FlavorProperties>
|
||||
<UserProperties ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" ProjectLinkReference="2f5f8c90-0bd1-424f-997c-7bc6280919d1" />
|
||||
<UserProperties ProjectLinkReference="2f5f8c90-0bd1-424f-997c-7bc6280919d1" ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" />
|
||||
</VisualStudio>
|
||||
</ProjectExtensions>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
|
||||
@@ -12,112 +12,115 @@ namespace Renci.SshNet.Tests.SshClientTests
|
||||
/// <summary>
|
||||
/// Summary description for UnitTest1
|
||||
/// </summary>
|
||||
public partial class TestPortForwarding
|
||||
{
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(SshConnectionException))]
|
||||
public void Test_PortForwarding_Local_Without_Connecting()
|
||||
{
|
||||
using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
|
||||
{
|
||||
var port1 = client.AddForwardedPort<ForwardedPortLocal>("localhost", 8084, "www.renci.org", 80);
|
||||
port1.Exception += delegate(object sender, ExceptionEventArgs e)
|
||||
{
|
||||
Assert.Fail(e.Exception.ToString());
|
||||
};
|
||||
port1.Start();
|
||||
public partial class TestPortForwarding
|
||||
{
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(SshConnectionException))]
|
||||
public void Test_PortForwarding_Local_Without_Connecting()
|
||||
{
|
||||
using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
|
||||
{
|
||||
var port1 = new ForwardedPortLocal("localhost", 8084, "www.renci.org", 80);
|
||||
client.AddForwardedPort(port1);
|
||||
port1.Exception += delegate(object sender, ExceptionEventArgs e)
|
||||
{
|
||||
Assert.Fail(e.Exception.ToString());
|
||||
};
|
||||
port1.Start();
|
||||
|
||||
System.Threading.Tasks.Parallel.For(0, 100,
|
||||
//new ParallelOptions
|
||||
//{
|
||||
// MaxDegreeOfParallelism = 20,
|
||||
//},
|
||||
(counter) =>
|
||||
{
|
||||
var start = DateTime.Now;
|
||||
var req = HttpWebRequest.Create("http://localhost:8084");
|
||||
using (var response = req.GetResponse())
|
||||
{
|
||||
System.Threading.Tasks.Parallel.For(0, 100,
|
||||
//new ParallelOptions
|
||||
//{
|
||||
// MaxDegreeOfParallelism = 20,
|
||||
//},
|
||||
(counter) =>
|
||||
{
|
||||
var start = DateTime.Now;
|
||||
var req = HttpWebRequest.Create("http://localhost:8084");
|
||||
using (var response = req.GetResponse())
|
||||
{
|
||||
|
||||
var data = ReadStream(response.GetResponseStream());
|
||||
var end = DateTime.Now;
|
||||
var data = ReadStream(response.GetResponseStream());
|
||||
var end = DateTime.Now;
|
||||
|
||||
Debug.WriteLine(string.Format("Request# {2}: Lenght: {0} Time: {1}", data.Length, (end - start), counter));
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
Debug.WriteLine(string.Format("Request# {2}: Lenght: {0} Time: {1}", data.Length, (end - start), counter));
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Test_PortForwarding_Remote()
|
||||
{
|
||||
// ******************************************************************
|
||||
// ************* Tests are still in not finished ********************
|
||||
// ******************************************************************
|
||||
[TestMethod]
|
||||
public void Test_PortForwarding_Remote()
|
||||
{
|
||||
// ******************************************************************
|
||||
// ************* Tests are still in not finished ********************
|
||||
// ******************************************************************
|
||||
|
||||
using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
|
||||
{
|
||||
client.Connect();
|
||||
var port1 = client.AddForwardedPort<ForwardedPortRemote>(8082, "www.renci.org", 80);
|
||||
port1.Exception += delegate(object sender, ExceptionEventArgs e)
|
||||
{
|
||||
Assert.Fail(e.Exception.ToString());
|
||||
};
|
||||
port1.Start();
|
||||
var boundport = port1.BoundPort;
|
||||
using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
|
||||
{
|
||||
client.Connect();
|
||||
var port1 = new ForwardedPortRemote(8082, "www.renci.org", 80);
|
||||
client.AddForwardedPort(port1);
|
||||
port1.Exception += delegate(object sender, ExceptionEventArgs e)
|
||||
{
|
||||
Assert.Fail(e.Exception.ToString());
|
||||
};
|
||||
port1.Start();
|
||||
var boundport = port1.BoundPort;
|
||||
|
||||
System.Threading.Tasks.Parallel.For(0, 5,
|
||||
//new ParallelOptions
|
||||
//{
|
||||
// MaxDegreeOfParallelism = 1,
|
||||
//},
|
||||
(counter) =>
|
||||
{
|
||||
var cmd = client.CreateCommand(string.Format("wget -O- http://localhost:{0}", boundport));
|
||||
var result = cmd.Execute();
|
||||
var end = DateTime.Now;
|
||||
Debug.WriteLine(string.Format("Length: {0}", result.Length));
|
||||
}
|
||||
);
|
||||
Thread.Sleep(1000 * 100);
|
||||
port1.Stop();
|
||||
}
|
||||
}
|
||||
System.Threading.Tasks.Parallel.For(0, 5,
|
||||
//new ParallelOptions
|
||||
//{
|
||||
// MaxDegreeOfParallelism = 1,
|
||||
//},
|
||||
(counter) =>
|
||||
{
|
||||
var cmd = client.CreateCommand(string.Format("wget -O- http://localhost:{0}", boundport));
|
||||
var result = cmd.Execute();
|
||||
var end = DateTime.Now;
|
||||
Debug.WriteLine(string.Format("Length: {0}", result.Length));
|
||||
}
|
||||
);
|
||||
Thread.Sleep(1000 * 100);
|
||||
port1.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Test_PortForwarding_Local()
|
||||
{
|
||||
using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
|
||||
{
|
||||
client.Connect();
|
||||
var port1 = client.AddForwardedPort<ForwardedPortLocal>("localhost", 8084, "www.renci.org", 80);
|
||||
port1.Exception += delegate(object sender, ExceptionEventArgs e)
|
||||
{
|
||||
Assert.Fail(e.Exception.ToString());
|
||||
};
|
||||
port1.Start();
|
||||
[TestMethod]
|
||||
public void Test_PortForwarding_Local()
|
||||
{
|
||||
using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
|
||||
{
|
||||
client.Connect();
|
||||
var port1 = new ForwardedPortLocal("localhost", 8084, "www.renci.org", 80);
|
||||
client.AddForwardedPort(port1);
|
||||
port1.Exception += delegate(object sender, ExceptionEventArgs e)
|
||||
{
|
||||
Assert.Fail(e.Exception.ToString());
|
||||
};
|
||||
port1.Start();
|
||||
|
||||
System.Threading.Tasks.Parallel.For(0, 100,
|
||||
//new ParallelOptions
|
||||
//{
|
||||
// MaxDegreeOfParallelism = 20,
|
||||
//},
|
||||
(counter) =>
|
||||
{
|
||||
var start = DateTime.Now;
|
||||
var req = HttpWebRequest.Create("http://localhost:8084");
|
||||
using (var response = req.GetResponse())
|
||||
{
|
||||
System.Threading.Tasks.Parallel.For(0, 100,
|
||||
//new ParallelOptions
|
||||
//{
|
||||
// MaxDegreeOfParallelism = 20,
|
||||
//},
|
||||
(counter) =>
|
||||
{
|
||||
var start = DateTime.Now;
|
||||
var req = HttpWebRequest.Create("http://localhost:8084");
|
||||
using (var response = req.GetResponse())
|
||||
{
|
||||
|
||||
var data = ReadStream(response.GetResponseStream());
|
||||
var end = DateTime.Now;
|
||||
var data = ReadStream(response.GetResponseStream());
|
||||
var end = DateTime.Now;
|
||||
|
||||
Debug.WriteLine(string.Format("Request# {2}: Length: {0} Time: {1}", data.Length, (end - start), counter));
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Debug.WriteLine(string.Format("Request# {2}: Length: {0} Time: {1}", data.Length, (end - start), counter));
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,80 +15,81 @@ namespace Renci.SshNet.Tests.SshClientTests
|
||||
[TestClass]
|
||||
public partial class TestPortForwarding
|
||||
{
|
||||
[TestMethod]
|
||||
[WorkItem(713)]
|
||||
[Owner("kenneth_aa")]
|
||||
[Description("Test if calling Stop on ForwardedPortLocal instance causes wait.")]
|
||||
public void Test_PortForwarding_Local_Stop_Hangs_On_Wait()
|
||||
{
|
||||
using (var client = new SshClient(Resources.HOST, Int32.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD))
|
||||
{
|
||||
client.Connect();
|
||||
[TestMethod]
|
||||
[WorkItem(713)]
|
||||
[Owner("kenneth_aa")]
|
||||
[Description("Test if calling Stop on ForwardedPortLocal instance causes wait.")]
|
||||
public void Test_PortForwarding_Local_Stop_Hangs_On_Wait()
|
||||
{
|
||||
using (var client = new SshClient(Resources.HOST, Int32.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD))
|
||||
{
|
||||
client.Connect();
|
||||
|
||||
var port1 = client.AddForwardedPort<ForwardedPortLocal>("localhost", 8084, "www.google.com",80);
|
||||
var port1 = new ForwardedPortLocal("localhost", 8084, "www.google.com", 80);
|
||||
client.AddForwardedPort(port1);
|
||||
port1.Exception += delegate(object sender, ExceptionEventArgs e)
|
||||
{
|
||||
Assert.Fail(e.Exception.ToString());
|
||||
};
|
||||
{
|
||||
Assert.Fail(e.Exception.ToString());
|
||||
};
|
||||
|
||||
port1.Start();
|
||||
port1.Start();
|
||||
|
||||
bool hasTestedTunnel = false;
|
||||
System.Threading.ThreadPool.QueueUserWorkItem(delegate(object state)
|
||||
{
|
||||
try
|
||||
{
|
||||
var url = "http://www.google.com/";
|
||||
Debug.WriteLine("Starting web request to \"" + url + "\"");
|
||||
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
|
||||
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
|
||||
bool hasTestedTunnel = false;
|
||||
System.Threading.ThreadPool.QueueUserWorkItem(delegate(object state)
|
||||
{
|
||||
try
|
||||
{
|
||||
var url = "http://www.google.com/";
|
||||
Debug.WriteLine("Starting web request to \"" + url + "\"");
|
||||
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
|
||||
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
|
||||
|
||||
Assert.IsNotNull(response);
|
||||
Assert.IsNotNull(response);
|
||||
|
||||
Debug.WriteLine("Http Response status code: " + response.StatusCode.ToString());
|
||||
Debug.WriteLine("Http Response status code: " + response.StatusCode.ToString());
|
||||
|
||||
response.Close();
|
||||
response.Close();
|
||||
|
||||
hasTestedTunnel = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Assert.Fail(ex.ToString());
|
||||
}
|
||||
hasTestedTunnel = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Assert.Fail(ex.ToString());
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
// Wait for the web request to complete.
|
||||
while(!hasTestedTunnel)
|
||||
{
|
||||
System.Threading.Thread.Sleep(1000);
|
||||
}
|
||||
// Wait for the web request to complete.
|
||||
while(!hasTestedTunnel)
|
||||
{
|
||||
System.Threading.Thread.Sleep(1000);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Try stop the port forwarding, wait 3 seconds and fail if it is still started.
|
||||
System.Threading.ThreadPool.QueueUserWorkItem(delegate(object state)
|
||||
{
|
||||
Debug.WriteLine("Trying to stop port forward.");
|
||||
port1.Stop();
|
||||
Debug.WriteLine("Port forwarding stopped.");
|
||||
try
|
||||
{
|
||||
// Try stop the port forwarding, wait 3 seconds and fail if it is still started.
|
||||
System.Threading.ThreadPool.QueueUserWorkItem(delegate(object state)
|
||||
{
|
||||
Debug.WriteLine("Trying to stop port forward.");
|
||||
port1.Stop();
|
||||
Debug.WriteLine("Port forwarding stopped.");
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
System.Threading.Thread.Sleep(3000);
|
||||
if (port1.IsStarted)
|
||||
{
|
||||
Assert.Fail("Port forwarding not stopped.");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Assert.Fail(ex.ToString());
|
||||
}
|
||||
client.Disconnect();
|
||||
Debug.WriteLine("Success.");
|
||||
}
|
||||
}
|
||||
System.Threading.Thread.Sleep(3000);
|
||||
if (port1.IsStarted)
|
||||
{
|
||||
Assert.Fail("Port forwarding not stopped.");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Assert.Fail(ex.ToString());
|
||||
}
|
||||
client.Disconnect();
|
||||
Debug.WriteLine("Success.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -101,8 +102,9 @@ namespace Renci.SshNet.Tests.SshClientTests
|
||||
using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
|
||||
{
|
||||
client.Connect();
|
||||
var port1 = client.AddForwardedPort<ForwardedPortLocal>(null, 8080 , null, 80);
|
||||
client.Disconnect();
|
||||
var port1 = new ForwardedPortLocal(null, 8080, null, 80);
|
||||
client.AddForwardedPort(port1);
|
||||
client.Disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,8 +116,9 @@ namespace Renci.SshNet.Tests.SshClientTests
|
||||
using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
|
||||
{
|
||||
client.Connect();
|
||||
var port1 = client.AddForwardedPort<ForwardedPortRemote>(null, 8080, null, 80);
|
||||
client.Disconnect();
|
||||
var port1 = new ForwardedPortRemote(null, 8080, null, 80);
|
||||
client.AddForwardedPort(port1);
|
||||
client.Disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,8 +130,9 @@ namespace Renci.SshNet.Tests.SshClientTests
|
||||
using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
|
||||
{
|
||||
client.Connect();
|
||||
var port1 = client.AddForwardedPort<ForwardedPortRemote>(string.Empty, 8080, string.Empty, 80);
|
||||
client.Disconnect();
|
||||
var port1 = new ForwardedPortRemote(string.Empty, 8080, string.Empty, 80);
|
||||
client.AddForwardedPort(port1);
|
||||
client.Disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,8 +144,9 @@ namespace Renci.SshNet.Tests.SshClientTests
|
||||
using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
|
||||
{
|
||||
client.Connect();
|
||||
var port1 = client.AddForwardedPort<ForwardedPortLocal>(string.Empty, 8080, string.Empty, 80);
|
||||
client.Disconnect();
|
||||
var port1 = new ForwardedPortLocal(string.Empty, 8080, string.Empty, 80);
|
||||
client.AddForwardedPort(port1);
|
||||
client.Disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,8 +158,9 @@ namespace Renci.SshNet.Tests.SshClientTests
|
||||
using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
|
||||
{
|
||||
client.Connect();
|
||||
var port1 = client.AddForwardedPort<ForwardedPortRemote>("localhost", IPEndPoint.MaxPort+1, "www.renci.org", IPEndPoint.MaxPort+1);
|
||||
client.Disconnect();
|
||||
var port1 = new ForwardedPortRemote("localhost", IPEndPoint.MaxPort + 1, "www.renci.org", IPEndPoint.MaxPort + 1);
|
||||
client.AddForwardedPort(port1);
|
||||
client.Disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -197,6 +197,9 @@
|
||||
<Compile Include="..\Renci.SshNet\ForwardedPort.cs">
|
||||
<Link>ForwardedPort.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\ForwardedPortDynamic.cs">
|
||||
<Link>ForwardedPortDynamic.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\ForwardedPortLocal.cs">
|
||||
<Link>ForwardedPortLocal.cs</Link>
|
||||
</Compile>
|
||||
@@ -720,7 +723,7 @@
|
||||
<Import Project="$(MSBuildExtensionsPath)\Microsoft\Silverlight for Phone\$(TargetFrameworkVersion)\Microsoft.Silverlight.CSharp.targets" />
|
||||
<ProjectExtensions>
|
||||
<VisualStudio>
|
||||
<UserProperties ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" ProjectLinkReference="2f5f8c90-0bd1-424f-997c-7bc6280919d1" />
|
||||
<UserProperties ProjectLinkReference="2f5f8c90-0bd1-424f-997c-7bc6280919d1" ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" />
|
||||
</VisualStudio>
|
||||
</ProjectExtensions>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
|
||||
@@ -41,18 +41,12 @@ namespace Renci.SshNet.Channels
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Binds channel to specified remote host.
|
||||
/// </summary>
|
||||
/// <param name="remoteHost">The remote host.</param>
|
||||
/// <param name="port">The port.</param>
|
||||
/// <param name="socket">The socket.</param>
|
||||
public void Bind(string remoteHost, uint port, Socket socket)
|
||||
public void Open(string remoteHost, uint port, Socket socket)
|
||||
{
|
||||
this._socket = socket;
|
||||
|
||||
IPEndPoint ep = socket.RemoteEndPoint as IPEndPoint;
|
||||
|
||||
|
||||
|
||||
if (!this.IsConnected)
|
||||
{
|
||||
@@ -65,10 +59,21 @@ namespace Renci.SshNet.Channels
|
||||
|
||||
// Wait for channel to open
|
||||
this.WaitHandle(this._channelOpen);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Binds channel to specified remote host.
|
||||
/// </summary>
|
||||
/// <param name="remoteHost">The remote host.</param>
|
||||
/// <param name="port">The port.</param>
|
||||
/// <param name="socket">The socket.</param>
|
||||
public void Bind()
|
||||
{
|
||||
// Cannot bind if channel is not open
|
||||
if (!this.IsOpen)
|
||||
return;
|
||||
|
||||
// Start reading data from the port and send to channel
|
||||
EventWaitHandle readerTaskError = new AutoResetEvent(false);
|
||||
|
||||
var readerTaskCompleted = new ManualResetEvent(false);
|
||||
Exception exception = null;
|
||||
|
||||
@@ -103,7 +108,7 @@ namespace Renci.SshNet.Channels
|
||||
// socket buffer is probably empty, wait and try again
|
||||
Thread.Sleep(30);
|
||||
}
|
||||
else if (exp.SocketErrorCode == SocketError.ConnectionAborted)
|
||||
else if (exp.SocketErrorCode == SocketError.ConnectionAborted || exp.SocketErrorCode == SocketError.ConnectionReset)
|
||||
{
|
||||
break;
|
||||
}
|
||||
@@ -114,7 +119,6 @@ namespace Renci.SshNet.Channels
|
||||
}
|
||||
catch (Exception exp)
|
||||
{
|
||||
readerTaskError.Set();
|
||||
exception = exp;
|
||||
}
|
||||
finally
|
||||
@@ -125,14 +129,12 @@ namespace Renci.SshNet.Channels
|
||||
|
||||
// Channel was open and we MUST receive EOF notification,
|
||||
// data transfer can take longer then connection specified timeout
|
||||
System.Threading.WaitHandle.WaitAny(new WaitHandle[] { this._channelEof, readerTaskError });
|
||||
// If listener thread is finished then socket was closed
|
||||
System.Threading.WaitHandle.WaitAny(new WaitHandle[] { this._channelEof, readerTaskCompleted });
|
||||
|
||||
this._socket.Dispose();
|
||||
this._socket = null;
|
||||
|
||||
// Wait for task to finish and will throw any errors if any
|
||||
readerTaskCompleted.WaitOne();
|
||||
|
||||
if (exception != null)
|
||||
throw exception;
|
||||
}
|
||||
@@ -169,6 +171,13 @@ namespace Renci.SshNet.Channels
|
||||
this._channelOpen.Set();
|
||||
}
|
||||
|
||||
protected override void OnOpenFailure(uint reasonCode, string description, string language)
|
||||
{
|
||||
base.OnOpenFailure(reasonCode, description, language);
|
||||
|
||||
this._channelOpen.Set();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when channel has no more data to receive.
|
||||
/// </summary>
|
||||
@@ -179,6 +188,13 @@ namespace Renci.SshNet.Channels
|
||||
this._channelEof.Set();
|
||||
}
|
||||
|
||||
protected override void OnClose()
|
||||
{
|
||||
base.OnClose();
|
||||
|
||||
this._channelEof.Set();
|
||||
}
|
||||
|
||||
partial void ExecuteThread(Action action);
|
||||
|
||||
partial void InternalSocketReceive(byte[] buffer, ref int read);
|
||||
|
||||
@@ -16,26 +16,6 @@ namespace Renci.SshNet
|
||||
/// </value>
|
||||
internal Session Session { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bound host.
|
||||
/// </summary>
|
||||
public string BoundHost { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bound port.
|
||||
/// </summary>
|
||||
public uint BoundPort { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the forwarded host.
|
||||
/// </summary>
|
||||
public string Host { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the forwarded port.
|
||||
/// </summary>
|
||||
public uint Port { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether port forwarding started.
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,277 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
using Renci.SshNet.Channels;
|
||||
using Renci.SshNet.Common;
|
||||
|
||||
namespace Renci.SshNet
|
||||
{
|
||||
public partial class ForwardedPortDynamic
|
||||
{
|
||||
private TcpListener _listener;
|
||||
|
||||
partial void InternalStart()
|
||||
{
|
||||
// If port already started don't start it again
|
||||
if (this.IsStarted)
|
||||
return;
|
||||
|
||||
var ip = IPAddress.Any;
|
||||
if (!string.IsNullOrEmpty(this.BoundHost))
|
||||
{
|
||||
ip = Dns.GetHostAddresses(this.BoundHost)[0];
|
||||
}
|
||||
|
||||
var ep = new IPEndPoint(ip, (int)this.BoundPort);
|
||||
|
||||
this._listener = new TcpListener(ep);
|
||||
this._listener.Start();
|
||||
|
||||
this._listenerTaskCompleted = new ManualResetEvent(false);
|
||||
this.ExecuteThread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var socket = this._listener.AcceptSocket();
|
||||
|
||||
this.ExecuteThread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var channel = this.Session.CreateChannel<ChannelDirectTcpip>())
|
||||
{
|
||||
var version = new byte[1];
|
||||
|
||||
socket.Receive(version);
|
||||
|
||||
if (version[0] == 4)
|
||||
{
|
||||
this.HandleSocks4(socket, channel);
|
||||
}
|
||||
else if (version[0] == 5)
|
||||
{
|
||||
this.HandleSocks5(socket, channel);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException(string.Format("SOCKS version {0} is not supported.", version));
|
||||
}
|
||||
|
||||
channel.Bind();
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception exp)
|
||||
{
|
||||
this.RaiseExceptionEvent(exp);
|
||||
}
|
||||
finally
|
||||
{
|
||||
socket.Close();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (SocketException exp)
|
||||
{
|
||||
if (!(exp.SocketErrorCode == SocketError.Interrupted))
|
||||
{
|
||||
this.RaiseExceptionEvent(exp);
|
||||
}
|
||||
}
|
||||
catch (Exception exp)
|
||||
{
|
||||
this.RaiseExceptionEvent(exp);
|
||||
}
|
||||
finally
|
||||
{
|
||||
this._listenerTaskCompleted.Set();
|
||||
}
|
||||
});
|
||||
|
||||
this.IsStarted = true;
|
||||
}
|
||||
|
||||
partial void InternalStop()
|
||||
{
|
||||
// If port not started you cant stop it
|
||||
if (!this.IsStarted)
|
||||
return;
|
||||
|
||||
this._listener.Stop();
|
||||
this._listenerTaskCompleted.WaitOne(this.Session.ConnectionInfo.Timeout);
|
||||
this._listenerTaskCompleted.Dispose();
|
||||
this._listenerTaskCompleted = null;
|
||||
|
||||
this.IsStarted = false;
|
||||
}
|
||||
|
||||
private void HandleSocks4(Socket socket, ChannelDirectTcpip channel)
|
||||
{
|
||||
using (var stream = new NetworkStream(socket))
|
||||
{
|
||||
var commandCode = stream.ReadByte();
|
||||
// TODO: See what need to be done depends on the code
|
||||
|
||||
var portBuffer = new byte[2];
|
||||
stream.Read(portBuffer, 0, portBuffer.Length);
|
||||
var port = (uint)(portBuffer[0] * 256 + portBuffer[1]);
|
||||
|
||||
var ipBuffer = new byte[4];
|
||||
stream.Read(ipBuffer, 0, ipBuffer.Length);
|
||||
var ipAddress = new IPAddress(ipBuffer);
|
||||
|
||||
var username = ReadString(stream);
|
||||
|
||||
var host = ipAddress.ToString();
|
||||
|
||||
this.RaiseRequestReceived(host, port);
|
||||
|
||||
channel.Open(host, port, socket);
|
||||
|
||||
stream.WriteByte(0x00);
|
||||
|
||||
if (channel.IsOpen)
|
||||
{
|
||||
stream.WriteByte(0x5a);
|
||||
}
|
||||
else
|
||||
{
|
||||
stream.WriteByte(0x5b);
|
||||
}
|
||||
|
||||
stream.Write(portBuffer, 0, portBuffer.Length);
|
||||
stream.Write(ipBuffer, 0, ipBuffer.Length);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleSocks5(Socket socket, ChannelDirectTcpip channel)
|
||||
{
|
||||
using (var stream = new NetworkStream(socket))
|
||||
{
|
||||
var authenticationMethodsCount = stream.ReadByte();
|
||||
|
||||
var authenticationMethods = new byte[authenticationMethodsCount];
|
||||
stream.Read(authenticationMethods, 0, authenticationMethods.Length);
|
||||
|
||||
stream.WriteByte(0x05);
|
||||
|
||||
if (authenticationMethods.Min() == 0)
|
||||
{
|
||||
stream.WriteByte(0x00);
|
||||
}
|
||||
else
|
||||
{
|
||||
stream.WriteByte(0xFF);
|
||||
}
|
||||
|
||||
var version = stream.ReadByte();
|
||||
|
||||
if (version != 5)
|
||||
throw new ProxyException("SOCKS5: Version 5 is expected.");
|
||||
|
||||
var commandCode = stream.ReadByte();
|
||||
|
||||
if (stream.ReadByte() != 0)
|
||||
{
|
||||
throw new ProxyException("SOCKS5: 0 is expected.");
|
||||
}
|
||||
|
||||
var addressType = stream.ReadByte();
|
||||
|
||||
IPAddress ipAddress = null;
|
||||
byte[] addressBuffer = null;
|
||||
switch (addressType)
|
||||
{
|
||||
case 0x01:
|
||||
{
|
||||
addressBuffer = new byte[4];
|
||||
stream.Read(addressBuffer, 0, 4);
|
||||
|
||||
ipAddress = new IPAddress(addressBuffer);
|
||||
}
|
||||
break;
|
||||
case 0x03:
|
||||
{
|
||||
var length = stream.ReadByte();
|
||||
addressBuffer = new byte[length];
|
||||
stream.Read(addressBuffer, 0, addressBuffer.Length);
|
||||
|
||||
ipAddress = IPAddress.Parse(new Renci.SshNet.Common.ASCIIEncoding().GetString(addressBuffer));
|
||||
}
|
||||
break;
|
||||
case 0x04:
|
||||
{
|
||||
addressBuffer = new byte[16];
|
||||
stream.Read(addressBuffer, 0, 16);
|
||||
|
||||
ipAddress = new IPAddress(addressBuffer);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new ProxyException(string.Format("SOCKS5: Address type '{0}' is not supported.", addressType));
|
||||
}
|
||||
|
||||
var portBuffer = new byte[2];
|
||||
stream.Read(portBuffer, 0, portBuffer.Length);
|
||||
var port = (uint)(portBuffer[0] * 256 + portBuffer[1]);
|
||||
var host = ipAddress.ToString();
|
||||
|
||||
this.RaiseRequestReceived(host, port);
|
||||
|
||||
channel.Open(host, port, socket);
|
||||
|
||||
stream.WriteByte(0x05);
|
||||
|
||||
if (channel.IsOpen)
|
||||
{
|
||||
stream.WriteByte(0x00);
|
||||
}
|
||||
else
|
||||
{
|
||||
stream.WriteByte(0x01);
|
||||
}
|
||||
|
||||
stream.WriteByte(0x00);
|
||||
|
||||
var buffer = ipAddress.GetAddressBytes();
|
||||
|
||||
if (ipAddress.AddressFamily == AddressFamily.InterNetwork)
|
||||
{
|
||||
stream.WriteByte(0x01);
|
||||
}
|
||||
else if (ipAddress.AddressFamily == AddressFamily.InterNetwork)
|
||||
{
|
||||
stream.WriteByte(0x04);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException("Not supported address family.");
|
||||
}
|
||||
|
||||
stream.Write(buffer, 0, buffer.Length);
|
||||
stream.Write(portBuffer, 0, portBuffer.Length);
|
||||
}
|
||||
}
|
||||
|
||||
private static string ReadString(NetworkStream stream)
|
||||
{
|
||||
StringBuilder text = new StringBuilder();
|
||||
var aa = (char)stream.ReadByte();
|
||||
while (aa != 0)
|
||||
{
|
||||
text.Append(aa);
|
||||
aa = (char)stream.ReadByte();
|
||||
}
|
||||
return text.ToString();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Renci.SshNet
|
||||
{
|
||||
public partial class ForwardedPortDynamic
|
||||
{
|
||||
partial void ExecuteThread(Action action)
|
||||
{
|
||||
Task.Factory.StartNew(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace Renci.SshNet
|
||||
{
|
||||
public partial class ForwardedPortDynamic : ForwardedPort
|
||||
{
|
||||
private EventWaitHandle _listenerTaskCompleted;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bound host.
|
||||
/// </summary>
|
||||
public string BoundHost { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bound port.
|
||||
/// </summary>
|
||||
public uint BoundPort { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ForwardedPortDynamic"/> class.
|
||||
/// </summary>
|
||||
/// <param name="port">The port.</param>
|
||||
public ForwardedPortDynamic(uint port)
|
||||
: this(string.Empty, port)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ForwardedPortDynamic"/> class.
|
||||
/// </summary>
|
||||
/// <param name="host">The host.</param>
|
||||
/// <param name="port">The port.</param>
|
||||
public ForwardedPortDynamic(string host, uint port)
|
||||
{
|
||||
this.BoundHost = host;
|
||||
this.BoundPort = port;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts local port forwarding.
|
||||
/// </summary>
|
||||
public override void Start()
|
||||
{
|
||||
this.InternalStart();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops local port forwarding.
|
||||
/// </summary>
|
||||
public override void Stop()
|
||||
{
|
||||
base.Stop();
|
||||
|
||||
this.InternalStop();
|
||||
}
|
||||
|
||||
partial void InternalStart();
|
||||
|
||||
partial void InternalStop();
|
||||
|
||||
partial void ExecuteThread(Action action);
|
||||
|
||||
#region IDisposable Members
|
||||
|
||||
private bool _isDisposed = false;
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <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 ResourceMessages.</param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
// Check to see if Dispose has already been called.
|
||||
if (!this._isDisposed)
|
||||
{
|
||||
// If disposing equals true, dispose all managed
|
||||
// and unmanaged ResourceMessages.
|
||||
if (disposing)
|
||||
{
|
||||
// Dispose managed ResourceMessages.
|
||||
if (this._listenerTaskCompleted != null)
|
||||
{
|
||||
this._listenerTaskCompleted.Dispose();
|
||||
this._listenerTaskCompleted = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Note disposing has been done.
|
||||
_isDisposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases unmanaged resources and performs other cleanup operations before the
|
||||
/// <see cref="ForwardedPortLocal"/> is reclaimed by garbage collection.
|
||||
/// </summary>
|
||||
~ForwardedPortDynamic()
|
||||
{
|
||||
// Do not re-create Dispose clean-up code here.
|
||||
// Calling Dispose(false) is optimal in terms of
|
||||
// readability and maintainability.
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
using System;
|
||||
using System.Net.Sockets;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using Renci.SshNet.Channels;
|
||||
|
||||
namespace Renci.SshNet
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides functionality for local port forwarding
|
||||
/// </summary>
|
||||
public partial class ForwardedPortLocal
|
||||
{
|
||||
private TcpListener _listener;
|
||||
|
||||
partial void InternalStart()
|
||||
{
|
||||
// If port already started don't start it again
|
||||
if (this.IsStarted)
|
||||
return;
|
||||
|
||||
var ep = new IPEndPoint(Dns.GetHostAddresses(this.BoundHost)[0], (int)this.BoundPort);
|
||||
|
||||
this._listener = new TcpListener(ep);
|
||||
this._listener.Start();
|
||||
|
||||
this._listenerTaskCompleted = new ManualResetEvent(false);
|
||||
this.ExecuteThread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var socket = this._listener.AcceptSocket();
|
||||
|
||||
this.ExecuteThread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
IPEndPoint originatorEndPoint = socket.RemoteEndPoint as IPEndPoint;
|
||||
|
||||
this.RaiseRequestReceived(originatorEndPoint.Address.ToString(), (uint)originatorEndPoint.Port);
|
||||
|
||||
var channel = this.Session.CreateChannel<ChannelDirectTcpip>();
|
||||
|
||||
channel.Open(this.Host, this.Port, socket);
|
||||
|
||||
channel.Bind();
|
||||
}
|
||||
catch (Exception exp)
|
||||
{
|
||||
this.RaiseExceptionEvent(exp);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (SocketException exp)
|
||||
{
|
||||
if (!(exp.SocketErrorCode == SocketError.Interrupted))
|
||||
{
|
||||
this.RaiseExceptionEvent(exp);
|
||||
}
|
||||
}
|
||||
catch (Exception exp)
|
||||
{
|
||||
this.RaiseExceptionEvent(exp);
|
||||
}
|
||||
finally
|
||||
{
|
||||
this._listenerTaskCompleted.Set();
|
||||
}
|
||||
});
|
||||
|
||||
this.IsStarted = true;
|
||||
}
|
||||
|
||||
partial void InternalStop()
|
||||
{
|
||||
// If port not started you cant stop it
|
||||
if (!this.IsStarted)
|
||||
return;
|
||||
|
||||
this._listener.Stop();
|
||||
this._listenerTaskCompleted.WaitOne(this.Session.ConnectionInfo.Timeout);
|
||||
this._listenerTaskCompleted.Dispose();
|
||||
this._listenerTaskCompleted = null;
|
||||
|
||||
this.IsStarted = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,5 @@
|
||||
using System.Threading.Tasks;
|
||||
using System;
|
||||
using System.Net.Sockets;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using Renci.SshNet.Channels;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Renci.SshNet
|
||||
{
|
||||
@@ -12,84 +8,9 @@ namespace Renci.SshNet
|
||||
/// </summary>
|
||||
public partial class ForwardedPortLocal
|
||||
{
|
||||
private TcpListener _listener;
|
||||
|
||||
partial void ExecuteThread(Action action)
|
||||
{
|
||||
Task.Factory.StartNew(action);
|
||||
}
|
||||
|
||||
partial void InternalStart()
|
||||
{
|
||||
// If port already started don't start it again
|
||||
if (this.IsStarted)
|
||||
return;
|
||||
|
||||
var ep = new IPEndPoint(Dns.GetHostAddresses(this.BoundHost)[0], (int)this.BoundPort);
|
||||
|
||||
this._listener = new TcpListener(ep);
|
||||
this._listener.Start();
|
||||
|
||||
this._listenerTaskCompleted = new ManualResetEvent(false);
|
||||
this.ExecuteThread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var socket = this._listener.AcceptSocket();
|
||||
|
||||
this.ExecuteThread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
IPEndPoint originatorEndPoint = socket.RemoteEndPoint as IPEndPoint;
|
||||
|
||||
this.RaiseRequestReceived(originatorEndPoint.Address.ToString(), (uint)originatorEndPoint.Port);
|
||||
|
||||
var channel = this.Session.CreateChannel<ChannelDirectTcpip>();
|
||||
|
||||
channel.Bind(this.Host, this.Port, socket);
|
||||
}
|
||||
catch (Exception exp)
|
||||
{
|
||||
this.RaiseExceptionEvent(exp);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (SocketException exp)
|
||||
{
|
||||
if (!(exp.SocketErrorCode == SocketError.Interrupted))
|
||||
{
|
||||
this.RaiseExceptionEvent(exp);
|
||||
}
|
||||
}
|
||||
catch (Exception exp)
|
||||
{
|
||||
this.RaiseExceptionEvent(exp);
|
||||
}
|
||||
finally
|
||||
{
|
||||
this._listenerTaskCompleted.Set();
|
||||
}
|
||||
});
|
||||
|
||||
this.IsStarted = true;
|
||||
}
|
||||
|
||||
partial void InternalStop()
|
||||
{
|
||||
// If port not started you cant stop it
|
||||
if (!this.IsStarted)
|
||||
return;
|
||||
|
||||
this._listener.Stop();
|
||||
this._listenerTaskCompleted.WaitOne(this.Session.ConnectionInfo.Timeout);
|
||||
this._listenerTaskCompleted.Dispose();
|
||||
this._listenerTaskCompleted = null;
|
||||
|
||||
this.IsStarted = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,71 @@ namespace Renci.SshNet
|
||||
{
|
||||
private EventWaitHandle _listenerTaskCompleted;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bound host.
|
||||
/// </summary>
|
||||
public string BoundHost { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bound port.
|
||||
/// </summary>
|
||||
public uint BoundPort { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the forwarded host.
|
||||
/// </summary>
|
||||
public string Host { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the forwarded port.
|
||||
/// </summary>
|
||||
public uint Port { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ForwardedPortLocal"/> class.
|
||||
/// </summary>
|
||||
/// <param name="boundPort">The bound port.</param>
|
||||
/// <param name="host">The host.</param>
|
||||
/// <param name="port">The port.</param>
|
||||
public ForwardedPortLocal(uint boundPort, string host, uint port)
|
||||
: this(string.Empty, boundPort, host, port)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ForwardedPortLocal"/> class.
|
||||
/// </summary>
|
||||
/// <param name="boundHost">The bound host.</param>
|
||||
/// <param name="boundPort">The bound port.</param>
|
||||
/// <param name="host">The host.</param>
|
||||
/// <param name="port">The port.</param>
|
||||
public ForwardedPortLocal(string boundHost, uint boundPort, string host, uint port)
|
||||
{
|
||||
if (boundHost == null)
|
||||
throw new ArgumentNullException("boundHost");
|
||||
|
||||
if (host == null)
|
||||
throw new ArgumentNullException("host");
|
||||
|
||||
if (!boundHost.IsValidHost())
|
||||
throw new ArgumentException("boundHost");
|
||||
|
||||
if (!boundPort.IsValidPort())
|
||||
throw new ArgumentOutOfRangeException("boundPort");
|
||||
|
||||
if (!host.IsValidHost())
|
||||
throw new ArgumentException("host");
|
||||
|
||||
if (!port.IsValidPort())
|
||||
throw new ArgumentOutOfRangeException("port");
|
||||
|
||||
this.BoundHost = boundHost;
|
||||
this.BoundPort = boundPort;
|
||||
this.Host = host;
|
||||
this.Port = port;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Starts local port forwarding.
|
||||
/// </summary>
|
||||
|
||||
@@ -17,6 +17,65 @@ namespace Renci.SshNet
|
||||
|
||||
private EventWaitHandle _globalRequestResponse = new AutoResetEvent(false);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bound host.
|
||||
/// </summary>
|
||||
public string BoundHost { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bound port.
|
||||
/// </summary>
|
||||
public uint BoundPort { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the forwarded host.
|
||||
/// </summary>
|
||||
public string Host { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the forwarded port.
|
||||
/// </summary>
|
||||
public uint Port { get; protected set; }
|
||||
|
||||
public ForwardedPortRemote(uint boundPort, string host, uint port)
|
||||
: this(string.Empty, boundPort, host, port)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ForwardedPortRemote"/> class.
|
||||
/// </summary>
|
||||
/// <param name="boundHost">The bound host.</param>
|
||||
/// <param name="boundPort">The bound port.</param>
|
||||
/// <param name="host">The host.</param>
|
||||
/// <param name="port">The port.</param>
|
||||
public ForwardedPortRemote(string boundHost, uint boundPort, string host, uint port)
|
||||
{
|
||||
if (boundHost == null)
|
||||
throw new ArgumentNullException("boundHost");
|
||||
|
||||
if (host == null)
|
||||
throw new ArgumentNullException("host");
|
||||
|
||||
if (!boundHost.IsValidHost())
|
||||
throw new ArgumentException("boundHost");
|
||||
|
||||
if (!boundPort.IsValidPort())
|
||||
throw new ArgumentOutOfRangeException("boundPort");
|
||||
|
||||
if (!host.IsValidHost())
|
||||
throw new ArgumentException("host");
|
||||
|
||||
if (!port.IsValidPort())
|
||||
throw new ArgumentOutOfRangeException("port");
|
||||
|
||||
this.BoundHost = boundHost;
|
||||
this.BoundPort = boundPort;
|
||||
this.Host = host;
|
||||
this.Port = port;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Starts remote port forwarding.
|
||||
/// </summary>
|
||||
|
||||
@@ -129,6 +129,14 @@
|
||||
<Compile Include="ConnectionInfo.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="ForwardedPortDynamic.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="ForwardedPortDynamic.NET.cs" />
|
||||
<Compile Include="ForwardedPortDynamic.NET40.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="ForwardedPortLocal.NET.cs" />
|
||||
<Compile Include="ProxyTypes.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
|
||||
@@ -120,71 +120,20 @@ namespace Renci.SshNet
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds forwarded port to the list.
|
||||
/// Adds the forwarded port.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of forwarded port to add</typeparam>
|
||||
/// <param name="boundHost">The bound host.</param>
|
||||
/// <param name="boundPort">The bound port.</param>
|
||||
/// <param name="connectedHost">The connected host.</param>
|
||||
/// <param name="connectedPort">The connected port.</param>
|
||||
/// <returns>
|
||||
/// Forwarded port
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="boundHost"/> or <paramref name="connectedHost"/> is null.</exception>
|
||||
/// <exception cref="ArgumentException"><paramref name="boundHost"/> or <paramref name="connectedHost"/> is invalid.</exception>
|
||||
/// <exception cref="ArgumentOutOfRangeException"><paramref name="boundPort"/> or <paramref name="connectedPort"/> is not within <see cref="System.Net.IPEndPoint.MinPort"/> and <see cref="System.Net.IPEndPoint.MaxPort"/>.</exception>
|
||||
/// <exception cref="Renci.SshNet.Common.SshConnectionException">Client is not connected.</exception>
|
||||
public T AddForwardedPort<T>(string boundHost, uint boundPort, string connectedHost, uint connectedPort) where T : ForwardedPort, new()
|
||||
{
|
||||
if (boundHost == null)
|
||||
throw new ArgumentNullException("boundHost");
|
||||
|
||||
if (connectedHost == null)
|
||||
throw new ArgumentNullException("connectedHost");
|
||||
|
||||
if (!boundHost.IsValidHost())
|
||||
throw new ArgumentException("boundHost");
|
||||
|
||||
if (!boundPort.IsValidPort())
|
||||
throw new ArgumentOutOfRangeException("boundPort");
|
||||
|
||||
if (!connectedHost.IsValidHost())
|
||||
throw new ArgumentException("connectedHost");
|
||||
|
||||
if (!connectedPort.IsValidPort())
|
||||
throw new ArgumentOutOfRangeException("connectedPort");
|
||||
|
||||
/// <param name="port">The port.</param>
|
||||
public void AddForwardedPort(ForwardedPort port)
|
||||
{
|
||||
// Ensure that connection is established.
|
||||
this.EnsureConnection();
|
||||
|
||||
T port = new T();
|
||||
if (port.Session != null && port.Session != this.Session)
|
||||
throw new InvalidOperationException("Forwarded port is already added to a different client.");
|
||||
|
||||
port.Session = this.Session;
|
||||
port.BoundHost = boundHost;
|
||||
port.BoundPort = boundPort;
|
||||
port.Host = connectedHost;
|
||||
port.Port = connectedPort;
|
||||
|
||||
this._forwardedPorts.Add(port);
|
||||
|
||||
return port;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds forwarded port to the list bound to "localhost".
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of forwarded port to add</typeparam>
|
||||
/// <param name="boundPort">The bound port.</param>
|
||||
/// <param name="connectedHost">The connected host.</param>
|
||||
/// <param name="connectedPort">The connected port.</param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="connectedHost"/> is null.</exception>
|
||||
/// <exception cref="ArgumentException"><paramref name="boundPort"/>, <paramref name="connectedPort"/> or <paramref name="connectedHost"/> is invalid.</exception>
|
||||
/// <exception cref="ArgumentOutOfRangeException"><paramref name="boundPort"/> or <paramref name="connectedPort"/> is not within <see cref="System.Net.IPEndPoint.MinPort"/> and <see cref="System.Net.IPEndPoint.MaxPort"/>.</exception>
|
||||
/// <exception cref="Renci.SshNet.Common.SshConnectionException">Client is not connected.</exception>
|
||||
public T AddForwardedPort<T>(uint boundPort, string connectedHost, uint connectedPort) where T : ForwardedPort, new()
|
||||
{
|
||||
return this.AddForwardedPort<T>("localhost", boundPort, connectedHost, connectedPort);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -200,6 +149,8 @@ namespace Renci.SshNet
|
||||
// Stop port forwarding before removing it
|
||||
port.Stop();
|
||||
|
||||
port.Session = null;
|
||||
|
||||
this._forwardedPorts.Remove(port);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user