2
0
Эх сурвалжийг харах

Refactor PortForwarding
Fix ChannelDirectTcpip clean up after socket is close by remote host
Add ForwardedPortDynamic to support dynamic forwarding, SOCKS4 and SOCKS5 are supported.

olegkap_cp 14 жил өмнө
parent
commit
cfa1a7028b

+ 13 - 0
Renci.SshClient/Renci.SshNet.NET35/ForwardedPortDynamic.NET35.cs

@@ -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(); });
+        }
+    }
+}

+ 0 - 75
Renci.SshClient/Renci.SshNet.NET35/ForwardedPortLocal.NET35.cs

@@ -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;
-        }
     }
 }

+ 11 - 1
Renci.SshClient/Renci.SshNet.NET35/Renci.SshNet.NET35.csproj

@@ -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. 

+ 4 - 1
Renci.SshClient/Renci.SshNet.Silverlight/Renci.SshNet.Silverlight.csproj

@@ -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. 

+ 101 - 98
Renci.SshClient/Renci.SshNet.Tests/SshClientTests/TestPortForwarding.NET40.cs

@@ -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));
+						}
+					}
+				);
+			}
+		}
+	}
 }

+ 89 - 83
Renci.SshClient/Renci.SshNet.Tests/SshClientTests/TestPortForwarding.cs

@@ -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();
-
-                var port1 = client.AddForwardedPort<ForwardedPortLocal>("localhost", 8084, "www.google.com",80);
+		[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 = new ForwardedPortLocal("localhost", 8084, "www.google.com", 80);
+                client.AddForwardedPort(port1);
                 port1.Exception += delegate(object sender, ExceptionEventArgs e)
-                {
-                    Assert.Fail(e.Exception.ToString());
-                };
-
-                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();
-
-                        Assert.IsNotNull(response);
-
-                        Debug.WriteLine("Http Response status code: " + response.StatusCode.ToString());
-
-                        response.Close();
-
-                        hasTestedTunnel = true;
-                    }
-                    catch (Exception ex)
-                    {
-                        Assert.Fail(ex.ToString());
-                    }
-
-                });
-
-                // 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.");
-
-                    });
-
-                    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.");
-            }
-        }
+				{
+					Assert.Fail(e.Exception.ToString());
+				};
+
+				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();
+
+						Assert.IsNotNull(response);
+
+						Debug.WriteLine("Http Response status code: " + response.StatusCode.ToString());
+
+						response.Close();
+
+						hasTestedTunnel = true;
+					}
+					catch (Exception ex)
+					{
+						Assert.Fail(ex.ToString());
+					}
+
+				});
+
+				// 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.");
+
+					});
+
+					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();
 			}
 		}
 

+ 4 - 1
Renci.SshClient/Renci.SshNet.WindowsPhone/Renci.SshNet.WindowsPhone.csproj

@@ -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. 

+ 32 - 16
Renci.SshClient/Renci.SshNet/Channels/ChannelDirectTcpip.cs

@@ -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);
+        }
 
-            //  Start reading data from the port and send to channel
-            EventWaitHandle readerTaskError = new AutoResetEvent(false);
+        /// <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
             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);

+ 0 - 20
Renci.SshClient/Renci.SshNet/ForwardedPort.cs

@@ -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>

+ 277 - 0
Renci.SshClient/Renci.SshNet/ForwardedPortDynamic.NET.cs

@@ -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();
+        }
+
+
+    }
+}

+ 13 - 0
Renci.SshClient/Renci.SshNet/ForwardedPortDynamic.NET40.cs

@@ -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);
+        }
+    }
+}

+ 121 - 0
Renci.SshClient/Renci.SshNet/ForwardedPortDynamic.cs

@@ -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
+}
+}

+ 91 - 0
Renci.SshClient/Renci.SshNet/ForwardedPortLocal.NET.cs

@@ -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;
+        }
+    }
+}

+ 2 - 81
Renci.SshClient/Renci.SshNet/ForwardedPortLocal.NET40.cs

@@ -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;
-        }
     }
 }

+ 65 - 0
Renci.SshClient/Renci.SshNet/ForwardedPortLocal.cs

@@ -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>

+ 59 - 0
Renci.SshClient/Renci.SshNet/ForwardedPortRemote.cs

@@ -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>

+ 8 - 0
Renci.SshClient/Renci.SshNet/Renci.SshNet.csproj

@@ -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>

+ 8 - 57
Renci.SshClient/Renci.SshNet/SshClient.cs

@@ -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);
         }