Explorar el Código

Merge remote-tracking branch 'refs/remotes/origin/develop'

drieseng hace 9 años
padre
commit
351e3c0afa
Se han modificado 100 ficheros con 2244 adiciones y 1103 borrados
  1. 2 1
      appveyor.yml
  2. 9 3
      src/Renci.SshNet.NET35/Renci.SshNet.NET35.csproj
  3. 3 1
      src/Renci.SshNet.NETCore/project.json
  4. 6 0
      src/Renci.SshNet.Silverlight/Renci.SshNet.Silverlight.csproj
  5. 1 3
      src/Renci.SshNet.Silverlight/Session.SilverlightShared.cs
  6. 9 3
      src/Renci.SshNet.Silverlight5/Renci.SshNet.Silverlight5.csproj
  7. 27 0
      src/Renci.SshNet.Tests.NET35/Renci.SshNet.Tests.NET35.csproj
  8. 2 1
      src/Renci.SshNet.Tests/Classes/CipherInfoTest.cs
  9. 4 0
      src/Renci.SshNet.Tests/Classes/Common/ASCIIEncodingTest.cs
  10. 4 0
      src/Renci.SshNet.Tests/Classes/Common/BigIntegerTest.cs
  11. 207 0
      src/Renci.SshNet.Tests/Classes/Common/CountdownEventTest.cs
  12. 7 1
      src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_Concat.cs
  13. 9 2
      src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_IsEqualTo_ByteArray.cs
  14. 8 2
      src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_Take_Count.cs
  15. 0 1
      src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_Take_OffsetAndCount.cs
  16. 1 4
      src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_ToGlobalRequestName.cs
  17. 0 1
      src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_TrimLeadingZeros.cs
  18. 0 1
      src/Renci.SshNet.Tests/Classes/Common/HostKeyEventArgsTest.cs
  19. 5 3
      src/Renci.SshNet.Tests/Classes/Common/PipeStream_Close_BlockingRead.cs
  20. 5 4
      src/Renci.SshNet.Tests/Classes/Common/PipeStream_Close_BlockingWrite.cs
  21. 4 1
      src/Renci.SshNet.Tests/Classes/Common/PipeStream_Flush_BytesRemainingAfterRead.cs
  22. 4 1
      src/Renci.SshNet.Tests/Classes/Common/PipeStream_Flush_NoBytesRemainingAfterRead.cs
  23. 0 2
      src/Renci.SshNet.Tests/Classes/Compression/CompressorTest.cs
  24. 5 3
      src/Renci.SshNet.Tests/Classes/Compression/ZlibStreamTest.cs
  25. 81 38
      src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Dispose_PortStarted_ChannelBound.cs
  26. 256 0
      src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_SessionErrorOccurred_ChannelBound.cs
  27. 22 7
      src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Started_SocketVersionNotSupported.cs
  28. 69 39
      src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Stop_PortStarted_ChannelBound.cs
  29. 38 31
      src/Renci.SshNet.Tests/Classes/ForwardedPortLocalTest_Dispose_PortStarted_ChannelBound.cs
  30. 67 44
      src/Renci.SshNet.Tests/Classes/ForwardedPortLocalTest_Stop_PortStarted_ChannelBound.cs
  31. 4 0
      src/Renci.SshNet.Tests/Classes/ForwardedPortRemoteTest.cs
  32. 67 33
      src/Renci.SshNet.Tests/Classes/ForwardedPortRemoteTest_Dispose_PortStarted_ChannelBound.cs
  33. 6 6
      src/Renci.SshNet.Tests/Classes/Messages/Authentication/BannerMessageTest.cs
  34. 5 3
      src/Renci.SshNet.Tests/Classes/Messages/Authentication/FailureMessageTest.cs
  35. 9 6
      src/Renci.SshNet.Tests/Classes/Messages/Authentication/RequestMessagePublicKeyTest.cs
  36. 6 6
      src/Renci.SshNet.Tests/Classes/Messages/Authentication/SuccessMessageTest.cs
  37. 5 3
      src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelCloseMessageTest.cs
  38. 5 3
      src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelEofMessageTest.cs
  39. 5 3
      src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelExtendedDataMessageTest.cs
  40. 5 3
      src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelFailureMessageTest.cs
  41. 5 3
      src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelOpenConfirmationMessageTest.cs
  42. 5 3
      src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelOpenFailureMessageTest.cs
  43. 5 3
      src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelRequest/EndOfWriteRequestInfoTest.cs
  44. 5 3
      src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelRequest/KeepAliveRequestInfoTest.cs
  45. 5 3
      src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelSuccessMessageTest.cs
  46. 5 3
      src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelWindowAdjustMessageTest.cs
  47. 5 3
      src/Renci.SshNet.Tests/Classes/Messages/Connection/GlobalRequestMessageTest.cs
  48. 3 3
      src/Renci.SshNet.Tests/Classes/Messages/Connection/RequestFailureMessageTest.cs
  49. 5 3
      src/Renci.SshNet.Tests/Classes/Messages/Connection/RequestSuccessMessageTest.cs
  50. 8 7
      src/Renci.SshNet.Tests/Classes/Messages/MessageAttributeTest.cs
  51. 4 6
      src/Renci.SshNet.Tests/Classes/Messages/MessageTest.cs
  52. 3 3
      src/Renci.SshNet.Tests/Classes/Messages/Transport/DebugMessageTest.cs
  53. 5 3
      src/Renci.SshNet.Tests/Classes/Messages/Transport/DisconnectMessageTest.cs
  54. 3 2
      src/Renci.SshNet.Tests/Classes/Messages/Transport/KeyExchangeDhGroupExchangeGroupTest.cs
  55. 3 2
      src/Renci.SshNet.Tests/Classes/Messages/Transport/KeyExchangeDhReplyMessageTest.cs
  56. 38 37
      src/Renci.SshNet.Tests/Classes/Messages/Transport/KeyExchangeInitMessageTest.cs
  57. 3 2
      src/Renci.SshNet.Tests/Classes/Messages/Transport/NewKeysMessageTest.cs
  58. 3 2
      src/Renci.SshNet.Tests/Classes/Messages/Transport/ServiceAcceptMessageTest.cs
  59. 3 2
      src/Renci.SshNet.Tests/Classes/Messages/Transport/ServiceRequestMessageTest.cs
  60. 3 2
      src/Renci.SshNet.Tests/Classes/Messages/Transport/UnimplementedMessageTest.cs
  61. 1 0
      src/Renci.SshNet.Tests/Classes/Security/CertificateHostAlgorithmTest.cs
  62. 3 2
      src/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/Paddings/PKCS5PaddingTest.cs
  63. 2 1
      src/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/Paddings/PKCS7PaddingTest.cs
  64. 19 10
      src/Renci.SshNet.Tests/Classes/Security/Cryptography/DsaKeyTest.cs
  65. 9 10
      src/Renci.SshNet.Tests/Classes/Security/Cryptography/HMacTest.cs
  66. 7 4
      src/Renci.SshNet.Tests/Classes/Security/KeyExchangeDiffieHellmanGroup1Sha1Test.cs
  67. 5 3
      src/Renci.SshNet.Tests/Classes/Security/KeyExchangeDiffieHellmanGroupExchangeSha256Test.cs
  68. 8 8
      src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ConnectionReset.cs
  69. 3 2
      src/Renci.SshNet.Tests/Classes/Sftp/SftpDownloadAsyncResultTest.cs
  70. 5 4
      src/Renci.SshNet.Tests/Classes/Sftp/SftpFileAttributesTest.cs
  71. 5 3
      src/Renci.SshNet.Tests/Classes/Sftp/SftpFileSystemInformationTest.cs
  72. 3 2
      src/Renci.SshNet.Tests/Classes/Sftp/SftpListDirectoryAsyncResultTest.cs
  73. 3 2
      src/Renci.SshNet.Tests/Classes/Sftp/SftpSynchronizeDirectoriesAsyncResultTest.cs
  74. 3 2
      src/Renci.SshNet.Tests/Classes/Sftp/SftpUploadAsyncResultTest.cs
  75. 15 12
      src/Renci.SshNet.Tests/Common/AsyncSocketListener.cs
  76. 20 6
      src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj
  77. 14 8
      src/Renci.SshNet.UAP10/Renci.SshNet.UAP10.csproj
  78. 2 1
      src/Renci.SshNet.VS2015.sln.DotSettings
  79. 6 0
      src/Renci.SshNet.WindowsPhone/Renci.SshNet.WindowsPhone.csproj
  80. 187 58
      src/Renci.SshNet.WindowsPhone/Session.WP.cs
  81. 15 3
      src/Renci.SshNet.WindowsPhone8/Renci.SshNet.WindowsPhone8.csproj
  82. 3 2
      src/Renci.SshNet/Abstractions/CryptoAbstraction.cs
  83. 6 17
      src/Renci.SshNet/Channels/Channel.cs
  84. 2 56
      src/Renci.SshNet/Channels/ChannelDirectTcpip.cs
  85. 0 26
      src/Renci.SshNet/Channels/ChannelForwardedTcpip.cs
  86. 1 1
      src/Renci.SshNet/ClientAuthentication.cs
  87. 2 2
      src/Renci.SshNet/Common/BigInteger.cs
  88. 153 0
      src/Renci.SshNet/Common/CountdownEvent.cs
  89. 2 2
      src/Renci.SshNet/Common/HostKeyEventArgs.cs
  90. 1 1
      src/Renci.SshNet/Common/NetConfServerException.cs
  91. 1 1
      src/Renci.SshNet/Common/ProxyException.cs
  92. 1 1
      src/Renci.SshNet/Common/SshException.cs
  93. 11 11
      src/Renci.SshNet/ConnectionInfo.cs
  94. 11 10
      src/Renci.SshNet/ForwardedPort.cs
  95. 179 244
      src/Renci.SshNet/ForwardedPortDynamic.NET.cs
  96. 26 26
      src/Renci.SshNet/ForwardedPortDynamic.cs
  97. 160 136
      src/Renci.SshNet/ForwardedPortLocal.NET.cs
  98. 26 24
      src/Renci.SshNet/ForwardedPortLocal.cs
  99. 92 44
      src/Renci.SshNet/ForwardedPortRemote.cs
  100. 141 0
      src/Renci.SshNet/ForwardedPortStatus.cs

+ 2 - 1
appveyor.yml

@@ -11,4 +11,5 @@ test:
   assemblies: src\Renci.SshNet.Tests\bin\Debug\Renci.SshNet.Tests.dll
   categories:
     except:
-    - integration
+    - integration
+    - LongRunning

+ 9 - 3
src/Renci.SshNet.NET35/Renci.SshNet.NET35.csproj

@@ -18,7 +18,7 @@
     <DebugType>full</DebugType>
     <Optimize>false</Optimize>
     <OutputPath>bin\Debug\</OutputPath>
-    <DefineConstants>TRACE;DEBUG;FEATURE_REGEX_COMPILE;FEATURE_BINARY_SERIALIZATION;FEATURE_RNG_CREATE;FEATURE_SOCKET_SYNC;FEATURE_SOCKET_EAP;FEATURE_SOCKET_APM;FEATURE_SOCKET_POLL;FEATURE_STREAM_APM;FEATURE_DNS_SYNC;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_HASH_MD5;FEATURE_HASH_SHA1_CREATE;FEATURE_HASH_SHA256_CREATE;FEATURE_HASH_SHA384_CREATE;FEATURE_HASH_SHA512_CREATE;FEATURE_HASH_RIPEMD160_CREATE;FEATURE_HMAC_MD5;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256;FEATURE_HMAC_SHA384;FEATURE_HMAC_SHA512;FEATURE_HMAC_RIPEMD160;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_DIAGNOSTICS_TRACESOURCE;FEATURE_ENCODING_ASCII</DefineConstants>
+    <DefineConstants>TRACE;DEBUG;FEATURE_REGEX_COMPILE;FEATURE_BINARY_SERIALIZATION;FEATURE_RNG_CREATE;FEATURE_SOCKET_SYNC;FEATURE_SOCKET_EAP;FEATURE_SOCKET_APM;FEATURE_SOCKET_SETSOCKETOPTION;FEATURE_SOCKET_POLL;FEATURE_STREAM_APM;FEATURE_DNS_SYNC;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_HASH_MD5;FEATURE_HASH_SHA1_CREATE;FEATURE_HASH_SHA256_CREATE;FEATURE_HASH_SHA384_CREATE;FEATURE_HASH_SHA512_CREATE;FEATURE_HASH_RIPEMD160_CREATE;FEATURE_HMAC_MD5;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256;FEATURE_HMAC_SHA384;FEATURE_HMAC_SHA512;FEATURE_HMAC_RIPEMD160;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_DIAGNOSTICS_TRACESOURCE;FEATURE_ENCODING_ASCII</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
     <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
@@ -28,7 +28,7 @@
     <DebugType>none</DebugType>
     <Optimize>true</Optimize>
     <OutputPath>bin\Release\</OutputPath>
-    <DefineConstants>TRACE;FEATURE_REGEX_COMPILE;FEATURE_BINARY_SERIALIZATION;FEATURE_RNG_CREATE;FEATURE_SOCKET_SYNC;FEATURE_SOCKET_EAP;FEATURE_SOCKET_APM;FEATURE_SOCKET_POLL;FEATURE_STREAM_APM;FEATURE_DNS_SYNC;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_HASH_MD5;FEATURE_HASH_SHA1_CREATE;FEATURE_HASH_SHA256_CREATE;FEATURE_HASH_SHA384_CREATE;FEATURE_HASH_SHA512_CREATE;FEATURE_HASH_RIPEMD160_CREATE;FEATURE_HMAC_MD5;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256;FEATURE_HMAC_SHA384;FEATURE_HMAC_SHA512;FEATURE_HMAC_RIPEMD160;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_DIAGNOSTICS_TRACESOURCE;FEATURE_ENCODING_ASCII</DefineConstants>
+    <DefineConstants>TRACE;FEATURE_REGEX_COMPILE;FEATURE_BINARY_SERIALIZATION;FEATURE_RNG_CREATE;FEATURE_SOCKET_SYNC;FEATURE_SOCKET_EAP;FEATURE_SOCKET_APM;FEATURE_SOCKET_SETSOCKETOPTION;FEATURE_SOCKET_POLL;FEATURE_STREAM_APM;FEATURE_DNS_SYNC;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_HASH_MD5;FEATURE_HASH_SHA1_CREATE;FEATURE_HASH_SHA256_CREATE;FEATURE_HASH_SHA384_CREATE;FEATURE_HASH_SHA512_CREATE;FEATURE_HASH_RIPEMD160_CREATE;FEATURE_HMAC_MD5;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256;FEATURE_HMAC_SHA384;FEATURE_HMAC_SHA512;FEATURE_HMAC_RIPEMD160;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_DIAGNOSTICS_TRACESOURCE;FEATURE_ENCODING_ASCII</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
     <DocumentationFile>bin\Release\Renci.SshNet.xml</DocumentationFile>
@@ -165,6 +165,9 @@
     <Compile Include="..\Renci.SshNet\Common\ChannelRequestEventArgs.cs">
       <Link>Common\ChannelRequestEventArgs.cs</Link>
     </Compile>
+    <Compile Include="..\Renci.SshNet\Common\CountdownEvent.cs">
+      <Link>Common\CountdownEvent.cs</Link>
+    </Compile>
     <Compile Include="..\Renci.SshNet\Common\DerData.cs">
       <Link>Common\DerData.cs</Link>
     </Compile>
@@ -279,6 +282,9 @@
     <Compile Include="..\Renci.SshNet\ForwardedPortRemote.cs">
       <Link>ForwardedPortRemote.cs</Link>
     </Compile>
+    <Compile Include="..\Renci.SshNet\ForwardedPortStatus.cs">
+      <Link>ForwardedPortStatus.cs</Link>
+    </Compile>
     <Compile Include="..\Renci.SshNet\HashInfo.cs">
       <Link>HashInfo.cs</Link>
     </Compile>
@@ -913,7 +919,7 @@
   <Import Project="$(MSBuildToolsPath)\Microsoft.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. 

+ 3 - 1
src/Renci.SshNet.NETCore/project.json

@@ -65,10 +65,12 @@
           "FEATURE_RNG_CREATE",
           "FEATURE_SOCKET_TAP",
           "FEATURE_SOCKET_EAP",
-          "FEATURE_SOCKET_POLL",
           "FEATURE_SOCKET_SYNC",
+          "FEATURE_SOCKET_SETSOCKETOPTION",
+          "FEATURE_SOCKET_POLL",
           "FEATURE_DNS_TAP",
           "FEATURE_STREAM_TAP",
+          "FEATURE_THREAD_COUNTDOWNEVENT",
           "FEATURE_THREAD_THREADPOOL",
           "FEATURE_THREAD_SLEEP",
           "FEATURE_HASH_MD5",

+ 6 - 0
src/Renci.SshNet.Silverlight/Renci.SshNet.Silverlight.csproj

@@ -183,6 +183,9 @@
     <Compile Include="..\Renci.SshNet\Common\ChannelRequestEventArgs.cs">
       <Link>Common\ChannelRequestEventArgs.cs</Link>
     </Compile>
+    <Compile Include="..\Renci.SshNet\Common\CountdownEvent.cs">
+      <Link>Common\CountdownEvent.cs</Link>
+    </Compile>
     <Compile Include="..\Renci.SshNet\Common\DerData.cs">
       <Link>Common\DerData.cs</Link>
     </Compile>
@@ -288,6 +291,9 @@
     <Compile Include="..\Renci.SshNet\ForwardedPortRemote.cs">
       <Link>ForwardedPortRemote.cs</Link>
     </Compile>
+    <Compile Include="..\Renci.SshNet\ForwardedPortStatus.cs">
+      <Link>ForwardedPortStatus.cs</Link>
+    </Compile>
     <Compile Include="..\Renci.SshNet\HashInfo.cs">
       <Link>HashInfo.cs</Link>
     </Compile>

+ 1 - 3
src/Renci.SshNet.Silverlight/Session.SilverlightShared.cs

@@ -1,6 +1,4 @@
-using System.Linq;
-
-namespace Renci.SshNet
+namespace Renci.SshNet
 {
     public partial class Session
     {

+ 9 - 3
src/Renci.SshNet.Silverlight5/Renci.SshNet.Silverlight5.csproj

@@ -29,7 +29,7 @@
     <DebugType>full</DebugType>
     <Optimize>false</Optimize>
     <OutputPath>Bin\Debug</OutputPath>
-    <DefineConstants>TRACE;DEBUG;FEATURE_DIRECTORYINFO_ENUMERATEFILES;FEATURE_RNG_CSP;FEATURE_SOCKET_EAP;FEATURE_STREAM_APM;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_HASH_SHA1_MANAGED;FEATURE_HASH_SHA256_MANAGED;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256</DefineConstants>
+    <DefineConstants>TRACE;DEBUG;FEATURE_DIRECTORYINFO_ENUMERATEFILES;FEATURE_RNG_CSP;FEATURE_SOCKET_EAP;FEATURE_SOCKET_SETSOCKETOPTION;FEATURE_STREAM_APM;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_HASH_SHA1_MANAGED;FEATURE_HASH_SHA256_MANAGED;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256</DefineConstants>
     <NoStdLib>true</NoStdLib>
     <NoConfig>true</NoConfig>
     <ErrorReport>prompt</ErrorReport>
@@ -42,7 +42,7 @@
     <DebugType>none</DebugType>
     <Optimize>true</Optimize>
     <OutputPath>Bin\Release</OutputPath>
-    <DefineConstants>TRACE;FEATURE_DIRECTORYINFO_ENUMERATEFILES;FEATURE_RNG_CSP;FEATURE_SOCKET_EAP;FEATURE_STREAM_APM;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_HASH_SHA1_MANAGED;FEATURE_HASH_SHA256_MANAGED;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256</DefineConstants>
+    <DefineConstants>TRACE;FEATURE_DIRECTORYINFO_ENUMERATEFILES;FEATURE_RNG_CSP;FEATURE_SOCKET_EAP;FEATURE_SOCKET_SETSOCKETOPTION;FEATURE_STREAM_APM;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_HASH_SHA1_MANAGED;FEATURE_HASH_SHA256_MANAGED;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256</DefineConstants>
     <NoStdLib>true</NoStdLib>
     <NoConfig>true</NoConfig>
     <ErrorReport>prompt</ErrorReport>
@@ -195,6 +195,9 @@
     <Compile Include="..\Renci.SshNet\Common\ChannelRequestEventArgs.cs">
       <Link>Common\ChannelRequestEventArgs.cs</Link>
     </Compile>
+    <Compile Include="..\Renci.SshNet\Common\CountdownEvent.cs">
+      <Link>Common\CountdownEvent.cs</Link>
+    </Compile>
     <Compile Include="..\Renci.SshNet\Common\DerData.cs">
       <Link>Common\DerData.cs</Link>
     </Compile>
@@ -303,6 +306,9 @@
     <Compile Include="..\Renci.SshNet\ForwardedPortRemote.cs">
       <Link>ForwardedPortRemote.cs</Link>
     </Compile>
+    <Compile Include="..\Renci.SshNet\ForwardedPortStatus.cs">
+      <Link>ForwardedPortStatus.cs</Link>
+    </Compile>
     <Compile Include="..\Renci.SshNet\HashInfo.cs">
       <Link>HashInfo.cs</Link>
     </Compile>
@@ -917,7 +923,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. 

+ 27 - 0
src/Renci.SshNet.Tests.NET35/Renci.SshNet.Tests.NET35.csproj

@@ -65,6 +65,18 @@
     <Compile Include="..\..\test\Renci.SshNet.Shared.Tests\Abstractions\ThreadAbstraction_ExecuteThread.cs">
       <Link>Classes\Abstraction\ThreadAbstraction_ExecuteThread.cs</Link>
     </Compile>
+    <Compile Include="..\..\test\Renci.SshNet.Shared.Tests\ForwardedPortStatusTest_Started.cs">
+      <Link>Classes\ForwardedPortStatusTest_Started.cs</Link>
+    </Compile>
+    <Compile Include="..\..\test\Renci.SshNet.Shared.Tests\ForwardedPortStatusTest_Starting.cs">
+      <Link>Classes\ForwardedPortStatusTest_Starting.cs</Link>
+    </Compile>
+    <Compile Include="..\..\test\Renci.SshNet.Shared.Tests\ForwardedPortStatusTest_Stopped.cs">
+      <Link>Classes\ForwardedPortStatusTest_Stopped.cs</Link>
+    </Compile>
+    <Compile Include="..\..\test\Renci.SshNet.Shared.Tests\ForwardedPortStatusTest_Stopping.cs">
+      <Link>Classes\ForwardedPortStatusTest_Stopping.cs</Link>
+    </Compile>
     <Compile Include="..\Renci.SshNet.Tests\Classes\Channels\ChannelDirectTcpipTest.cs">
       <Link>Classes\Channels\ChannelDirectTcpipTest.cs</Link>
     </Compile>
@@ -251,6 +263,9 @@
     <Compile Include="..\Renci.SshNet.Tests\Classes\Common\ChannelRequestEventArgsTest.cs">
       <Link>Classes\Common\ChannelRequestEventArgsTest.cs</Link>
     </Compile>
+    <Compile Include="..\Renci.SshNet.Tests\Classes\Common\CountdownEventTest.cs">
+      <Link>Classes\Common\CountdownEventTest.cs</Link>
+    </Compile>
     <Compile Include="..\Renci.SshNet.Tests\Classes\Common\DerDataTest.cs">
       <Link>Classes\Common\DerDataTest.cs</Link>
     </Compile>
@@ -269,6 +284,18 @@
     <Compile Include="..\Renci.SshNet.Tests\Classes\Common\PipeStreamTest.cs">
       <Link>Classes\Common\PipeStreamTest.cs</Link>
     </Compile>
+    <Compile Include="..\Renci.SshNet.Tests\Classes\Common\PipeStream_Close_BlockingRead.cs">
+      <Link>Classes\Common\PipeStream_Close_BlockingRead.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet.Tests\Classes\Common\PipeStream_Close_BlockingWrite.cs">
+      <Link>Classes\Common\PipeStream_Close_BlockingWrite.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet.Tests\Classes\Common\PipeStream_Flush_BytesRemainingAfterRead.cs">
+      <Link>Classes\Common\PipeStream_Flush_BytesRemainingAfterRead.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet.Tests\Classes\Common\PipeStream_Flush_NoBytesRemainingAfterRead.cs">
+      <Link>Classes\Common\PipeStream_Flush_NoBytesRemainingAfterRead.cs</Link>
+    </Compile>
     <Compile Include="..\Renci.SshNet.Tests\Classes\Common\PortForwardEventArgsTest.cs">
       <Link>Classes\Common\PortForwardEventArgsTest.cs</Link>
     </Compile>

+ 2 - 1
src/Renci.SshNet.Tests/Classes/CipherInfoTest.cs

@@ -15,7 +15,8 @@ namespace Renci.SshNet.Tests.Classes
         /// <summary>
         ///A test for CipherInfo Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void CipherInfoConstructorTest()
         {
             int keySize = 0; // TODO: Initialize to an appropriate value

+ 4 - 0
src/Renci.SshNet.Tests/Classes/Common/ASCIIEncodingTest.cs

@@ -148,6 +148,8 @@ namespace Renci.SshNet.Tests.Classes.Common
         }
 
         [TestMethod]
+        [TestCategory("LongRunning")]
+        [TestCategory("Performance")]
         public void GetBytes_Performance()
         {
             const string input = "eererzfdfdsfsfsfsqdqseererzfdfdsfsfsfsqdqseererzfdfdsfsfsfsqdqseererzfdfdsfsfsfsqdqseererzfdfdsfsfsfsqdqseererzfdfdsfsfsfsqdqseererzfdfdsfsfsfsqdqseererzfdfdsfsfsfsqdqseererzfdfdsfsfsfsqdqs";
@@ -191,6 +193,8 @@ namespace Renci.SshNet.Tests.Classes.Common
         }
 
         [TestMethod]
+        [TestCategory("LongRunning")]
+        [TestCategory("Performance")]
         public void GetChars_Performance()
         {
             var input = new byte[2000];

+ 4 - 0
src/Renci.SshNet.Tests/Classes/Common/BigIntegerTest.cs

@@ -1560,6 +1560,8 @@ namespace Renci.SshNet.Tests.Classes.Common
         }
 
         [TestMethod]
+        [TestCategory("LongRunning")]
+        [TestCategory("Performance")]
         public void ToArray_Performance()
         {
             const int loopCount = 100000000;
@@ -1586,6 +1588,8 @@ namespace Renci.SshNet.Tests.Classes.Common
         }
 
         [TestMethod]
+        [TestCategory("LongRunning")]
+        [TestCategory("Performance")]
         public void Ctor_ByteArray_Performance()
         {
             const int loopCount = 100000000;

+ 207 - 0
src/Renci.SshNet.Tests/Classes/Common/CountdownEventTest.cs

@@ -0,0 +1,207 @@
+using System;
+using System.Threading;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+#if !FEATURE_THREAD_COUNTDOWNEVENT
+using CountdownEvent = Renci.SshNet.Common.CountdownEvent;
+#endif
+
+namespace Renci.SshNet.Tests.Classes.Common
+{
+    [TestClass]
+    public class CountdownEventTest
+    {
+        private Random _random;
+
+        [TestInitialize]
+        public void Init()
+        {
+            _random = new Random();
+        }
+
+        [TestMethod]
+        public void Ctor_InitialCountGreatherThanZero()
+        {
+            var initialCount = _random.Next(1, 500);
+
+            var countdownEvent = CreateCountdownEvent(initialCount);
+            Assert.AreEqual(initialCount, countdownEvent.CurrentCount);
+            Assert.IsFalse(countdownEvent.IsSet);
+            countdownEvent.Dispose();
+        }
+
+        [TestMethod]
+        public void Ctor_InitialCountZero()
+        {
+            const int initialCount = 0;
+
+            var countdownEvent = CreateCountdownEvent(0);
+            Assert.AreEqual(initialCount, countdownEvent.CurrentCount);
+            Assert.IsTrue(countdownEvent.IsSet);
+            countdownEvent.Dispose();
+        }
+
+        [TestMethod]
+        public void Signal_CurrentCountGreatherThanOne()
+        {
+            var initialCount = _random.Next(2, 1000);
+
+            var countdownEvent = CreateCountdownEvent(initialCount);
+            Assert.IsFalse(countdownEvent.Signal());
+            Assert.AreEqual(--initialCount, countdownEvent.CurrentCount);
+            Assert.IsFalse(countdownEvent.IsSet);
+            countdownEvent.Dispose();
+        }
+
+        [TestMethod]
+        public void Signal_CurrentCountOne()
+        {
+            var countdownEvent = CreateCountdownEvent(1);
+            Assert.IsTrue(countdownEvent.Signal());
+            Assert.AreEqual(0, countdownEvent.CurrentCount);
+            Assert.IsTrue(countdownEvent.IsSet);
+            countdownEvent.Dispose();
+        }
+
+        [TestMethod]
+        public void Signal_CurrentCountZero()
+        {
+            var countdownEvent = CreateCountdownEvent(0);
+
+            try
+            {
+                countdownEvent.Signal();
+                Assert.Fail();
+            }
+            catch (InvalidOperationException)
+            {
+                // Invalid attempt made to decrement the event's count below zero
+            }
+            finally
+            {
+                countdownEvent.Dispose();
+            }
+        }
+
+        public void CurrentCountShouldReturnZeroAfterAttemptToDecrementCountBelowZero()
+        {
+        }
+
+        [TestMethod]
+        public void Wait_TimeoutInfinite_ShouldBlockUntilCountdownEventIsSet()
+        {
+            var sleep = TimeSpan.FromMilliseconds(100);
+            var timeout = Session.InfiniteTimeSpan;
+
+            var countdownEvent = CreateCountdownEvent(1);
+            var signalCount = 0;
+            var expectedSignalCount = _random.Next(5, 20);
+
+            for (var i = 0; i < (expectedSignalCount - 1); i++)
+                countdownEvent.AddCount();
+
+            var threads = new Thread[expectedSignalCount];
+            for (var i = 0; i < expectedSignalCount; i++)
+            {
+                threads[i] = new Thread(() =>
+                    {
+                        Thread.Sleep(sleep);
+                        Interlocked.Increment(ref signalCount);
+                        countdownEvent.Signal();
+                    });
+                threads[i].Start();
+            }
+
+            var start = DateTime.Now;
+            var actual = countdownEvent.Wait(timeout);
+            var elapsedTime = DateTime.Now - start;
+
+            Assert.IsTrue(actual);
+            Assert.AreEqual(expectedSignalCount, signalCount);
+            Assert.IsTrue(countdownEvent.IsSet);
+            Assert.IsTrue(elapsedTime >= sleep);
+            Assert.IsTrue(elapsedTime <= sleep.Add(TimeSpan.FromMilliseconds(100)));
+
+            countdownEvent.Dispose();
+        }
+
+        [TestMethod]
+        public void Wait_ShouldReturnTrueWhenCountdownEventIsSetBeforeTimeoutExpires()
+        {
+            var sleep = TimeSpan.FromMilliseconds(100);
+            var timeout = sleep.Add(TimeSpan.FromSeconds(2));
+
+            var countdownEvent = CreateCountdownEvent(1);
+            var signalCount = 0;
+            var expectedSignalCount = _random.Next(5, 20);
+
+            for (var i = 0; i < (expectedSignalCount - 1); i++)
+                countdownEvent.AddCount();
+
+            var threads = new Thread[expectedSignalCount];
+            for (var i = 0; i < expectedSignalCount; i++)
+            {
+                threads[i] = new Thread(() =>
+                {
+                    Thread.Sleep(sleep);
+                    Interlocked.Increment(ref signalCount);
+                    countdownEvent.Signal();
+                });
+                threads[i].Start();
+            }
+
+            var start = DateTime.Now;
+            var actual = countdownEvent.Wait(timeout);
+            var elapsedTime = DateTime.Now - start;
+
+            Assert.IsTrue(actual);
+            Assert.AreEqual(expectedSignalCount, signalCount);
+            Assert.IsTrue(countdownEvent.IsSet);
+            Assert.IsTrue(elapsedTime >= sleep);
+            Assert.IsTrue(elapsedTime <= timeout);
+
+            countdownEvent.Dispose();
+        }
+
+        [TestMethod]
+        public void Wait_ShouldReturnFalseTimeoutExpiresBeforeCountdownEventIsSet()
+        {
+            var sleep = TimeSpan.FromMilliseconds(100);
+            var timeout = TimeSpan.FromMilliseconds(30);
+
+            var countdownEvent = CreateCountdownEvent(1);
+            var signalCount = 0;
+            var expectedSignalCount = _random.Next(5, 20);
+
+            for (var i = 0; i < (expectedSignalCount - 1); i++)
+                countdownEvent.AddCount();
+
+            var threads = new Thread[expectedSignalCount];
+            for (var i = 0; i < expectedSignalCount; i++)
+            {
+                threads[i] = new Thread(() =>
+                {
+                    Thread.Sleep(sleep);
+                    countdownEvent.Signal();
+                    Interlocked.Increment(ref signalCount);
+                });
+                threads[i].Start();
+            }
+
+            var start = DateTime.Now;
+            var actual = countdownEvent.Wait(timeout);
+            var elapsedTime = DateTime.Now - start;
+
+            Assert.IsFalse(actual);
+            Assert.IsFalse(countdownEvent.IsSet);
+            Assert.IsTrue(elapsedTime >= timeout);
+
+            countdownEvent.Wait(Session.InfiniteTimeSpan);
+            countdownEvent.Dispose();
+        }
+
+        private static CountdownEvent CreateCountdownEvent(int initialCount)
+        {
+            return new CountdownEvent(initialCount);
+        }
+    }
+}

+ 7 - 1
src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_Concat.cs

@@ -97,6 +97,8 @@ namespace Renci.SshNet.Tests.Classes.Common
         }
 
         [TestMethod]
+        [TestCategory("LongRunning")]
+        [TestCategory("Performance")]
         public void Performance_LargeArray_FirstEmpty()
         {
             var first = Array<byte>.Empty;
@@ -108,6 +110,8 @@ namespace Renci.SshNet.Tests.Classes.Common
 
 
         [TestMethod]
+        [TestCategory("LongRunning")]
+        [TestCategory("Performance")]
         public void Performance_LargeArray_SecondEmpty()
         {
             var first = CreateBuffer(50000);
@@ -118,6 +122,8 @@ namespace Renci.SshNet.Tests.Classes.Common
         }
 
         [TestMethod]
+        [TestCategory("LongRunning")]
+        [TestCategory("Performance")]
         public void Performance_LargeArray_BothNotEmpty()
         {
             var first = CreateBuffer(50000);
@@ -127,7 +133,7 @@ namespace Renci.SshNet.Tests.Classes.Common
             Performance(first, second, runs);
         }
 
-        private void Performance(byte[] first, byte[] second, int runs)
+        private static void Performance(byte[] first, byte[] second, int runs)
         {
             var stopWatch = new Stopwatch();
 

+ 9 - 2
src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_IsEqualTo_ByteArray.cs

@@ -3,7 +3,6 @@ using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
 using System.Linq;
 using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Renci.SshNet.Common;
 
 namespace Renci.SshNet.Tests.Classes.Common
 {
@@ -98,6 +97,8 @@ namespace Renci.SshNet.Tests.Classes.Common
         }
 
         [TestMethod]
+        [TestCategory("LongRunning")]
+        [TestCategory("Performance")]
         public void Performance_LargeArray_Equal()
         {
             var buffer = CreateBuffer(50000);
@@ -108,6 +109,8 @@ namespace Renci.SshNet.Tests.Classes.Common
             Performance(left, right, runs);
         }
         [TestMethod]
+        [TestCategory("LongRunning")]
+        [TestCategory("Performance")]
         public void Performance_LargeArray_NotEqual_DifferentLength()
         {
             var left = CreateBuffer(50000);
@@ -118,6 +121,8 @@ namespace Renci.SshNet.Tests.Classes.Common
         }
 
         [TestMethod]
+        [TestCategory("LongRunning")]
+        [TestCategory("Performance")]
         public void Performance_LargeArray_NotEqual_SameLength()
         {
             var buffer = CreateBuffer(50000);
@@ -129,6 +134,8 @@ namespace Renci.SshNet.Tests.Classes.Common
         }
 
         [TestMethod]
+        [TestCategory("LongRunning")]
+        [TestCategory("Performance")]
         public void Performance_LargeArray_Same()
         {
             var left = CreateBuffer(50000);
@@ -138,7 +145,7 @@ namespace Renci.SshNet.Tests.Classes.Common
             Performance(left, right, runs);
         }
 
-        private void Performance(byte[] left, byte[] right, int runs)
+        private static void Performance(byte[] left, byte[] right, int runs)
         {
             var stopWatch = new Stopwatch();
 

+ 8 - 2
src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_Take_Count.cs

@@ -3,7 +3,6 @@ using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
 using System.Linq;
 using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Renci.SshNet.Common;
 
 namespace Renci.SshNet.Tests.Classes.Common
 {
@@ -95,6 +94,8 @@ namespace Renci.SshNet.Tests.Classes.Common
         }
 
         [TestMethod]
+        [TestCategory("LongRunning")]
+        [TestCategory("Performance")]
         public void Performance_LargeArray_All()
         {
             var value = CreateBuffer(50000);
@@ -105,6 +106,8 @@ namespace Renci.SshNet.Tests.Classes.Common
         }
 
         [TestMethod]
+        [TestCategory("LongRunning")]
+        [TestCategory("Performance")]
         public void Performance_LargeArray_LargeCount()
         {
             var value = CreateBuffer(50000);
@@ -115,6 +118,8 @@ namespace Renci.SshNet.Tests.Classes.Common
         }
 
         [TestMethod]
+        [TestCategory("LongRunning")]
+        [TestCategory("Performance")]
         public void Performance_LargeArray_SmallCount()
         {
             var value = CreateBuffer(50000);
@@ -125,6 +130,7 @@ namespace Renci.SshNet.Tests.Classes.Common
         }
 
         [TestMethod]
+        [TestCategory("Performance")]
         public void Performance_LargeArray_ZeroCount()
         {
             var value = CreateBuffer(50000);
@@ -134,7 +140,7 @@ namespace Renci.SshNet.Tests.Classes.Common
             Performance(value, count, runs);
         }
 
-        private void Performance(byte[] value, int count, int runs)
+        private static void Performance(byte[] value, int count, int runs)
         {
             var stopWatch = new Stopwatch();
 

+ 0 - 1
src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_Take_OffsetAndCount.cs

@@ -1,7 +1,6 @@
 using System;
 using System.Diagnostics.CodeAnalysis;
 using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Renci.SshNet.Common;
 
 namespace Renci.SshNet.Tests.Classes.Common
 {

+ 1 - 4
src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_ToGlobalRequestName.cs

@@ -1,7 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
+using System.Text;
 using Microsoft.VisualStudio.TestTools.UnitTesting;
 using Renci.SshNet.Messages.Connection;
 

+ 0 - 1
src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_TrimLeadingZeros.cs

@@ -1,7 +1,6 @@
 using System;
 using System.Diagnostics.CodeAnalysis;
 using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Renci.SshNet.Common;
 
 namespace Renci.SshNet.Tests.Classes.Common
 {

+ 0 - 1
src/Renci.SshNet.Tests/Classes/Common/HostKeyEventArgsTest.cs

@@ -1,6 +1,5 @@
 using Renci.SshNet.Common;
 using Microsoft.VisualStudio.TestTools.UnitTesting;
-using System;
 using Renci.SshNet.Security;
 using Renci.SshNet.Tests.Common;
 

+ 5 - 3
src/Renci.SshNet.Tests/Common/PipeStream_Close_BlockingRead.cs → src/Renci.SshNet.Tests/Classes/Common/PipeStream_Close_BlockingRead.cs

@@ -1,9 +1,8 @@
 using System;
-using System.Threading;
 using Microsoft.VisualStudio.TestTools.UnitTesting;
 using Renci.SshNet.Common;
 
-namespace Renci.SshNet.Tests.Common
+namespace Renci.SshNet.Tests.Classes.Common
 {
     [TestClass]
     public class PipeStream_Close_BlockingRead
@@ -34,12 +33,15 @@ namespace Renci.SshNet.Tests.Common
         protected void Act()
         {
             _pipeStream.Close();
+
+            // give async read time to complete
+            _asyncReadResult.AsyncWaitHandle.WaitOne(100);
         }
 
         [TestMethod]
         public void BlockingReadShouldHaveBeenInterrupted()
         {
-            Assert.IsTrue(_asyncReadResult.AsyncWaitHandle.WaitOne(200));
+            Assert.IsTrue(_asyncReadResult.IsCompleted);
         }
 
         [TestMethod]

+ 5 - 4
src/Renci.SshNet.Tests/Common/PipeStream_Close_BlockingWrite.cs → src/Renci.SshNet.Tests/Classes/Common/PipeStream_Close_BlockingWrite.cs

@@ -2,7 +2,7 @@
 using Microsoft.VisualStudio.TestTools.UnitTesting;
 using Renci.SshNet.Common;
 
-namespace Renci.SshNet.Tests.Common
+namespace Renci.SshNet.Tests.Classes.Common
 {
     [TestClass]
     public class PipeStream_Close_BlockingWrite
@@ -44,19 +44,20 @@ namespace Renci.SshNet.Tests.Common
         protected void Act()
         {
             _pipeStream.Close();
+
+            // give async write time to complete
+            _asyncWriteResult.AsyncWaitHandle.WaitOne(100);
         }
 
         [TestMethod]
         public void BlockingWriteShouldHaveBeenInterrupted()
         {
-            Assert.IsTrue(_asyncWriteResult.AsyncWaitHandle.WaitOne(200));
+            Assert.IsTrue(_asyncWriteResult.IsCompleted);
         }
 
         [TestMethod]
         public void WriteShouldHaveThrownObjectDisposedException()
         {
-            _asyncWriteResult.AsyncWaitHandle.WaitOne(200);
-
             Assert.IsNotNull(_writeException);
             Assert.AreEqual(typeof (ObjectDisposedException), _writeException.GetType());
         }

+ 4 - 1
src/Renci.SshNet.Tests/Common/PipeStream_Flush_BytesRemainingAfterRead.cs → src/Renci.SshNet.Tests/Classes/Common/PipeStream_Flush_BytesRemainingAfterRead.cs

@@ -3,7 +3,7 @@ using System.Threading;
 using Microsoft.VisualStudio.TestTools.UnitTesting;
 using Renci.SshNet.Common;
 
-namespace Renci.SshNet.Tests.Common
+namespace Renci.SshNet.Tests.Classes.Common
 {
     [TestClass]
     public class PipeStream_Flush_BytesRemainingAfterRead
@@ -37,6 +37,9 @@ namespace Renci.SshNet.Tests.Common
         protected void Act()
         {
             _pipeStream.Flush();
+
+            // give async read time to complete
+            _asyncReadResult.AsyncWaitHandle.WaitOne(100);
         }
 
         [TestMethod]

+ 4 - 1
src/Renci.SshNet.Tests/Common/PipeStream_Flush_NoBytesRemainingAfterRead.cs → src/Renci.SshNet.Tests/Classes/Common/PipeStream_Flush_NoBytesRemainingAfterRead.cs

@@ -2,7 +2,7 @@
 using Microsoft.VisualStudio.TestTools.UnitTesting;
 using Renci.SshNet.Common;
 
-namespace Renci.SshNet.Tests.Common
+namespace Renci.SshNet.Tests.Classes.Common
 {
     [TestClass]
     public class PipeStream_Flush_NoBytesRemainingAfterRead
@@ -32,6 +32,9 @@ namespace Renci.SshNet.Tests.Common
         protected void Act()
         {
             _pipeStream.Flush();
+
+            // give async read time to complete
+            _asyncReadResult.AsyncWaitHandle.WaitOne(100);
         }
 
         [TestMethod]

+ 0 - 2
src/Renci.SshNet.Tests/Classes/Compression/CompressorTest.cs

@@ -1,7 +1,5 @@
 using Renci.SshNet.Compression;
 using Microsoft.VisualStudio.TestTools.UnitTesting;
-using System;
-using Renci.SshNet;
 using Renci.SshNet.Tests.Common;
 
 namespace Renci.SshNet.Tests.Classes.Compression

+ 5 - 3
src/Renci.SshNet.Tests/Classes/Compression/ZlibStreamTest.cs

@@ -10,13 +10,14 @@ namespace Renci.SshNet.Tests.Classes.Compression
     ///This is a test class for ZlibStreamTest and is intended
     ///to contain all ZlibStreamTest Unit Tests
     ///</summary>
-    [TestClass()]
+    [TestClass]
     public class ZlibStreamTest : TestBase
     {
         /// <summary>
         ///A test for ZlibStream Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void ZlibStreamConstructorTest()
         {
             Stream stream = null; // TODO: Initialize to an appropriate value
@@ -28,7 +29,8 @@ namespace Renci.SshNet.Tests.Classes.Compression
         /// <summary>
         ///A test for Write
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void WriteTest()
         {
             Stream stream = null; // TODO: Initialize to an appropriate value

+ 81 - 38
src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Dispose_PortStarted_ChannelBound.cs

@@ -1,6 +1,5 @@
 using System;
 using System.Collections.Generic;
-using System.Diagnostics;
 using System.Globalization;
 using System.Linq;
 using System.Net;
@@ -9,6 +8,7 @@ using System.Text;
 using System.Threading;
 using Microsoft.VisualStudio.TestTools.UnitTesting;
 using Moq;
+using Renci.SshNet.Abstractions;
 using Renci.SshNet.Channels;
 using Renci.SshNet.Common;
 
@@ -27,8 +27,9 @@ namespace Renci.SshNet.Tests.Classes
         private Socket _client;
         private IPEndPoint _remoteEndpoint;
         private string _userName;
-        private TimeSpan _expectedElapsedTime;
-        private TimeSpan _elapsedTimeOfStop;
+        private TimeSpan _bindSleepTime;
+        private ManualResetEvent _channelBindStarted;
+        private ManualResetEvent _channelBindCompleted;
 
         [TestInitialize]
         public void Setup()
@@ -50,9 +51,26 @@ namespace Renci.SshNet.Tests.Classes
                 _forwardedPort.Dispose();
                 _forwardedPort = null;
             }
+            if (_channelBindStarted != null)
+            {
+                _channelBindStarted.Dispose();
+                _channelBindStarted = null;
+            }
+            if (_channelBindCompleted != null)
+            {
+                _channelBindCompleted.Dispose();
+                _channelBindCompleted = null;
+            }
         }
 
-        protected void Arrange()
+        private void CreateMocks()
+        {
+            _connectionInfoMock = new Mock<IConnectionInfo>(MockBehavior.Strict);
+            _sessionMock = new Mock<ISession>(MockBehavior.Strict);
+            _channelMock = new Mock<IChannelDirectTcpip>(MockBehavior.Strict);
+        }
+
+        private void SetupData()
         {
             var random = new Random();
 
@@ -60,61 +78,62 @@ namespace Renci.SshNet.Tests.Classes
             _exceptionRegister = new List<ExceptionEventArgs>();
             _endpoint = new IPEndPoint(IPAddress.Loopback, 8122);
             _remoteEndpoint = new IPEndPoint(IPAddress.Parse("193.168.1.5"), random.Next(IPEndPoint.MinPort, IPEndPoint.MaxPort));
-            _expectedElapsedTime = TimeSpan.FromMilliseconds(random.Next(100, 500));
+            _bindSleepTime = TimeSpan.FromMilliseconds(random.Next(100, 500));
             _userName = random.Next().ToString(CultureInfo.InvariantCulture);
-            _forwardedPort = new ForwardedPortDynamic(_endpoint.Address.ToString(), (uint)_endpoint.Port);
+            _channelBindStarted = new ManualResetEvent(false);
+            _channelBindCompleted = new ManualResetEvent(false);
 
-            _connectionInfoMock = new Mock<IConnectionInfo>(MockBehavior.Strict);
-            _sessionMock = new Mock<ISession>(MockBehavior.Strict);
-            _channelMock = new Mock<IChannelDirectTcpip>(MockBehavior.Strict);
+            _forwardedPort = new ForwardedPortDynamic(_endpoint.Address.ToString(), (uint)_endpoint.Port);
+            _forwardedPort.Closing += (sender, args) => _closingRegister.Add(args);
+            _forwardedPort.Exception += (sender, args) => _exceptionRegister.Add(args);
+            _forwardedPort.Session = _sessionMock.Object;
 
-            Socket handlerSocket = null;
+            _client = new Socket(_endpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp)
+            {
+                ReceiveTimeout = 100,
+                SendTimeout = 100,
+                SendBufferSize = 0
+            };
+        }
 
+        private void SetupMocks()
+        {
             _connectionInfoMock.Setup(p => p.Timeout).Returns(TimeSpan.FromSeconds(15));
             _sessionMock.Setup(p => p.IsConnected).Returns(true);
             _sessionMock.Setup(p => p.ConnectionInfo).Returns(_connectionInfoMock.Object);
             _sessionMock.Setup(p => p.CreateChannelDirectTcpip()).Returns(_channelMock.Object);
-            _channelMock.Setup(p => p.Open(_remoteEndpoint.Address.ToString(), (uint)_remoteEndpoint.Port, _forwardedPort, It.IsAny<Socket>())).Callback<string, uint, IForwardedPort, Socket>((address, port, forwardedPort, socket) => handlerSocket = socket);
+            _channelMock.Setup(p => p.Open(_remoteEndpoint.Address.ToString(), (uint)_remoteEndpoint.Port, _forwardedPort, It.IsAny<Socket>()));
             _channelMock.Setup(p => p.IsOpen).Returns(true);
             _channelMock.Setup(p => p.Bind()).Callback(() =>
                 {
-                    Thread.Sleep(_expectedElapsedTime);
-                    if (handlerSocket != null && handlerSocket.Connected)
-                        handlerSocket.Shutdown(SocketShutdown.Both);
+                    _channelBindStarted.Set();
+                    Thread.Sleep(_bindSleepTime);
+                    _channelBindCompleted.Set();
                 });
             _channelMock.Setup(p => p.Close());
             _channelMock.Setup(p => p.Dispose());
+        }
+
+        protected void Arrange()
+        {
+            CreateMocks();
+            SetupData();
+            SetupMocks();
 
-            _forwardedPort.Closing += (sender, args) => _closingRegister.Add(args);
-            _forwardedPort.Exception += (sender, args) => _exceptionRegister.Add(args);
-            _forwardedPort.Session = _sessionMock.Object;
             _forwardedPort.Start();
 
-            _client = new Socket(_endpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp)
-                {
-                    ReceiveTimeout = 500,
-                    SendTimeout = 500,
-                    SendBufferSize = 0
-                };
             EstablishSocks4Connection(_client);
         }
 
         protected void Act()
         {
-            var stopwatch = new Stopwatch();
-            stopwatch.Start();
-
             _forwardedPort.Dispose();
-
-            stopwatch.Stop();
-            _elapsedTimeOfStop = stopwatch.Elapsed;
         }
 
         [TestMethod]
-        public void StopShouldBlockUntilBoundChannelHasClosed()
+        public void ShouldBlockUntilBindHasCompleted()
         {
-            Assert.IsTrue(_elapsedTimeOfStop >= _expectedElapsedTime);
-            Assert.IsTrue(_elapsedTimeOfStop < _expectedElapsedTime.Add(TimeSpan.FromMilliseconds(200)));
+            Assert.IsTrue(_channelBindCompleted.WaitOne(0));
         }
 
         [TestMethod]
@@ -141,16 +160,24 @@ namespace Renci.SshNet.Tests.Classes
         }
 
         [TestMethod]
-        public void ExistingConnectionShouldBeClosed()
+        public void BoundClientShouldNotBeClosed()
         {
+            // the forwarded port itself does not close the client connection; when the channel is closed properly
+            // it's the channel that will take care of closing the client connection
+            //
+            // we'll check if the client connection is still alive by attempting to receive, which should time out
+            // as the forwarded port (or its channel) are not sending anything
+
+            var buffer = new byte[1];
+
             try
             {
-                _client.Send(new byte[] { 0x0a }, 0, 1, SocketFlags.None);
+                _client.Receive(buffer);
                 Assert.Fail();
             }
             catch (SocketException ex)
             {
-                Assert.AreEqual(SocketError.ConnectionReset, ex.SocketErrorCode);
+                Assert.AreEqual(SocketError.TimedOut, ex.SocketErrorCode);
             }
         }
 
@@ -163,7 +190,7 @@ namespace Renci.SshNet.Tests.Classes
         [TestMethod]
         public void ExceptionShouldNotHaveFired()
         {
-            Assert.AreEqual(0, _exceptionRegister.Count);
+            Assert.AreEqual(0, _exceptionRegister.Count, GetReportedExceptions());
         }
 
         [TestMethod]
@@ -214,8 +241,24 @@ namespace Renci.SshNet.Tests.Classes
             // terminate user name with null
             client.Send(new byte[] { 0x00 }, 0, 1, SocketFlags.None);
 
-            var buffer = new byte[16];
-            client.Receive(buffer, 0, buffer.Length, SocketFlags.None);
+            var buffer = new byte[8];
+            var bytesRead = SocketAbstraction.Read(client, buffer, 0, buffer.Length, TimeSpan.FromMilliseconds(500));
+            Assert.AreEqual(buffer.Length, bytesRead);
+
+            // wait until SOCKS client is bound to channel
+            Assert.IsTrue(_channelBindStarted.WaitOne(TimeSpan.FromMilliseconds(200)));
+        }
+
+        private string GetReportedExceptions()
+        {
+            if (_exceptionRegister.Count == 0)
+                return string.Empty;
+
+            string reportedExceptions = string.Empty;
+            foreach (var exceptionEvent in _exceptionRegister)
+                reportedExceptions += exceptionEvent.Exception.ToString();
+
+            return reportedExceptions;
         }
     }
 }

+ 256 - 0
src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_SessionErrorOccurred_ChannelBound.cs

@@ -0,0 +1,256 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+using System.Threading;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Moq;
+using Renci.SshNet.Abstractions;
+using Renci.SshNet.Channels;
+using Renci.SshNet.Common;
+
+namespace Renci.SshNet.Tests.Classes
+{
+    [TestClass]
+    public class ForwardedPortDynamicTest_SessionErrorOccurred_ChannelBound
+    {
+        private Mock<ISession> _sessionMock;
+        private Mock<IConnectionInfo> _connectionInfoMock;
+        private Mock<IChannelDirectTcpip> _channelMock;
+        private ForwardedPortDynamic _forwardedPort;
+        private IList<EventArgs> _closingRegister;
+        private IList<ExceptionEventArgs> _exceptionRegister;
+        private Exception _sessionException;
+        private IPEndPoint _endpoint;
+        private Socket _client;
+        private IPEndPoint _remoteEndpoint;
+        private string _userName;
+        private TimeSpan _bindSleepTime;
+        private ManualResetEvent _channelBindStarted;
+        private ManualResetEvent _channelBindCompleted;
+
+        [TestInitialize]
+        public void Setup()
+        {
+            Arrange();
+            Act();
+        }
+
+        [TestCleanup]
+        public void Cleanup()
+        {
+            if (_client != null)
+            {
+                _client.Dispose();
+                _client = null;
+            }
+            if (_forwardedPort != null)
+            {
+                _forwardedPort.Dispose();
+                _forwardedPort = null;
+            }
+            if (_channelBindStarted != null)
+            {
+                _channelBindStarted.Dispose();
+                _channelBindStarted = null;
+            }
+            if (_channelBindCompleted != null)
+            {
+                _channelBindCompleted.Dispose();
+                _channelBindCompleted = null;
+            }
+        }
+
+        private void CreateMocks()
+        {
+            _connectionInfoMock = new Mock<IConnectionInfo>(MockBehavior.Strict);
+            _sessionMock = new Mock<ISession>(MockBehavior.Strict);
+            _channelMock = new Mock<IChannelDirectTcpip>(MockBehavior.Strict);
+        }
+
+        private void SetupData()
+        {
+            var random = new Random();
+
+            _closingRegister = new List<EventArgs>();
+            _exceptionRegister = new List<ExceptionEventArgs>();
+            _endpoint = new IPEndPoint(IPAddress.Loopback, 8122);
+            _remoteEndpoint = new IPEndPoint(IPAddress.Parse("193.168.1.5"), random.Next(IPEndPoint.MinPort, IPEndPoint.MaxPort));
+            _bindSleepTime = TimeSpan.FromMilliseconds(random.Next(100, 500));
+            _userName = random.Next().ToString(CultureInfo.InvariantCulture);
+            _forwardedPort = new ForwardedPortDynamic(_endpoint.Address.ToString(), (uint) _endpoint.Port);
+            _sessionException = new Exception();
+            _channelBindStarted = new ManualResetEvent(false);
+            _channelBindCompleted = new ManualResetEvent(false);
+
+            _forwardedPort.Closing += (sender, args) => _closingRegister.Add(args);
+            _forwardedPort.Exception += (sender, args) => _exceptionRegister.Add(args);
+            _forwardedPort.Session = _sessionMock.Object;
+
+            _client = new Socket(_endpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp)
+                {
+                    ReceiveTimeout = 100,
+                    SendTimeout = 100,
+                    SendBufferSize = 0
+                };
+        }
+
+        private void SetupMocks()
+        {
+            _connectionInfoMock.Setup(p => p.Timeout).Returns(TimeSpan.FromSeconds(15));
+            _sessionMock.Setup(p => p.IsConnected).Returns(true);
+            _sessionMock.Setup(p => p.ConnectionInfo).Returns(_connectionInfoMock.Object);
+            _sessionMock.Setup(p => p.CreateChannelDirectTcpip()).Returns(_channelMock.Object);
+            _channelMock.Setup(p => p.Open(_remoteEndpoint.Address.ToString(), (uint)_remoteEndpoint.Port, _forwardedPort, It.IsAny<Socket>()));
+            _channelMock.Setup(p => p.IsOpen).Returns(true);
+            _channelMock.Setup(p => p.Bind()).Callback(() =>
+                {
+                    _channelBindStarted.Set();
+                    Thread.Sleep(_bindSleepTime);
+                    _channelBindCompleted.Set();
+                });
+            _channelMock.Setup(p => p.Close());
+            _channelMock.Setup(p => p.Dispose());
+        }
+
+        protected void Arrange()
+        {
+            CreateMocks();
+            SetupData();
+            SetupMocks();
+
+            // start port
+            _forwardedPort.Start();
+            // connect to port
+            EstablishSocks4Connection(_client);
+            // wait until SOCKS client is bound to channel
+            Assert.IsTrue(_channelBindStarted.WaitOne(TimeSpan.FromMilliseconds(200)));
+        }
+
+        protected void Act()
+        {
+            _sessionMock.Raise(p => p.ErrorOccured += null, new ExceptionEventArgs(_sessionException));
+        }
+
+        [TestMethod]
+        public void ShouldBlockUntilBindHasCompleted()
+        {
+            Assert.IsTrue(_channelBindCompleted.WaitOne(0));
+        }
+
+        [TestMethod]
+        public void IsStartedShouldReturnFalse()
+        {
+            Assert.IsFalse(_forwardedPort.IsStarted);
+        }
+
+        [TestMethod]
+        public void ForwardedPortShouldRefuseNewConnections()
+        {
+            using (var client = new Socket(_endpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp))
+            {
+                try
+                {
+                    client.Connect(_endpoint);
+                    Assert.Fail();
+                }
+                catch (SocketException ex)
+                {
+                    Assert.AreEqual(SocketError.ConnectionRefused, ex.SocketErrorCode);
+                }
+            }
+        }
+
+        [TestMethod]
+        public void BoundClientShouldNotBeClosed()
+        {
+            // the forwarded port itself does not close the client connection; when the channel is closed properly
+            // it's the channel that will take care of closing the client connection
+            //
+            // we'll check if the client connection is still alive by attempting to receive, which should time out
+            // as the forwarded port (or its channel) are not sending anything
+
+            var buffer = new byte[1];
+
+            try
+            {
+                _client.Receive(buffer);
+                Assert.Fail();
+            }
+            catch (SocketException ex)
+            {
+                Assert.AreEqual(SocketError.TimedOut, ex.SocketErrorCode);
+            }
+        }
+
+        [TestMethod]
+        public void ClosingShouldHaveFiredOnce()
+        {
+            Assert.AreEqual(1, _closingRegister.Count);
+        }
+
+        [TestMethod]
+        public void ExceptionShouldHaveFiredOne()
+        {
+            Assert.AreEqual(1, _exceptionRegister.Count);
+            Assert.IsNotNull(_exceptionRegister[0]);
+            Assert.AreSame(_sessionException, _exceptionRegister[0].Exception);
+        }
+
+        [TestMethod]
+        public void OpenOnChannelShouldBeInvokedOnce()
+        {
+            _channelMock.Verify(
+                p =>
+                    p.Open(_remoteEndpoint.Address.ToString(), (uint)_remoteEndpoint.Port, _forwardedPort,
+                        It.IsAny<Socket>()), Times.Once);
+        }
+
+        [TestMethod]
+        public void BindOnChannelShouldBeInvokedOnce()
+        {
+            _channelMock.Verify(p => p.Bind(), Times.Once);
+        }
+
+        [TestMethod]
+        public void CloseOnChannelShouldBeInvokedOnce()
+        {
+            _channelMock.Verify(p => p.Close(), Times.Once);
+        }
+
+        [TestMethod]
+        public void DisposeOnChannelShouldBeInvokedOnce()
+        {
+            _channelMock.Verify(p => p.Dispose(), Times.Once);
+        }
+
+        private void EstablishSocks4Connection(Socket client)
+        {
+            var userNameBytes = Encoding.ASCII.GetBytes(_userName);
+            var addressBytes = _remoteEndpoint.Address.GetAddressBytes();
+            var portBytes = BitConverter.GetBytes((ushort)_remoteEndpoint.Port).Reverse().ToArray();
+
+            _client.Connect(_endpoint);
+
+            // send SOCKS version
+            client.Send(new byte[] { 0x04 }, 0, 1, SocketFlags.None);
+            // send command byte
+            client.Send(new byte[] { 0x00 }, 0, 1, SocketFlags.None);
+            // send port
+            client.Send(portBytes, 0, portBytes.Length, SocketFlags.None);
+            // send address
+            client.Send(addressBytes, 0, addressBytes.Length, SocketFlags.None);
+            // send user name
+            client.Send(userNameBytes, 0, userNameBytes.Length, SocketFlags.None);
+            // terminate user name with null
+            client.Send(new byte[] { 0x00 }, 0, 1, SocketFlags.None);
+
+            var buffer = new byte[8];
+            var bytesRead = SocketAbstraction.Read(client, buffer, 0, buffer.Length, TimeSpan.FromMilliseconds(500));
+            Assert.AreEqual(buffer.Length, bytesRead);
+        }
+    }
+}

+ 22 - 7
src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Started_SocketVersionNotSupported.cs

@@ -3,6 +3,7 @@ using System.Collections.Generic;
 using System.Net;
 using System.Net.Sockets;
 using System.Text;
+using System.Threading;
 using Microsoft.VisualStudio.TestTools.UnitTesting;
 using Moq;
 using Renci.SshNet.Channels;
@@ -20,6 +21,7 @@ namespace Renci.SshNet.Tests.Classes
         private Socket _client;
         private IList<EventArgs> _closingRegister;
         private IList<ExceptionEventArgs> _exceptionRegister;
+        private ManualResetEvent _exceptionFired;
         private TimeSpan _connectionTimeout;
 
         [TestInitialize]
@@ -38,14 +40,17 @@ namespace Renci.SshNet.Tests.Classes
                 _connectionInfoMock.Setup(p => p.Timeout).Returns(TimeSpan.FromSeconds(5));
                 _forwardedPort.Stop();
             }
+
             if (_client != null)
             {
-                if (_client.Connected)
-                {
-                    _client.Shutdown(SocketShutdown.Both);
-                    _client.Close();
-                    _client = null;
-                }
+                _client.Close();
+                _client = null;
+            }
+
+            if (_exceptionFired != null)
+            {
+                _exceptionFired.Dispose();
+                _exceptionFired = null;
             }
         }
 
@@ -53,6 +58,7 @@ namespace Renci.SshNet.Tests.Classes
         {
             _closingRegister = new List<EventArgs>();
             _exceptionRegister = new List<ExceptionEventArgs>();
+            _exceptionFired = new ManualResetEvent(false);
             _connectionTimeout = TimeSpan.FromSeconds(5);
         }
 
@@ -74,6 +80,7 @@ namespace Renci.SshNet.Tests.Classes
             _channelMock.InSequence(seq).Setup(p => p.Close());
             _channelMock.InSequence(seq).Setup(p => p.Dispose());
         }
+
         private void Arrange()
         {
             SetupData();
@@ -82,7 +89,11 @@ namespace Renci.SshNet.Tests.Classes
 
             _forwardedPort = new ForwardedPortDynamic(8122);
             _forwardedPort.Closing += (sender, args) => _closingRegister.Add(args);
-            _forwardedPort.Exception += (sender, args) => _exceptionRegister.Add(args);
+            _forwardedPort.Exception += (sender, args) =>
+                {
+                    _exceptionRegister.Add(args);
+                    _exceptionFired.Set();
+                };
             _forwardedPort.Session = _sessionMock.Object;
             _forwardedPort.Start();
 
@@ -96,6 +107,10 @@ namespace Renci.SshNet.Tests.Classes
         {
             var buffer = new byte[] {0x07};
             _client.Send(buffer, 0, buffer.Length, SocketFlags.None);
+
+            // wait for Exception event to be fired as a way to ensure that SOCKS
+            // request has been handled completely
+            _exceptionFired.WaitOne(TimeSpan.FromMilliseconds(200));
         }
 
         [TestMethod]

+ 69 - 39
src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Stop_PortStarted_ChannelBound.cs

@@ -1,6 +1,5 @@
 using System;
 using System.Collections.Generic;
-using System.Diagnostics;
 using System.Globalization;
 using System.Linq;
 using System.Net;
@@ -9,6 +8,7 @@ using System.Text;
 using System.Threading;
 using Microsoft.VisualStudio.TestTools.UnitTesting;
 using Moq;
+using Renci.SshNet.Abstractions;
 using Renci.SshNet.Channels;
 using Renci.SshNet.Common;
 
@@ -27,8 +27,9 @@ namespace Renci.SshNet.Tests.Classes
         private Socket _client;
         private IPEndPoint _remoteEndpoint;
         private string _userName;
-        private TimeSpan _expectedElapsedTime;
-        private TimeSpan _elapsedTimeOfStop;
+        private TimeSpan _bindSleepTime;
+        private ManualResetEvent _channelBindStarted;
+        private ManualResetEvent _channelBindCompleted;
 
         [TestInitialize]
         public void Setup()
@@ -50,9 +51,26 @@ namespace Renci.SshNet.Tests.Classes
                 _forwardedPort.Dispose();
                 _forwardedPort = null;
             }
+            if (_channelBindStarted != null)
+            {
+                _channelBindStarted.Dispose();
+                _channelBindStarted = null;
+            }
+            if (_channelBindCompleted != null)
+            {
+                _channelBindCompleted.Dispose();
+                _channelBindCompleted = null;
+            }
         }
 
-        protected void Arrange()
+        private void CreateMocks()
+        {
+            _connectionInfoMock = new Mock<IConnectionInfo>(MockBehavior.Strict);
+            _sessionMock = new Mock<ISession>(MockBehavior.Strict);
+            _channelMock = new Mock<IChannelDirectTcpip>(MockBehavior.Strict);
+        }
+
+        private void SetupData()
         {
             var random = new Random();
 
@@ -60,62 +78,65 @@ namespace Renci.SshNet.Tests.Classes
             _exceptionRegister = new List<ExceptionEventArgs>();
             _endpoint = new IPEndPoint(IPAddress.Loopback, 8122);
             _remoteEndpoint = new IPEndPoint(IPAddress.Parse("193.168.1.5"), random.Next(IPEndPoint.MinPort, IPEndPoint.MaxPort));
-            _expectedElapsedTime = TimeSpan.FromMilliseconds(random.Next(100, 500));
+            _bindSleepTime = TimeSpan.FromMilliseconds(random.Next(100, 500));
             _userName = random.Next().ToString(CultureInfo.InvariantCulture);
+            _channelBindStarted = new ManualResetEvent(false);
+            _channelBindCompleted = new ManualResetEvent(false);
 
-            _connectionInfoMock = new Mock<IConnectionInfo>(MockBehavior.Strict);
-            _sessionMock = new Mock<ISession>(MockBehavior.Strict);
-            _channelMock = new Mock<IChannelDirectTcpip>(MockBehavior.Strict);
-
-            _forwardedPort = new ForwardedPortDynamic(_endpoint.Address.ToString(), (uint) _endpoint.Port);
+            _forwardedPort = new ForwardedPortDynamic(_endpoint.Address.ToString(), (uint)_endpoint.Port);
+            _forwardedPort.Closing += (sender, args) => _closingRegister.Add(args);
+            _forwardedPort.Exception += (sender, args) => _exceptionRegister.Add(args);
+            _forwardedPort.Session = _sessionMock.Object;
 
-            Socket handlerSocket = null;
+            _client = new Socket(_endpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp)
+                {
+                    ReceiveTimeout = 100,
+                    SendTimeout = 100,
+                    SendBufferSize = 0
+                };
+        }
 
+        private void SetupMocks()
+        {
             _connectionInfoMock.Setup(p => p.Timeout).Returns(TimeSpan.FromSeconds(15));
             _sessionMock.Setup(p => p.IsConnected).Returns(true);
             _sessionMock.Setup(p => p.ConnectionInfo).Returns(_connectionInfoMock.Object);
             _sessionMock.Setup(p => p.CreateChannelDirectTcpip()).Returns(_channelMock.Object);
-            _channelMock.Setup(p => p.Open(_remoteEndpoint.Address.ToString(), (uint) _remoteEndpoint.Port, _forwardedPort, It.IsAny<Socket>())).Callback<string, uint, IForwardedPort, Socket>((address, port, forwardedPort, socket) => handlerSocket = socket);
+            _channelMock.Setup(p => p.Open(_remoteEndpoint.Address.ToString(), (uint)_remoteEndpoint.Port, _forwardedPort, It.IsAny<Socket>()));
             _channelMock.Setup(p => p.IsOpen).Returns(true);
             _channelMock.Setup(p => p.Bind()).Callback(() =>
                 {
-                    Thread.Sleep(_expectedElapsedTime);
-                    if (handlerSocket != null && handlerSocket.Connected)
-                        handlerSocket.Shutdown(SocketShutdown.Both);
+                    _channelBindStarted.Set();
+                    Thread.Sleep(_bindSleepTime);
+                    _channelBindCompleted.Set();
                 });
             _channelMock.Setup(p => p.Close());
             _channelMock.Setup(p => p.Dispose());
+        }
 
-            _forwardedPort.Closing += (sender, args) => _closingRegister.Add(args);
-            _forwardedPort.Exception += (sender, args) => _exceptionRegister.Add(args);
-            _forwardedPort.Session = _sessionMock.Object;
-            _forwardedPort.Start();
+        protected void Arrange()
+        {
+            CreateMocks();
+            SetupData();
+            SetupMocks();
 
-            _client = new Socket(_endpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp)
-                {
-                    ReceiveTimeout = 500,
-                    SendTimeout = 500,
-                    SendBufferSize = 0
-                };
+            // start port
+            _forwardedPort.Start();
+            // connect to port
             EstablishSocks4Connection(_client);
+            // wait until SOCKS client is bound to channel
+            Assert.IsTrue(_channelBindStarted.WaitOne(TimeSpan.FromMilliseconds(200)));
         }
 
         protected void Act()
         {
-            var stopwatch = new Stopwatch();
-            stopwatch.Start();
-
             _forwardedPort.Stop();
-
-            stopwatch.Stop();
-            _elapsedTimeOfStop = stopwatch.Elapsed;
         }
 
         [TestMethod]
-        public void StopShouldBlockUntilBoundChannelHasClosed()
+        public void ShouldBlockUntilBindHasCompleted()
         {
-            Assert.IsTrue(_elapsedTimeOfStop >=_expectedElapsedTime);
-            Assert.IsTrue(_elapsedTimeOfStop < _expectedElapsedTime.Add(TimeSpan.FromMilliseconds(200)));
+            Assert.IsTrue(_channelBindCompleted.WaitOne(0));
         }
 
         [TestMethod]
@@ -142,16 +163,24 @@ namespace Renci.SshNet.Tests.Classes
         }
 
         [TestMethod]
-        public void ExistingConnectionShouldBeClosed()
+        public void BoundClientShouldNotBeClosed()
         {
+            // the forwarded port itself does not close the client connection; when the channel is closed properly
+            // it's the channel that will take care of closing the client connection
+            //
+            // we'll check if the client connection is still alive by attempting to receive, which should time out
+            // as the forwarded port (or its channel) are not sending anything
+
+            var buffer = new byte[1];
+
             try
             {
-                _client.Send(new byte[] { 0x0a }, 0, 1, SocketFlags.None);
+                _client.Receive(buffer);
                 Assert.Fail();
             }
             catch (SocketException ex)
             {
-                Assert.AreEqual(SocketError.ConnectionReset, ex.SocketErrorCode);
+                Assert.AreEqual(SocketError.TimedOut, ex.SocketErrorCode);
             }
         }
 
@@ -200,8 +229,9 @@ namespace Renci.SshNet.Tests.Classes
             // terminate user name with null
             client.Send(new byte[] { 0x00 }, 0, 1, SocketFlags.None);
 
-            var buffer = new byte[16];
-            client.Receive(buffer, 0, buffer.Length, SocketFlags.None);
+            var buffer = new byte[8];
+            var bytesRead = SocketAbstraction.Read(client, buffer, 0, buffer.Length, TimeSpan.FromMilliseconds(500));
+            Assert.AreEqual(buffer.Length, bytesRead);
         }
     }
 }

+ 38 - 31
src/Renci.SshNet.Tests/Classes/ForwardedPortLocalTest_Dispose_PortStarted_ChannelBound.cs

@@ -1,6 +1,5 @@
 using System;
 using System.Collections.Generic;
-using System.Diagnostics;
 using System.Net;
 using System.Net.Sockets;
 using System.Threading;
@@ -23,10 +22,9 @@ namespace Renci.SshNet.Tests.Classes
         private IPEndPoint _localEndpoint;
         private IPEndPoint _remoteEndpoint;
         private Socket _client;
-        private TimeSpan _expectedElapsedTime;
-        private TimeSpan _elapsedTimeOfStop;
-        private Stopwatch _stopwatch;
-
+        private TimeSpan _bindSleepTime;
+        private ManualResetEvent _channelBindStarted;
+        private ManualResetEvent _channelBindCompleted;
 
         [TestInitialize]
         public void Setup()
@@ -48,6 +46,16 @@ namespace Renci.SshNet.Tests.Classes
                 _forwardedPort.Dispose();
                 _forwardedPort = null;
             }
+            if (_channelBindStarted != null)
+            {
+                _channelBindStarted.Dispose();
+                _channelBindStarted = null;
+            }
+            if (_channelBindCompleted != null)
+            {
+                _channelBindCompleted.Dispose();
+                _channelBindCompleted = null;
+            }
         }
 
         protected void Arrange()
@@ -56,28 +64,26 @@ namespace Renci.SshNet.Tests.Classes
             _closingRegister = new List<EventArgs>();
             _exceptionRegister = new List<ExceptionEventArgs>();
             _localEndpoint = new IPEndPoint(IPAddress.Loopback, 8122);
-            _remoteEndpoint = new IPEndPoint(IPAddress.Parse("193.168.1.5"),
-                random.Next(IPEndPoint.MinPort, IPEndPoint.MaxPort));
-            _expectedElapsedTime = TimeSpan.FromMilliseconds(random.Next(100, 500));
-            _forwardedPort = new ForwardedPortLocal(_localEndpoint.Address.ToString(), (uint)_localEndpoint.Port,
-                _remoteEndpoint.Address.ToString(), (uint)_remoteEndpoint.Port);
+            _remoteEndpoint = new IPEndPoint(IPAddress.Parse("193.168.1.5"), random.Next(IPEndPoint.MinPort, IPEndPoint.MaxPort));
+            _bindSleepTime = TimeSpan.FromMilliseconds(random.Next(100, 500));
+            _forwardedPort = new ForwardedPortLocal(_localEndpoint.Address.ToString(), (uint) _localEndpoint.Port, _remoteEndpoint.Address.ToString(), (uint) _remoteEndpoint.Port);
+            _channelBindStarted = new ManualResetEvent(false);
+            _channelBindCompleted = new ManualResetEvent(false);
 
             _connectionInfoMock = new Mock<IConnectionInfo>(MockBehavior.Strict);
             _sessionMock = new Mock<ISession>(MockBehavior.Strict);
             _channelMock = new Mock<IChannelDirectTcpip>(MockBehavior.Strict);
 
-            Socket handlerSocket = null;
-
             _connectionInfoMock.Setup(p => p.Timeout).Returns(TimeSpan.FromSeconds(15));
             _sessionMock.Setup(p => p.IsConnected).Returns(true);
             _sessionMock.Setup(p => p.ConnectionInfo).Returns(_connectionInfoMock.Object);
             _sessionMock.Setup(p => p.CreateChannelDirectTcpip()).Returns(_channelMock.Object);
-            _channelMock.Setup(p => p.Open(_forwardedPort.Host, _forwardedPort.Port, _forwardedPort, It.IsAny<Socket>())).Callback<string, uint, IForwardedPort, Socket>((address, port, forwardedPort, socket) => handlerSocket = socket);
+            _channelMock.Setup(p => p.Open(_forwardedPort.Host, _forwardedPort.Port, _forwardedPort, It.IsAny<Socket>()));
             _channelMock.Setup(p => p.Bind()).Callback(() =>
                 {
-                    Thread.Sleep(_expectedElapsedTime);
-                    if (handlerSocket != null && handlerSocket.Connected)
-                        handlerSocket.Shutdown(SocketShutdown.Both);
+                    _channelBindStarted.Set();
+                    Thread.Sleep(_bindSleepTime);
+                    _channelBindCompleted.Set();
                 });
             _channelMock.Setup(p => p.Close());
             _channelMock.Setup(p => p.Dispose());
@@ -89,33 +95,26 @@ namespace Renci.SshNet.Tests.Classes
 
             _client = new Socket(_localEndpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp)
                 {
-                    ReceiveTimeout = 500,
+                    ReceiveTimeout = 100,
                     SendTimeout = 500,
                     SendBufferSize = 0
                 };
 
-            _stopwatch = new Stopwatch();
-            _stopwatch.Start();
-
             _client.Connect(_localEndpoint);
 
-            // give client socket time to establish connection
-            Thread.Sleep(50);
+            // wait for SOCKS client to bind to channel
+            Assert.IsTrue(_channelBindStarted.WaitOne(TimeSpan.FromMilliseconds(200)));
         }
 
         protected void Act()
         {
             _forwardedPort.Dispose();
-
-            _stopwatch.Stop();
-            _elapsedTimeOfStop = _stopwatch.Elapsed;
         }
 
         [TestMethod]
-        public void StopShouldBlockUntilBoundChannelHasClosed()
+        public void ShouldBlockUntilBindHasCompleted()
         {
-            Assert.IsTrue(_elapsedTimeOfStop >= _expectedElapsedTime, string.Format("Expected {0} or greater but was {1}.", _expectedElapsedTime.TotalMilliseconds, _elapsedTimeOfStop.TotalMilliseconds));
-            Assert.IsTrue(_elapsedTimeOfStop < _expectedElapsedTime.Add(TimeSpan.FromMilliseconds(200)));
+            Assert.IsTrue(_channelBindCompleted.WaitOne(0));
         }
 
         [TestMethod]
@@ -142,16 +141,24 @@ namespace Renci.SshNet.Tests.Classes
         }
 
         [TestMethod]
-        public void ExistingConnectionShouldBeClosed()
+        public void BoundClientShouldNotBeClosed()
         {
+            // the forwarded port itself does not close the client connection; when the channel is closed properly
+            // it's the channel that will take care of closing the client connection
+            //
+            // we'll check if the client connection is still alive by attempting to receive, which should time out
+            // as the forwarded port (or its channel) are not sending anything
+
+            var buffer = new byte[1];
+
             try
             {
-                _client.Send(new byte[] { 0x0a }, 0, 1, SocketFlags.None);
+                _client.Receive(buffer);
                 Assert.Fail();
             }
             catch (SocketException ex)
             {
-                Assert.AreEqual(SocketError.ConnectionReset, ex.SocketErrorCode);
+                Assert.AreEqual(SocketError.TimedOut, ex.SocketErrorCode);
             }
         }
 

+ 67 - 44
src/Renci.SshNet.Tests/Classes/ForwardedPortLocalTest_Stop_PortStarted_ChannelBound.cs

@@ -1,6 +1,5 @@
 using System;
 using System.Collections.Generic;
-using System.Diagnostics;
 using System.Net;
 using System.Net.Sockets;
 using System.Threading;
@@ -23,9 +22,9 @@ namespace Renci.SshNet.Tests.Classes
         private IPEndPoint _localEndpoint;
         private IPEndPoint _remoteEndpoint;
         private Socket _client;
-        private TimeSpan _expectedElapsedTime;
-        private TimeSpan _elapsedTimeOfStop;
-        private Stopwatch _stopwatch;
+        private TimeSpan _bindSleepTime;
+        private ManualResetEvent _channelBound;
+        private ManualResetEvent _channelBindCompleted;
 
         [TestInitialize]
         public void Setup()
@@ -47,74 +46,90 @@ namespace Renci.SshNet.Tests.Classes
                 _forwardedPort.Dispose();
                 _forwardedPort = null;
             }
+            if (_channelBound != null)
+            {
+                _channelBound.Dispose();
+                _channelBound = null;
+            }
+            if (_channelBindCompleted != null)
+            {
+                _channelBindCompleted.Dispose();
+                _channelBindCompleted = null;
+            }
         }
 
-        protected void Arrange()
+        private void CreateMocks()
+        {
+            _connectionInfoMock = new Mock<IConnectionInfo>(MockBehavior.Strict);
+            _sessionMock = new Mock<ISession>(MockBehavior.Strict);
+            _channelMock = new Mock<IChannelDirectTcpip>(MockBehavior.Strict);
+        }
+
+        private void SetupData()
         {
             var random = new Random();
+
             _closingRegister = new List<EventArgs>();
             _exceptionRegister = new List<ExceptionEventArgs>();
             _localEndpoint = new IPEndPoint(IPAddress.Loopback, 8122);
-            _remoteEndpoint = new IPEndPoint(IPAddress.Parse("193.168.1.5"),
-                random.Next(IPEndPoint.MinPort, IPEndPoint.MaxPort));
-            _expectedElapsedTime = TimeSpan.FromMilliseconds(random.Next(100, 500));
-            _forwardedPort = new ForwardedPortLocal(_localEndpoint.Address.ToString(), (uint)_localEndpoint.Port,
-                _remoteEndpoint.Address.ToString(), (uint)_remoteEndpoint.Port);
+            _remoteEndpoint = new IPEndPoint(IPAddress.Parse("193.168.1.5"), random.Next(IPEndPoint.MinPort, IPEndPoint.MaxPort));
+            _bindSleepTime = TimeSpan.FromMilliseconds(random.Next(100, 500));
+            _channelBound = new ManualResetEvent(false);
+            _channelBindCompleted = new ManualResetEvent(false);
 
-            _connectionInfoMock = new Mock<IConnectionInfo>(MockBehavior.Strict);
-            _sessionMock = new Mock<ISession>(MockBehavior.Strict);
-            _channelMock = new Mock<IChannelDirectTcpip>(MockBehavior.Strict);
+            _forwardedPort = new ForwardedPortLocal(_localEndpoint.Address.ToString(), (uint) _localEndpoint.Port, _remoteEndpoint.Address.ToString(), (uint) _remoteEndpoint.Port);
+            _forwardedPort.Closing += (sender, args) => _closingRegister.Add(args);
+            _forwardedPort.Exception += (sender, args) => _exceptionRegister.Add(args);
+            _forwardedPort.Session = _sessionMock.Object;
 
-            Socket handlerSocket = null;
+            _client = new Socket(_localEndpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp)
+                {
+                    ReceiveTimeout = 100,
+                    SendTimeout = 100,
+                    SendBufferSize = 0
+                };
+        }
 
+        private void SetupMocks()
+        {
             _connectionInfoMock.Setup(p => p.Timeout).Returns(TimeSpan.FromSeconds(15));
             _sessionMock.Setup(p => p.IsConnected).Returns(true);
             _sessionMock.Setup(p => p.ConnectionInfo).Returns(_connectionInfoMock.Object);
             _sessionMock.Setup(p => p.CreateChannelDirectTcpip()).Returns(_channelMock.Object);
-            _channelMock.Setup(p => p.Open(_forwardedPort.Host, _forwardedPort.Port, _forwardedPort, It.IsAny<Socket>())).Callback<string, uint, IForwardedPort, Socket>((address, port, forwardedPort, socket) => handlerSocket = socket);
+            _channelMock.Setup(p => p.Open(_forwardedPort.Host, _forwardedPort.Port, _forwardedPort, It.IsAny<Socket>()));
             _channelMock.Setup(p => p.Bind()).Callback(() =>
                 {
-                    Thread.Sleep(_expectedElapsedTime);
-                    if (handlerSocket != null && handlerSocket.Connected)
-                        handlerSocket.Shutdown(SocketShutdown.Both);
+                    _channelBound.Set();
+                    Thread.Sleep(_bindSleepTime);
+                    _channelBindCompleted.Set();
                 });
             _channelMock.Setup(p => p.Close());
             _channelMock.Setup(p => p.Dispose());
+        }
 
-            _forwardedPort.Closing += (sender, args) => _closingRegister.Add(args);
-            _forwardedPort.Exception += (sender, args) => _exceptionRegister.Add(args);
-            _forwardedPort.Session = _sessionMock.Object;
-            _forwardedPort.Start();
-
-            _client = new Socket(_localEndpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp)
-                {
-                    ReceiveTimeout = 500,
-                    SendTimeout = 500,
-                    SendBufferSize = 0
-                };
-
-            _stopwatch = new Stopwatch();
-            _stopwatch.Start();
+        protected void Arrange()
+        {
+            CreateMocks();
+            SetupData();
+            SetupMocks();
 
+            // start port
+            _forwardedPort.Start();
+            // connect to port
             _client.Connect(_localEndpoint);
-
-            // give client socket time to establish connection
-            Thread.Sleep(50);
+            // wait for SOCKS client to bind to channel
+            Assert.IsTrue(_channelBound.WaitOne(TimeSpan.FromMilliseconds(200)));
         }
 
         protected void Act()
         {
             _forwardedPort.Stop();
-
-            _stopwatch.Stop();
-            _elapsedTimeOfStop = _stopwatch.Elapsed;
         }
 
         [TestMethod]
-        public void StopShouldBlockUntilBoundChannelHasClosed()
+        public void ShouldBlockUntilBindHasCompleted()
         {
-            Assert.IsTrue(_elapsedTimeOfStop >= _expectedElapsedTime, string.Format("Expected {0} or greater but was {1}.", _expectedElapsedTime.TotalMilliseconds, _elapsedTimeOfStop.TotalMilliseconds));
-            Assert.IsTrue(_elapsedTimeOfStop < _expectedElapsedTime.Add(TimeSpan.FromMilliseconds(200)));
+            Assert.IsTrue(_channelBindCompleted.WaitOne(0));
         }
 
         [TestMethod]
@@ -141,16 +156,24 @@ namespace Renci.SshNet.Tests.Classes
         }
 
         [TestMethod]
-        public void ExistingConnectionShouldBeClosed()
+        public void BoundClientShouldNotBeClosed()
         {
+            // the forwarded port itself does not close the client connection; when the channel is closed properly
+            // it's the channel that will take care of closing the client connection
+            //
+            // we'll check if the client connection is still alive by attempting to receive, which should time out
+            // as the forwarded port (or its channel) are not sending anything
+
+            var buffer = new byte[1];
+
             try
             {
-                _client.Send(new byte[] { 0x0a }, 0, 1, SocketFlags.None);
+                _client.Receive(buffer);
                 Assert.Fail();
             }
             catch (SocketException ex)
             {
-                Assert.AreEqual(SocketError.ConnectionReset, ex.SocketErrorCode);
+                Assert.AreEqual(SocketError.TimedOut, ex.SocketErrorCode);
             }
         }
 

+ 4 - 0
src/Renci.SshNet.Tests/Classes/ForwardedPortRemoteTest.cs

@@ -76,6 +76,7 @@ namespace Renci.SshNet.Tests.Classes
         ///A test for Stop
         ///</summary>
         [TestMethod]
+        [Ignore] // placeholder
         public void StopTest()
         {
             uint boundPort = 0; // TODO: Initialize to an appropriate value
@@ -110,6 +111,7 @@ namespace Renci.SshNet.Tests.Classes
         ///A test for Dispose
         ///</summary>
         [TestMethod]
+        [Ignore] // placeholder
         public void DisposeTest()
         {
             uint boundPort = 0; // TODO: Initialize to an appropriate value
@@ -124,6 +126,7 @@ namespace Renci.SshNet.Tests.Classes
         ///A test for ForwardedPortRemote Constructor
         ///</summary>
         [TestMethod]
+        [Ignore] // placeholder
         public void ForwardedPortRemoteConstructorTest()
         {
             string boundHost = string.Empty; // TODO: Initialize to an appropriate value
@@ -138,6 +141,7 @@ namespace Renci.SshNet.Tests.Classes
         ///A test for ForwardedPortRemote Constructor
         ///</summary>
         [TestMethod]
+        [Ignore] // placeholder
         public void ForwardedPortRemoteConstructorTest1()
         {
             uint boundPort = 0; // TODO: Initialize to an appropriate value

+ 67 - 33
src/Renci.SshNet.Tests/Classes/ForwardedPortRemoteTest_Dispose_PortStarted_ChannelBound.cs

@@ -1,6 +1,5 @@
 using System;
 using System.Collections.Generic;
-using System.Diagnostics;
 using System.Globalization;
 using System.Net;
 using System.Threading;
@@ -22,8 +21,7 @@ namespace Renci.SshNet.Tests.Classes
         private IList<ExceptionEventArgs> _exceptionRegister;
         private IPEndPoint _bindEndpoint;
         private IPEndPoint _remoteEndpoint;
-        private TimeSpan _expectedElapsedTime;
-        private TimeSpan _elapsedTimeOfStop;
+        private TimeSpan _bindSleepTime;
         private uint _remoteChannelNumberWhileClosing;
         private uint _remoteWindowSizeWhileClosing;
         private uint _remotePacketSizeWhileClosing;
@@ -32,6 +30,8 @@ namespace Renci.SshNet.Tests.Classes
         private uint _remotePacketSizeStarted;
         private string _originatorAddress;
         private uint _originatorPort;
+        private ManualResetEvent _channelBindStarted;
+        private ManualResetEvent _channelBindCompleted;
 
         protected ForwardedPortRemote ForwardedPort { get; private set; }
 
@@ -39,14 +39,7 @@ namespace Renci.SshNet.Tests.Classes
         public void Setup()
         {
             Arrange();
-
-            var stopwatch = new Stopwatch();
-            stopwatch.Start();
-
             Act();
-
-            stopwatch.Stop();
-            _elapsedTimeOfStop = stopwatch.Elapsed;
         }
 
         [TestCleanup]
@@ -57,30 +50,59 @@ namespace Renci.SshNet.Tests.Classes
                 ForwardedPort.Dispose();
                 ForwardedPort = null;
             }
+
+            if (_channelBindStarted != null)
+            {
+                _channelBindStarted.Dispose();
+                _channelBindStarted = null;
+            }
+
+            if (_channelBindCompleted != null)
+            {
+                _channelBindCompleted.Dispose();
+                _channelBindCompleted = null;
+            }
         }
 
-        protected void Arrange()
+        private void CreateMocks()
+        {
+            _connectionInfoMock = new Mock<IConnectionInfo>(MockBehavior.Strict);
+            _sessionMock = new Mock<ISession>(MockBehavior.Strict);
+            _channelMock = new Mock<IChannelForwardedTcpip>(MockBehavior.Strict);
+        }
+
+        private void SetUpData()
         {
             var random = new Random();
+
             _closingRegister = new List<EventArgs>();
             _exceptionRegister = new List<ExceptionEventArgs>();
             _bindEndpoint = new IPEndPoint(IPAddress.Any, random.Next(IPEndPoint.MinPort, IPEndPoint.MaxPort));
             _remoteEndpoint = new IPEndPoint(IPAddress.Parse("193.168.1.5"), random.Next(IPEndPoint.MinPort, IPEndPoint.MaxPort));
-            _expectedElapsedTime = TimeSpan.FromMilliseconds(random.Next(100, 500));
-            ForwardedPort = new ForwardedPortRemote(_bindEndpoint.Address, (uint)_bindEndpoint.Port, _remoteEndpoint.Address, (uint)_remoteEndpoint.Port);
+            _bindSleepTime = TimeSpan.FromMilliseconds(random.Next(100, 500));
             _remoteChannelNumberWhileClosing = (uint) random.Next(0, 1000);
             _remoteWindowSizeWhileClosing = (uint) random.Next(0, int.MaxValue);
             _remotePacketSizeWhileClosing = (uint) random.Next(0, int.MaxValue);
-            _remoteChannelNumberStarted = (uint)random.Next(0, 1000);
-            _remoteWindowSizeStarted = (uint)random.Next(0, int.MaxValue);
-            _remotePacketSizeStarted = (uint)random.Next(0, int.MaxValue);
+            _remoteChannelNumberStarted = (uint) random.Next(0, 1000);
+            _remoteWindowSizeStarted = (uint) random.Next(0, int.MaxValue);
+            _remotePacketSizeStarted = (uint) random.Next(0, int.MaxValue);
             _originatorAddress = random.Next().ToString(CultureInfo.InvariantCulture);
-            _originatorPort = (uint)random.Next(0, int.MaxValue);
+            _originatorPort = (uint) random.Next(0, int.MaxValue);
+            _channelBindStarted = new ManualResetEvent(false);
+            _channelBindCompleted = new ManualResetEvent(false);
 
-            _connectionInfoMock = new Mock<IConnectionInfo>(MockBehavior.Strict);
-            _sessionMock = new Mock<ISession>(MockBehavior.Strict);
-            _channelMock = new Mock<IChannelForwardedTcpip>(MockBehavior.Strict);
+            ForwardedPort = new ForwardedPortRemote(_bindEndpoint.Address, (uint)_bindEndpoint.Port, _remoteEndpoint.Address, (uint)_remoteEndpoint.Port);
+            ForwardedPort.Closing += (sender, args) =>
+                {
+                    _closingRegister.Add(args);
+                    _sessionMock.Raise(p => p.ChannelOpenReceived += null, new MessageEventArgs<ChannelOpenMessage>(new ChannelOpenMessage(_remoteChannelNumberWhileClosing, _remoteWindowSizeWhileClosing, _remotePacketSizeWhileClosing, new ForwardedTcpipChannelInfo(ForwardedPort.BoundHost, ForwardedPort.BoundPort, _originatorAddress, _originatorPort))));
+                };
+            ForwardedPort.Exception += (sender, args) => _exceptionRegister.Add(args);
+            ForwardedPort.Session = _sessionMock.Object;
+        }
 
+        private void SetupMocks()
+        {
             _connectionInfoMock.Setup(p => p.Timeout).Returns(TimeSpan.FromSeconds(15));
             _sessionMock.Setup(p => p.IsConnected).Returns(true);
             _sessionMock.Setup(p => p.ConnectionInfo).Returns(_connectionInfoMock.Object);
@@ -107,7 +129,12 @@ namespace Renci.SshNet.Tests.Classes
                     p.Bind(
                         It.Is<IPEndPoint>(
                             ep => ep.Address.Equals(_remoteEndpoint.Address) && ep.Port == _remoteEndpoint.Port),
-                        ForwardedPort)).Callback(() => Thread.Sleep(_expectedElapsedTime));
+                        ForwardedPort)).Callback(() =>
+                            {
+                                _channelBindStarted.Set();
+                                Thread.Sleep(_bindSleepTime);
+                                _channelBindCompleted.Set();
+                            });
             _channelMock.Setup(p => p.Close());
             _channelMock.Setup(p => p.Dispose());
             _sessionMock.Setup(
@@ -116,19 +143,27 @@ namespace Renci.SshNet.Tests.Classes
                         It.Is<GlobalRequestMessage>(
                             g =>
                                 g.RequestName == GlobalRequestName.CancelTcpIpForward &&
-                                g.AddressToBind == ForwardedPort.BoundHost && g.PortToBind == ForwardedPort.BoundPort)));
-            _sessionMock.Setup(p => p.MessageListenerCompleted).Returns(new ManualResetEvent(true));
+                                g.AddressToBind == ForwardedPort.BoundHost && g.PortToBind == ForwardedPort.BoundPort))).Callback(
+                                    () =>
+                                        {
+                                            // raise event confirming that forwarded port was cancelled
+                                            _sessionMock.Raise(p => p.RequestSuccessReceived += null, new MessageEventArgs<RequestSuccessMessage>(new RequestSuccessMessage()));
+                                        });
+            _sessionMock.Setup(p => p.MessageListenerCompleted).Returns(new ManualResetEvent(false));
+        }
+
+        protected void Arrange()
+        {
+            CreateMocks();
+            SetUpData();
+            SetupMocks();
 
-            ForwardedPort.Closing += (sender, args) =>
-                {
-                    _closingRegister.Add(args);
-                    _sessionMock.Raise(p => p.ChannelOpenReceived += null, new MessageEventArgs<ChannelOpenMessage>(new ChannelOpenMessage(_remoteChannelNumberWhileClosing, _remoteWindowSizeWhileClosing, _remotePacketSizeWhileClosing, new ForwardedTcpipChannelInfo(ForwardedPort.BoundHost, ForwardedPort.BoundPort, _originatorAddress, _originatorPort))));
-                };
-            ForwardedPort.Exception += (sender, args) => _exceptionRegister.Add(args);
-            ForwardedPort.Session = _sessionMock.Object;
             ForwardedPort.Start();
 
             _sessionMock.Raise(p => p.ChannelOpenReceived += null, new MessageEventArgs<ChannelOpenMessage>(new ChannelOpenMessage(_remoteChannelNumberStarted, _remoteWindowSizeStarted, _remotePacketSizeStarted, new ForwardedTcpipChannelInfo(ForwardedPort.BoundHost, ForwardedPort.BoundPort, _originatorAddress, _originatorPort))));
+
+            // wait until channel is bound
+            Assert.IsTrue(_channelBindStarted.WaitOne(TimeSpan.FromMilliseconds(200)));
         }
 
         protected virtual void Act()
@@ -137,10 +172,9 @@ namespace Renci.SshNet.Tests.Classes
         }
 
         [TestMethod]
-        public void StopShouldBlockUntilBoundChannelHasClosed()
+        public void ShouldBlockUntilBindHasCompleted()
         {
-            Assert.IsTrue(_elapsedTimeOfStop >= _expectedElapsedTime, string.Format("Expected {0} or greater but was {1}.", _expectedElapsedTime.TotalMilliseconds, _elapsedTimeOfStop.TotalMilliseconds));
-            Assert.IsTrue(_elapsedTimeOfStop < _expectedElapsedTime.Add(TimeSpan.FromMilliseconds(200)));
+            Assert.IsTrue(_channelBindCompleted.WaitOne(0));
         }
 
         [TestMethod]

+ 6 - 6
src/Renci.SshNet.Tests/Classes/Messages/Authentication/BannerMessageTest.cs

@@ -1,21 +1,21 @@
-using Renci.SshNet.Messages.Authentication;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using System;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Renci.SshNet.Messages.Authentication;
 using Renci.SshNet.Tests.Common;
 
-namespace Renci.SshNet.Tests.Messages.Authentication
+namespace Renci.SshNet.Tests.Classes.Messages.Authentication
 {   
     /// <summary>
     ///This is a test class for BannerMessageTest and is intended
     ///to contain all BannerMessageTest Unit Tests
     ///</summary>
-    [TestClass()]
+    [TestClass]
     public class BannerMessageTest : TestBase
     {
         /// <summary>
         ///A test for BannerMessage Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void BannerMessageConstructorTest()
         {
             BannerMessage target = new BannerMessage();

+ 5 - 3
src/Renci.SshNet.Tests/Classes/Messages/Authentication/FailureMessageTest.cs

@@ -9,13 +9,14 @@ namespace Renci.SshNet.Tests.Messages.Authentication
     ///This is a test class for FailureMessageTest and is intended
     ///to contain all FailureMessageTest Unit Tests
     ///</summary>
-    [TestClass()]
+    [TestClass]
     public class FailureMessageTest : TestBase
     {
         /// <summary>
         ///A test for FailureMessage Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void FailureMessageConstructorTest()
         {
             FailureMessage target = new FailureMessage();
@@ -25,7 +26,8 @@ namespace Renci.SshNet.Tests.Messages.Authentication
         /// <summary>
         ///A test for AllowedAuthentications
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void AllowedAuthenticationsTest()
         {
             FailureMessage target = new FailureMessage(); // TODO: Initialize to an appropriate value

+ 9 - 6
src/Renci.SshNet.Tests/Classes/Messages/Authentication/RequestMessagePublicKeyTest.cs

@@ -16,7 +16,8 @@ namespace Renci.SshNet.Tests.Messages.Authentication
         /// <summary>
         ///A test for RequestMessagePublicKey Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void RequestMessagePublicKeyConstructorTest()
         {
             ServiceName serviceName = new ServiceName(); // TODO: Initialize to an appropriate value
@@ -30,7 +31,8 @@ namespace Renci.SshNet.Tests.Messages.Authentication
         /// <summary>
         ///A test for RequestMessagePublicKey Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void RequestMessagePublicKeyConstructorTest1()
         {
             ServiceName serviceName = new ServiceName(); // TODO: Initialize to an appropriate value
@@ -45,7 +47,8 @@ namespace Renci.SshNet.Tests.Messages.Authentication
         /// <summary>
         ///A test for MethodName
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void MethodNameTest()
         {
             ServiceName serviceName = new ServiceName(); // TODO: Initialize to an appropriate value
@@ -61,7 +64,8 @@ namespace Renci.SshNet.Tests.Messages.Authentication
         /// <summary>
         ///A test for Signature
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void SignatureTest()
         {
             ServiceName serviceName = new ServiceName(); // TODO: Initialize to an appropriate value
@@ -70,9 +74,8 @@ namespace Renci.SshNet.Tests.Messages.Authentication
             byte[] keyData = null; // TODO: Initialize to an appropriate value
             RequestMessagePublicKey target = new RequestMessagePublicKey(serviceName, username, keyAlgorithmName, keyData); // TODO: Initialize to an appropriate value
             byte[] expected = null; // TODO: Initialize to an appropriate value
-            byte[] actual;
             target.Signature = expected;
-            actual = target.Signature;
+            var actual = target.Signature;
             Assert.AreEqual(expected, actual);
             Assert.Inconclusive("Verify the correctness of this test method.");
         }

+ 6 - 6
src/Renci.SshNet.Tests/Classes/Messages/Authentication/SuccessMessageTest.cs

@@ -1,10 +1,9 @@
-using Renci.SshNet.Messages.Authentication;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using System;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Renci.SshNet.Messages.Authentication;
 using Renci.SshNet.Tests.Common;
 
-namespace Renci.SshNet.Tests.Messages.Authentication
-{   
+namespace Renci.SshNet.Tests.Classes.Messages.Authentication
+{
     /// <summary>
     ///This is a test class for SuccessMessageTest and is intended
     ///to contain all SuccessMessageTest Unit Tests
@@ -15,7 +14,8 @@ namespace Renci.SshNet.Tests.Messages.Authentication
         /// <summary>
         ///A test for SuccessMessage Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void SuccessMessageConstructorTest()
         {
             SuccessMessage target = new SuccessMessage();

+ 5 - 3
src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelCloseMessageTest.cs

@@ -9,13 +9,14 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection
     ///This is a test class for ChannelCloseMessageTest and is intended
     ///to contain all ChannelCloseMessageTest Unit Tests
     ///</summary>
-    [TestClass()]
+    [TestClass]
     public class ChannelCloseMessageTest : TestBase
     {
         /// <summary>
         ///A test for ChannelCloseMessage Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void ChannelCloseMessageConstructorTest()
         {
             ChannelCloseMessage target = new ChannelCloseMessage();
@@ -25,7 +26,8 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection
         /// <summary>
         ///A test for ChannelCloseMessage Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void ChannelCloseMessageConstructorTest1()
         {
             uint localChannelNumber = 0; // TODO: Initialize to an appropriate value

+ 5 - 3
src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelEofMessageTest.cs

@@ -9,13 +9,14 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection
     ///This is a test class for ChannelEofMessageTest and is intended
     ///to contain all ChannelEofMessageTest Unit Tests
     ///</summary>
-    [TestClass()]
+    [TestClass]
     public class ChannelEofMessageTest : TestBase
     {
         /// <summary>
         ///A test for ChannelEofMessage Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void ChannelEofMessageConstructorTest()
         {
             ChannelEofMessage target = new ChannelEofMessage();
@@ -25,7 +26,8 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection
         /// <summary>
         ///A test for ChannelEofMessage Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void ChannelEofMessageConstructorTest1()
         {
             uint localChannelNumber = 0; // TODO: Initialize to an appropriate value

+ 5 - 3
src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelExtendedDataMessageTest.cs

@@ -9,13 +9,14 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection
     ///This is a test class for ChannelExtendedDataMessageTest and is intended
     ///to contain all ChannelExtendedDataMessageTest Unit Tests
     ///</summary>
-    [TestClass()]
+    [TestClass]
     public class ChannelExtendedDataMessageTest : TestBase
     {
         /// <summary>
         ///A test for ChannelExtendedDataMessage Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void ChannelExtendedDataMessageConstructorTest()
         {
             ChannelExtendedDataMessage target = new ChannelExtendedDataMessage();
@@ -25,7 +26,8 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection
         /// <summary>
         ///A test for ChannelExtendedDataMessage Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void ChannelExtendedDataMessageConstructorTest1()
         {
             uint localChannelNumber = 0; // TODO: Initialize to an appropriate value

+ 5 - 3
src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelFailureMessageTest.cs

@@ -9,13 +9,14 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection
     ///This is a test class for ChannelFailureMessageTest and is intended
     ///to contain all ChannelFailureMessageTest Unit Tests
     ///</summary>
-    [TestClass()]
+    [TestClass]
     public class ChannelFailureMessageTest : TestBase
     {
         /// <summary>
         ///A test for ChannelFailureMessage Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void ChannelFailureMessageConstructorTest()
         {
             ChannelFailureMessage target = new ChannelFailureMessage();
@@ -25,7 +26,8 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection
         /// <summary>
         ///A test for ChannelFailureMessage Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void ChannelFailureMessageConstructorTest1()
         {
             uint localChannelNumber = 0; // TODO: Initialize to an appropriate value

+ 5 - 3
src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelOpenConfirmationMessageTest.cs

@@ -9,13 +9,14 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection
     ///This is a test class for ChannelOpenConfirmationMessageTest and is intended
     ///to contain all ChannelOpenConfirmationMessageTest Unit Tests
     ///</summary>
-    [TestClass()]
+    [TestClass]
     public class ChannelOpenConfirmationMessageTest : TestBase
     {
         /// <summary>
         ///A test for ChannelOpenConfirmationMessage Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void ChannelOpenConfirmationMessageConstructorTest()
         {
             ChannelOpenConfirmationMessage target = new ChannelOpenConfirmationMessage();
@@ -25,7 +26,8 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection
         /// <summary>
         ///A test for ChannelOpenConfirmationMessage Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void ChannelOpenConfirmationMessageConstructorTest1()
         {
             uint localChannelNumber = 0; // TODO: Initialize to an appropriate value

+ 5 - 3
src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelOpenFailureMessageTest.cs

@@ -9,13 +9,14 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection
     ///This is a test class for ChannelOpenFailureMessageTest and is intended
     ///to contain all ChannelOpenFailureMessageTest Unit Tests
     ///</summary>
-    [TestClass()]
+    [TestClass]
     public class ChannelOpenFailureMessageTest : TestBase
     {
         /// <summary>
         ///A test for ChannelOpenFailureMessage Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void ChannelOpenFailureMessageConstructorTest()
         {
             ChannelOpenFailureMessage target = new ChannelOpenFailureMessage();
@@ -25,7 +26,8 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection
         /// <summary>
         ///A test for ChannelOpenFailureMessage Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void ChannelOpenFailureMessageConstructorTest1()
         {
             uint localChannelNumber = 0; // TODO: Initialize to an appropriate value

+ 5 - 3
src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelRequest/EndOfWriteRequestInfoTest.cs

@@ -9,13 +9,14 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection
     ///This is a test class for EndOfWriteRequestInfoTest and is intended
     ///to contain all EndOfWriteRequestInfoTest Unit Tests
     ///</summary>
-    [TestClass()]
+    [TestClass]
     public class EndOfWriteRequestInfoTest : TestBase
     {
         /// <summary>
         ///A test for EndOfWriteRequestInfo Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void EndOfWriteRequestInfoConstructorTest()
         {
             EndOfWriteRequestInfo target = new EndOfWriteRequestInfo();
@@ -25,7 +26,8 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection
         /// <summary>
         ///A test for RequestName
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void RequestNameTest()
         {
             EndOfWriteRequestInfo target = new EndOfWriteRequestInfo(); // TODO: Initialize to an appropriate value

+ 5 - 3
src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelRequest/KeepAliveRequestInfoTest.cs

@@ -9,13 +9,14 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection
     ///This is a test class for KeepAliveRequestInfoTest and is intended
     ///to contain all KeepAliveRequestInfoTest Unit Tests
     ///</summary>
-    [TestClass()]
+    [TestClass]
     public class KeepAliveRequestInfoTest : TestBase
     {
         /// <summary>
         ///A test for KeepAliveRequestInfo Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void KeepAliveRequestInfoConstructorTest()
         {
             KeepAliveRequestInfo target = new KeepAliveRequestInfo();
@@ -25,7 +26,8 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection
         /// <summary>
         ///A test for RequestName
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void RequestNameTest()
         {
             KeepAliveRequestInfo target = new KeepAliveRequestInfo(); // TODO: Initialize to an appropriate value

+ 5 - 3
src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelSuccessMessageTest.cs

@@ -9,13 +9,14 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection
     ///This is a test class for ChannelSuccessMessageTest and is intended
     ///to contain all ChannelSuccessMessageTest Unit Tests
     ///</summary>
-    [TestClass()]
+    [TestClass]
     public class ChannelSuccessMessageTest : TestBase
     {
         /// <summary>
         ///A test for ChannelSuccessMessage Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void ChannelSuccessMessageConstructorTest()
         {
             ChannelSuccessMessage target = new ChannelSuccessMessage();
@@ -25,7 +26,8 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection
         /// <summary>
         ///A test for ChannelSuccessMessage Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void ChannelSuccessMessageConstructorTest1()
         {
             uint localChannelNumber = 0; // TODO: Initialize to an appropriate value

+ 5 - 3
src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelWindowAdjustMessageTest.cs

@@ -9,13 +9,14 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection
     ///This is a test class for ChannelWindowAdjustMessageTest and is intended
     ///to contain all ChannelWindowAdjustMessageTest Unit Tests
     ///</summary>
-    [TestClass()]
+    [TestClass]
     public class ChannelWindowAdjustMessageTest : TestBase
     {
         /// <summary>
         ///A test for ChannelWindowAdjustMessage Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void ChannelWindowAdjustMessageConstructorTest()
         {
             ChannelWindowAdjustMessage target = new ChannelWindowAdjustMessage();
@@ -25,7 +26,8 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection
         /// <summary>
         ///A test for ChannelWindowAdjustMessage Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void ChannelWindowAdjustMessageConstructorTest1()
         {
             uint localChannelNumber = 0; // TODO: Initialize to an appropriate value

+ 5 - 3
src/Renci.SshNet.Tests/Classes/Messages/Connection/GlobalRequestMessageTest.cs

@@ -9,13 +9,14 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection
     ///This is a test class for GlobalRequestMessageTest and is intended
     ///to contain all GlobalRequestMessageTest Unit Tests
     ///</summary>
-    [TestClass()]
+    [TestClass]
     public class GlobalRequestMessageTest : TestBase
     {
         /// <summary>
         ///A test for GlobalRequestMessage Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void GlobalRequestMessageConstructorTest()
         {
             GlobalRequestMessage target = new GlobalRequestMessage();
@@ -25,7 +26,8 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection
         /// <summary>
         ///A test for GlobalRequestMessage Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void GlobalRequestMessageConstructorTest2()
         {
             GlobalRequestName requestName = new GlobalRequestName(); // TODO: Initialize to an appropriate value

+ 3 - 3
src/Renci.SshNet.Tests/Classes/Messages/Connection/RequestFailureMessageTest.cs

@@ -1,6 +1,5 @@
 using Renci.SshNet.Messages.Connection;
 using Microsoft.VisualStudio.TestTools.UnitTesting;
-using System;
 using Renci.SshNet.Tests.Common;
 
 namespace Renci.SshNet.Tests.Classes.Messages.Connection
@@ -9,13 +8,14 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection
     ///This is a test class for RequestFailureMessageTest and is intended
     ///to contain all RequestFailureMessageTest Unit Tests
     ///</summary>
-    [TestClass()]
+    [TestClass]
     public class RequestFailureMessageTest : TestBase
     {
         /// <summary>
         ///A test for RequestFailureMessage Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void RequestFailureMessageConstructorTest()
         {
             RequestFailureMessage target = new RequestFailureMessage();

+ 5 - 3
src/Renci.SshNet.Tests/Classes/Messages/Connection/RequestSuccessMessageTest.cs

@@ -9,13 +9,14 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection
     ///This is a test class for RequestSuccessMessageTest and is intended
     ///to contain all RequestSuccessMessageTest Unit Tests
     ///</summary>
-    [TestClass()]
+    [TestClass]
     public class RequestSuccessMessageTest : TestBase
     {
         /// <summary>
         ///A test for RequestSuccessMessage Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void RequestSuccessMessageConstructorTest()
         {
             RequestSuccessMessage target = new RequestSuccessMessage();
@@ -25,7 +26,8 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection
         /// <summary>
         ///A test for RequestSuccessMessage Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void RequestSuccessMessageConstructorTest1()
         {
             uint boundPort = 0; // TODO: Initialize to an appropriate value

+ 8 - 7
src/Renci.SshNet.Tests/Classes/Messages/MessageAttributeTest.cs

@@ -15,7 +15,8 @@ namespace Renci.SshNet.Tests.Classes.Messages
         /// <summary>
         ///A test for MessageAttribute Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void MessageAttributeConstructorTest()
         {
             string name = string.Empty; // TODO: Initialize to an appropriate value
@@ -27,16 +28,16 @@ namespace Renci.SshNet.Tests.Classes.Messages
         /// <summary>
         ///A test for Name
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void NameTest()
         {
             string name = string.Empty; // TODO: Initialize to an appropriate value
             byte number = 0; // TODO: Initialize to an appropriate value
             MessageAttribute target = new MessageAttribute(name, number); // TODO: Initialize to an appropriate value
             string expected = string.Empty; // TODO: Initialize to an appropriate value
-            string actual;
             target.Name = expected;
-            actual = target.Name;
+            var actual = target.Name;
             Assert.AreEqual(expected, actual);
             Assert.Inconclusive("Verify the correctness of this test method.");
         }
@@ -44,16 +45,16 @@ namespace Renci.SshNet.Tests.Classes.Messages
         /// <summary>
         ///A test for Number
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void NumberTest()
         {
             string name = string.Empty; // TODO: Initialize to an appropriate value
             byte number = 0; // TODO: Initialize to an appropriate value
             MessageAttribute target = new MessageAttribute(name, number); // TODO: Initialize to an appropriate value
             byte expected = 0; // TODO: Initialize to an appropriate value
-            byte actual;
             target.Number = expected;
-            actual = target.Number;
+            var actual = target.Number;
             Assert.AreEqual(expected, actual);
             Assert.Inconclusive("Verify the correctness of this test method.");
         }

+ 4 - 6
src/Renci.SshNet.Tests/Classes/Messages/MessageTest.cs

@@ -23,13 +23,12 @@ namespace Renci.SshNet.Tests.Classes.Messages
         /// <summary>
         ///A test for GetBytes
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
         public void GetBytesTest()
         {
             Message target = CreateMessage(); // TODO: Initialize to an appropriate value
             byte[] expected = null; // TODO: Initialize to an appropriate value
-            byte[] actual;
-            actual = target.GetBytes();
+            var actual = target.GetBytes();
             Assert.AreEqual(expected, actual);
             Assert.Inconclusive("Verify the correctness of this test method.");
         }
@@ -37,13 +36,12 @@ namespace Renci.SshNet.Tests.Classes.Messages
         /// <summary>
         ///A test for ToString
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
         public void ToStringTest()
         {
             Message target = CreateMessage(); // TODO: Initialize to an appropriate value
             string expected = string.Empty; // TODO: Initialize to an appropriate value
-            string actual;
-            actual = target.ToString();
+            var actual = target.ToString();
             Assert.AreEqual(expected, actual);
             Assert.Inconclusive("Verify the correctness of this test method.");
         }

+ 3 - 3
src/Renci.SshNet.Tests/Classes/Messages/Transport/DebugMessageTest.cs

@@ -1,6 +1,5 @@
 using Renci.SshNet.Messages.Transport;
 using Microsoft.VisualStudio.TestTools.UnitTesting;
-using System;
 using Renci.SshNet.Tests.Common;
 
 namespace Renci.SshNet.Tests.Classes.Messages.Transport
@@ -9,13 +8,14 @@ namespace Renci.SshNet.Tests.Classes.Messages.Transport
     ///This is a test class for DebugMessageTest and is intended
     ///to contain all DebugMessageTest Unit Tests
     ///</summary>
-    [TestClass()]
+    [TestClass]
     public class DebugMessageTest : TestBase
     {
         /// <summary>
         ///A test for DebugMessage Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void DebugMessageConstructorTest()
         {
             DebugMessage target = new DebugMessage();

+ 5 - 3
src/Renci.SshNet.Tests/Classes/Messages/Transport/DisconnectMessageTest.cs

@@ -9,13 +9,14 @@ namespace Renci.SshNet.Tests.Classes.Messages.Transport
     ///This is a test class for DisconnectMessageTest and is intended
     ///to contain all DisconnectMessageTest Unit Tests
     ///</summary>
-    [TestClass()]
+    [TestClass]
     public class DisconnectMessageTest : TestBase
     {
         /// <summary>
         ///A test for DisconnectMessage Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void DisconnectMessageConstructorTest()
         {
             DisconnectMessage target = new DisconnectMessage();
@@ -25,7 +26,8 @@ namespace Renci.SshNet.Tests.Classes.Messages.Transport
         /// <summary>
         ///A test for DisconnectMessage Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void DisconnectMessageConstructorTest1()
         {
             DisconnectReason reasonCode = new DisconnectReason(); // TODO: Initialize to an appropriate value

+ 3 - 2
src/Renci.SshNet.Tests/Classes/Messages/Transport/KeyExchangeDhGroupExchangeGroupTest.cs

@@ -9,13 +9,14 @@ namespace Renci.SshNet.Tests.Classes.Messages.Transport
     ///This is a test class for KeyExchangeDhGroupExchangeGroupTest and is intended
     ///to contain all KeyExchangeDhGroupExchangeGroupTest Unit Tests
     ///</summary>
-    [TestClass()]
+    [TestClass]
     public class KeyExchangeDhGroupExchangeGroupTest : TestBase
     {
         /// <summary>
         ///A test for KeyExchangeDhGroupExchangeGroup Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void KeyExchangeDhGroupExchangeGroupConstructorTest()
         {
             KeyExchangeDhGroupExchangeGroup target = new KeyExchangeDhGroupExchangeGroup();

+ 3 - 2
src/Renci.SshNet.Tests/Classes/Messages/Transport/KeyExchangeDhReplyMessageTest.cs

@@ -9,13 +9,14 @@ namespace Renci.SshNet.Tests.Classes.Messages.Transport
     ///This is a test class for KeyExchangeDhReplyMessageTest and is intended
     ///to contain all KeyExchangeDhReplyMessageTest Unit Tests
     ///</summary>
-    [TestClass()]
+    [TestClass]
     public class KeyExchangeDhReplyMessageTest : TestBase
     {
         /// <summary>
         ///A test for KeyExchangeDhReplyMessage Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void KeyExchangeDhReplyMessageConstructorTest()
         {
             KeyExchangeDhReplyMessage target = new KeyExchangeDhReplyMessage();

+ 38 - 37
src/Renci.SshNet.Tests/Classes/Messages/Transport/KeyExchangeInitMessageTest.cs

@@ -68,7 +68,8 @@ namespace Renci.SshNet.Tests.Classes.Messages.Transport
         /// <summary>
         ///A test for KeyExchangeInitMessage Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void KeyExchangeInitMessageConstructorTest()
         {
             KeyExchangeInitMessage target = new KeyExchangeInitMessage();
@@ -78,14 +79,14 @@ namespace Renci.SshNet.Tests.Classes.Messages.Transport
         /// <summary>
         ///A test for CompressionAlgorithmsClientToServer
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void CompressionAlgorithmsClientToServerTest()
         {
             KeyExchangeInitMessage target = new KeyExchangeInitMessage(); // TODO: Initialize to an appropriate value
             string[] expected = null; // TODO: Initialize to an appropriate value
-            string[] actual;
             target.CompressionAlgorithmsClientToServer = expected;
-            actual = target.CompressionAlgorithmsClientToServer;
+            var actual = target.CompressionAlgorithmsClientToServer;
             Assert.AreEqual(expected, actual);
             Assert.Inconclusive("Verify the correctness of this test method.");
         }
@@ -93,14 +94,14 @@ namespace Renci.SshNet.Tests.Classes.Messages.Transport
         /// <summary>
         ///A test for CompressionAlgorithmsServerToClient
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void CompressionAlgorithmsServerToClientTest()
         {
             KeyExchangeInitMessage target = new KeyExchangeInitMessage(); // TODO: Initialize to an appropriate value
             string[] expected = null; // TODO: Initialize to an appropriate value
-            string[] actual;
             target.CompressionAlgorithmsServerToClient = expected;
-            actual = target.CompressionAlgorithmsServerToClient;
+            var actual = target.CompressionAlgorithmsServerToClient;
             Assert.AreEqual(expected, actual);
             Assert.Inconclusive("Verify the correctness of this test method.");
         }
@@ -108,14 +109,14 @@ namespace Renci.SshNet.Tests.Classes.Messages.Transport
         /// <summary>
         ///A test for EncryptionAlgorithmsClientToServer
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void EncryptionAlgorithmsClientToServerTest()
         {
             KeyExchangeInitMessage target = new KeyExchangeInitMessage(); // TODO: Initialize to an appropriate value
             string[] expected = null; // TODO: Initialize to an appropriate value
-            string[] actual;
             target.EncryptionAlgorithmsClientToServer = expected;
-            actual = target.EncryptionAlgorithmsClientToServer;
+            var actual = target.EncryptionAlgorithmsClientToServer;
             Assert.AreEqual(expected, actual);
             Assert.Inconclusive("Verify the correctness of this test method.");
         }
@@ -123,14 +124,14 @@ namespace Renci.SshNet.Tests.Classes.Messages.Transport
         /// <summary>
         ///A test for EncryptionAlgorithmsServerToClient
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void EncryptionAlgorithmsServerToClientTest()
         {
             KeyExchangeInitMessage target = new KeyExchangeInitMessage(); // TODO: Initialize to an appropriate value
             string[] expected = null; // TODO: Initialize to an appropriate value
-            string[] actual;
             target.EncryptionAlgorithmsServerToClient = expected;
-            actual = target.EncryptionAlgorithmsServerToClient;
+            var actual = target.EncryptionAlgorithmsServerToClient;
             Assert.AreEqual(expected, actual);
             Assert.Inconclusive("Verify the correctness of this test method.");
         }
@@ -138,14 +139,14 @@ namespace Renci.SshNet.Tests.Classes.Messages.Transport
         /// <summary>
         ///A test for FirstKexPacketFollows
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void FirstKexPacketFollowsTest()
         {
             KeyExchangeInitMessage target = new KeyExchangeInitMessage(); // TODO: Initialize to an appropriate value
             bool expected = false; // TODO: Initialize to an appropriate value
-            bool actual;
             target.FirstKexPacketFollows = expected;
-            actual = target.FirstKexPacketFollows;
+            var actual = target.FirstKexPacketFollows;
             Assert.AreEqual(expected, actual);
             Assert.Inconclusive("Verify the correctness of this test method.");
         }
@@ -153,14 +154,14 @@ namespace Renci.SshNet.Tests.Classes.Messages.Transport
         /// <summary>
         ///A test for KeyExchangeAlgorithms
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void KeyExchangeAlgorithmsTest()
         {
             KeyExchangeInitMessage target = new KeyExchangeInitMessage(); // TODO: Initialize to an appropriate value
             string[] expected = null; // TODO: Initialize to an appropriate value
-            string[] actual;
             target.KeyExchangeAlgorithms = expected;
-            actual = target.KeyExchangeAlgorithms;
+            var actual = target.KeyExchangeAlgorithms;
             Assert.AreEqual(expected, actual);
             Assert.Inconclusive("Verify the correctness of this test method.");
         }
@@ -168,14 +169,14 @@ namespace Renci.SshNet.Tests.Classes.Messages.Transport
         /// <summary>
         ///A test for LanguagesClientToServer
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void LanguagesClientToServerTest()
         {
             KeyExchangeInitMessage target = new KeyExchangeInitMessage(); // TODO: Initialize to an appropriate value
             string[] expected = null; // TODO: Initialize to an appropriate value
-            string[] actual;
             target.LanguagesClientToServer = expected;
-            actual = target.LanguagesClientToServer;
+            var actual = target.LanguagesClientToServer;
             Assert.AreEqual(expected, actual);
             Assert.Inconclusive("Verify the correctness of this test method.");
         }
@@ -183,14 +184,14 @@ namespace Renci.SshNet.Tests.Classes.Messages.Transport
         /// <summary>
         ///A test for LanguagesServerToClient
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void LanguagesServerToClientTest()
         {
             KeyExchangeInitMessage target = new KeyExchangeInitMessage(); // TODO: Initialize to an appropriate value
             string[] expected = null; // TODO: Initialize to an appropriate value
-            string[] actual;
             target.LanguagesServerToClient = expected;
-            actual = target.LanguagesServerToClient;
+            var actual = target.LanguagesServerToClient;
             Assert.AreEqual(expected, actual);
             Assert.Inconclusive("Verify the correctness of this test method.");
         }
@@ -198,14 +199,14 @@ namespace Renci.SshNet.Tests.Classes.Messages.Transport
         /// <summary>
         ///A test for MacAlgorithmsClientToServer
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void MacAlgorithmsClientToServerTest()
         {
             KeyExchangeInitMessage target = new KeyExchangeInitMessage(); // TODO: Initialize to an appropriate value
             string[] expected = null; // TODO: Initialize to an appropriate value
-            string[] actual;
             target.MacAlgorithmsClientToServer = expected;
-            actual = target.MacAlgorithmsClientToServer;
+            var actual = target.MacAlgorithmsClientToServer;
             Assert.AreEqual(expected, actual);
             Assert.Inconclusive("Verify the correctness of this test method.");
         }
@@ -213,14 +214,14 @@ namespace Renci.SshNet.Tests.Classes.Messages.Transport
         /// <summary>
         ///A test for MacAlgorithmsServerToClient
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void MacAlgorithmsServerToClientTest()
         {
             KeyExchangeInitMessage target = new KeyExchangeInitMessage(); // TODO: Initialize to an appropriate value
             string[] expected = null; // TODO: Initialize to an appropriate value
-            string[] actual;
             target.MacAlgorithmsServerToClient = expected;
-            actual = target.MacAlgorithmsServerToClient;
+            var actual = target.MacAlgorithmsServerToClient;
             Assert.AreEqual(expected, actual);
             Assert.Inconclusive("Verify the correctness of this test method.");
         }
@@ -228,14 +229,14 @@ namespace Renci.SshNet.Tests.Classes.Messages.Transport
         /// <summary>
         ///A test for Reserved
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void ReservedTest()
         {
             KeyExchangeInitMessage target = new KeyExchangeInitMessage(); // TODO: Initialize to an appropriate value
             uint expected = 0; // TODO: Initialize to an appropriate value
-            uint actual;
             target.Reserved = expected;
-            actual = target.Reserved;
+            var actual = target.Reserved;
             Assert.AreEqual(expected, actual);
             Assert.Inconclusive("Verify the correctness of this test method.");
         }
@@ -243,14 +244,14 @@ namespace Renci.SshNet.Tests.Classes.Messages.Transport
         /// <summary>
         ///A test for ServerHostKeyAlgorithms
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void ServerHostKeyAlgorithmsTest()
         {
             KeyExchangeInitMessage target = new KeyExchangeInitMessage(); // TODO: Initialize to an appropriate value
             string[] expected = null; // TODO: Initialize to an appropriate value
-            string[] actual;
             target.ServerHostKeyAlgorithms = expected;
-            actual = target.ServerHostKeyAlgorithms;
+            var actual = target.ServerHostKeyAlgorithms;
             Assert.AreEqual(expected, actual);
             Assert.Inconclusive("Verify the correctness of this test method.");
         }

+ 3 - 2
src/Renci.SshNet.Tests/Classes/Messages/Transport/NewKeysMessageTest.cs

@@ -9,13 +9,14 @@ namespace Renci.SshNet.Tests.Classes.Messages.Transport
     ///This is a test class for NewKeysMessageTest and is intended
     ///to contain all NewKeysMessageTest Unit Tests
     ///</summary>
-    [TestClass()]
+    [TestClass]
     public class NewKeysMessageTest : TestBase
     {
         /// <summary>
         ///A test for NewKeysMessage Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void NewKeysMessageConstructorTest()
         {
             NewKeysMessage target = new NewKeysMessage();

+ 3 - 2
src/Renci.SshNet.Tests/Classes/Messages/Transport/ServiceAcceptMessageTest.cs

@@ -8,13 +8,14 @@ namespace Renci.SshNet.Tests.Classes.Messages.Transport
     ///This is a test class for ServiceAcceptMessageTest and is intended
     ///to contain all ServiceAcceptMessageTest Unit Tests
     ///</summary>
-    [TestClass()]
+    [TestClass]
     public class ServiceAcceptMessageTest
     {
         /// <summary>
         ///A test for ServiceAcceptMessage Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void ServiceAcceptMessageConstructorTest()
         {
             ServiceAcceptMessage target = new ServiceAcceptMessage();

+ 3 - 2
src/Renci.SshNet.Tests/Classes/Messages/Transport/ServiceRequestMessageTest.cs

@@ -9,13 +9,14 @@ namespace Renci.SshNet.Tests.Classes.Messages.Transport
     ///This is a test class for ServiceRequestMessageTest and is intended
     ///to contain all ServiceRequestMessageTest Unit Tests
     ///</summary>
-    [TestClass()]
+    [TestClass]
     public class ServiceRequestMessageTest
     {
         /// <summary>
         ///A test for ServiceRequestMessage Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void ServiceRequestMessageConstructorTest()
         {
             ServiceName serviceName = new ServiceName(); // TODO: Initialize to an appropriate value

+ 3 - 2
src/Renci.SshNet.Tests/Classes/Messages/Transport/UnimplementedMessageTest.cs

@@ -9,13 +9,14 @@ namespace Renci.SshNet.Tests.Classes.Messages.Transport
     ///This is a test class for UnimplementedMessageTest and is intended
     ///to contain all UnimplementedMessageTest Unit Tests
     ///</summary>
-    [TestClass()]
+    [TestClass]
     public class UnimplementedMessageTest : TestBase
     {
         /// <summary>
         ///A test for UnimplementedMessage Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void UnimplementedMessageConstructorTest()
         {
             UnimplementedMessage target = new UnimplementedMessage();

+ 1 - 0
src/Renci.SshNet.Tests/Classes/Security/CertificateHostAlgorithmTest.cs

@@ -15,6 +15,7 @@ namespace Renci.SshNet.Tests.Classes.Security
         ///A test for CertificateHostAlgorithm Constructor
         ///</summary>
         [TestMethod]
+        [Ignore] // placeholder
         public void CertificateHostAlgorithmConstructorTest()
         {
             string name = string.Empty; // TODO: Initialize to an appropriate value

+ 3 - 2
src/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/Paddings/PKCS5PaddingTest.cs

@@ -8,7 +8,7 @@ namespace Renci.SshNet.Tests.Classes.Security.Cryptography.Ciphers.Paddings
     ///This is a test class for PKCS5PaddingTest and is intended
     ///to contain all PKCS5PaddingTest Unit Tests
     ///</summary>
-    [TestClass()]
+    [TestClass]
     public class PKCS5PaddingTest : TestBase
     {
         /// <summary>
@@ -31,7 +31,8 @@ namespace Renci.SshNet.Tests.Classes.Security.Cryptography.Ciphers.Paddings
         /// <summary>
         ///A test for PKCS5Padding Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void PKCS5PaddingConstructorTest()
         {
             PKCS5Padding target = new PKCS5Padding();

+ 2 - 1
src/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/Paddings/PKCS7PaddingTest.cs

@@ -31,7 +31,8 @@ namespace Renci.SshNet.Tests.Classes.Security.Cryptography.Ciphers.Paddings
         /// <summary>
         ///A test for PKCS7Padding Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void PKCS7PaddingConstructorTest()
         {
             PKCS7Padding target = new PKCS7Padding();

+ 19 - 10
src/Renci.SshNet.Tests/Classes/Security/Cryptography/DsaKeyTest.cs

@@ -9,13 +9,14 @@ namespace Renci.SshNet.Tests.Classes.Security.Cryptography
     ///This is a test class for DsaKeyTest and is intended
     ///to contain all DsaKeyTest Unit Tests
     ///</summary>
-    [TestClass()]
+    [TestClass]
     public class DsaKeyTest : TestBase
     {
         /// <summary>
         ///A test for DsaKey Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder for actual test
         public void DsaKeyConstructorTest()
         {
             DsaKey target = new DsaKey();
@@ -37,7 +38,8 @@ namespace Renci.SshNet.Tests.Classes.Security.Cryptography
         /// <summary>
         ///A test for DsaKey Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder for actual test
         public void DsaKeyConstructorTest2()
         {
             BigInteger p = new BigInteger(); // TODO: Initialize to an appropriate value
@@ -52,7 +54,8 @@ namespace Renci.SshNet.Tests.Classes.Security.Cryptography
         /// <summary>
         ///A test for Dispose
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder for actual test
         public void DisposeTest()
         {
             DsaKey target = new DsaKey(); // TODO: Initialize to an appropriate value
@@ -63,7 +66,8 @@ namespace Renci.SshNet.Tests.Classes.Security.Cryptography
         /// <summary>
         ///A test for G
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder for actual test
         public void GTest()
         {
             DsaKey target = new DsaKey(); // TODO: Initialize to an appropriate value
@@ -75,7 +79,8 @@ namespace Renci.SshNet.Tests.Classes.Security.Cryptography
         /// <summary>
         ///A test for KeyLength
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder for actual test
         public void KeyLengthTest()
         {
             DsaKey target = new DsaKey(); // TODO: Initialize to an appropriate value
@@ -87,7 +92,8 @@ namespace Renci.SshNet.Tests.Classes.Security.Cryptography
         /// <summary>
         ///A test for P
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder for actual test
         public void PTest()
         {
             DsaKey target = new DsaKey(); // TODO: Initialize to an appropriate value
@@ -115,7 +121,8 @@ namespace Renci.SshNet.Tests.Classes.Security.Cryptography
         /// <summary>
         ///A test for Q
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder for actual test
         public void QTest()
         {
             DsaKey target = new DsaKey(); // TODO: Initialize to an appropriate value
@@ -127,7 +134,8 @@ namespace Renci.SshNet.Tests.Classes.Security.Cryptography
         /// <summary>
         ///A test for X
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder for actual test
         public void XTest()
         {
             DsaKey target = new DsaKey(); // TODO: Initialize to an appropriate value
@@ -139,7 +147,8 @@ namespace Renci.SshNet.Tests.Classes.Security.Cryptography
         /// <summary>
         ///A test for Y
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder for actual test
         public void YTest()
         {
             DsaKey target = new DsaKey(); // TODO: Initialize to an appropriate value

+ 9 - 10
src/Renci.SshNet.Tests/Classes/Security/Cryptography/HMacTest.cs

@@ -1,8 +1,7 @@
 using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Renci.SshNet.Security.Cryptography;
 using Renci.SshNet.Tests.Common;
 using Renci.SshNet.Tests.Properties;
-using System.Security.Cryptography;
+using Renci.SshNet.Abstractions;
 
 namespace Renci.SshNet.Tests.Classes.Security.Cryptography
 {
@@ -19,7 +18,7 @@ namespace Renci.SshNet.Tests.Classes.Security.Cryptography
         {
             var connectionInfo = new PasswordConnectionInfo(Resources.HOST, int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD);
             connectionInfo.HmacAlgorithms.Clear();
-            connectionInfo.HmacAlgorithms.Add("hmac-md5", new HashInfo(16 * 8, HashAlgorithmFactory.CreateHMACMD5));
+            connectionInfo.HmacAlgorithms.Add("hmac-md5", new HashInfo(16 * 8, CryptoAbstraction.CreateHMACMD5));
 
             using (var client = new SshClient(connectionInfo))
             {
@@ -34,7 +33,7 @@ namespace Renci.SshNet.Tests.Classes.Security.Cryptography
         {
             var connectionInfo = new PasswordConnectionInfo(Resources.HOST, int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD);
             connectionInfo.HmacAlgorithms.Clear();
-            connectionInfo.HmacAlgorithms.Add("hmac-sha1", new HashInfo(20 * 8, HashAlgorithmFactory.CreateHMACSHA1));
+            connectionInfo.HmacAlgorithms.Add("hmac-sha1", new HashInfo(20 * 8, CryptoAbstraction.CreateHMACSHA1));
 
             using (var client = new SshClient(connectionInfo))
             {
@@ -49,7 +48,7 @@ namespace Renci.SshNet.Tests.Classes.Security.Cryptography
         {
             var connectionInfo = new PasswordConnectionInfo(Resources.HOST, int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD);
             connectionInfo.HmacAlgorithms.Clear();
-            connectionInfo.HmacAlgorithms.Add("hmac-md5", new HashInfo(16 * 8, key => HashAlgorithmFactory.CreateHMACMD5(key, 96)));
+            connectionInfo.HmacAlgorithms.Add("hmac-md5", new HashInfo(16 * 8, key => CryptoAbstraction.CreateHMACMD5(key, 96)));
 
             using (var client = new SshClient(connectionInfo))
             {
@@ -64,7 +63,7 @@ namespace Renci.SshNet.Tests.Classes.Security.Cryptography
         {
             var connectionInfo = new PasswordConnectionInfo(Resources.HOST, int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD);
             connectionInfo.HmacAlgorithms.Clear();
-            connectionInfo.HmacAlgorithms.Add("hmac-sha1", new HashInfo(20 * 8, key => HashAlgorithmFactory.CreateHMACSHA1(key, 96)));
+            connectionInfo.HmacAlgorithms.Add("hmac-sha1", new HashInfo(20 * 8, key => CryptoAbstraction.CreateHMACSHA1(key, 96)));
 
             using (var client = new SshClient(connectionInfo))
             {
@@ -79,7 +78,7 @@ namespace Renci.SshNet.Tests.Classes.Security.Cryptography
         {
             var connectionInfo = new PasswordConnectionInfo(Resources.HOST, int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD);
             connectionInfo.HmacAlgorithms.Clear();
-            connectionInfo.HmacAlgorithms.Add("hmac-sha2-256", new HashInfo(32 * 8, HashAlgorithmFactory.CreateHMACSHA256));
+            connectionInfo.HmacAlgorithms.Add("hmac-sha2-256", new HashInfo(32 * 8, CryptoAbstraction.CreateHMACSHA256));
 
             using (var client = new SshClient(connectionInfo))
             {
@@ -94,7 +93,7 @@ namespace Renci.SshNet.Tests.Classes.Security.Cryptography
         {
             var connectionInfo = new PasswordConnectionInfo(Resources.HOST, int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD);
             connectionInfo.HmacAlgorithms.Clear();
-            connectionInfo.HmacAlgorithms.Add("hmac-sha2-256-96", new HashInfo(32 * 8, (key) => HashAlgorithmFactory.CreateHMACSHA256(key, 96)));
+            connectionInfo.HmacAlgorithms.Add("hmac-sha2-256-96", new HashInfo(32 * 8, (key) => CryptoAbstraction.CreateHMACSHA256(key, 96)));
 
             using (var client = new SshClient(connectionInfo))
             {
@@ -109,7 +108,7 @@ namespace Renci.SshNet.Tests.Classes.Security.Cryptography
         {
             var connectionInfo = new PasswordConnectionInfo(Resources.HOST, int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD);
             connectionInfo.HmacAlgorithms.Clear();
-            connectionInfo.HmacAlgorithms.Add("hmac-ripemd160", new HashInfo(160, HashAlgorithmFactory.CreateHMACRIPEMD160));
+            connectionInfo.HmacAlgorithms.Add("hmac-ripemd160", new HashInfo(160, CryptoAbstraction.CreateHMACRIPEMD160));
 
             using (var client = new SshClient(connectionInfo))
             {
@@ -124,7 +123,7 @@ namespace Renci.SshNet.Tests.Classes.Security.Cryptography
         {
             var connectionInfo = new PasswordConnectionInfo(Resources.HOST, int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD);
             connectionInfo.HmacAlgorithms.Clear();
-            connectionInfo.HmacAlgorithms.Add("hmac-ripemd160@openssh.com", new HashInfo(160, HashAlgorithmFactory.CreateHMACRIPEMD160));
+            connectionInfo.HmacAlgorithms.Add("hmac-ripemd160@openssh.com", new HashInfo(160, CryptoAbstraction.CreateHMACRIPEMD160));
 
             using (var client = new SshClient(connectionInfo))
             {

+ 7 - 4
src/Renci.SshNet.Tests/Classes/Security/KeyExchangeDiffieHellmanGroup1Sha1Test.cs

@@ -9,13 +9,14 @@ namespace Renci.SshNet.Tests.Classes.Security
     ///This is a test class for KeyExchangeDiffieHellmanGroup1Sha1Test and is intended
     ///to contain all KeyExchangeDiffieHellmanGroup1Sha1Test Unit Tests
     ///</summary>
-    [TestClass()]
+    [TestClass]
     public class KeyExchangeDiffieHellmanGroup1Sha1Test : TestBase
     {
         /// <summary>
         ///A test for KeyExchangeDiffieHellmanGroup1Sha1 Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder for actual test
         public void KeyExchangeDiffieHellmanGroup1Sha1ConstructorTest()
         {
             KeyExchangeDiffieHellmanGroup1Sha1 target = new KeyExchangeDiffieHellmanGroup1Sha1();
@@ -25,7 +26,8 @@ namespace Renci.SshNet.Tests.Classes.Security
         /// <summary>
         ///A test for GroupPrime
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder for actual test
         public void GroupPrimeTest()
         {
             KeyExchangeDiffieHellmanGroup1Sha1 target = new KeyExchangeDiffieHellmanGroup1Sha1(); // TODO: Initialize to an appropriate value
@@ -37,7 +39,8 @@ namespace Renci.SshNet.Tests.Classes.Security
         /// <summary>
         ///A test for Name
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder for actual test
         public void NameTest()
         {
             KeyExchangeDiffieHellmanGroup1Sha1 target = new KeyExchangeDiffieHellmanGroup1Sha1(); // TODO: Initialize to an appropriate value

+ 5 - 3
src/Renci.SshNet.Tests/Classes/Security/KeyExchangeDiffieHellmanGroupExchangeSha256Test.cs

@@ -9,13 +9,14 @@ namespace Renci.SshNet.Tests.Classes.Security
     ///This is a test class for KeyExchangeDiffieHellmanGroupExchangeSha256Test and is intended
     ///to contain all KeyExchangeDiffieHellmanGroupExchangeSha256Test Unit Tests
     ///</summary>
-    [TestClass()]
+    [TestClass]
     public class KeyExchangeDiffieHellmanGroupExchangeSha256Test : TestBase
     {
         /// <summary>
         ///A test for KeyExchangeDiffieHellmanGroupExchangeSha256 Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder for actual test
         public void KeyExchangeDiffieHellmanGroupExchangeSha256ConstructorTest()
         {
             KeyExchangeDiffieHellmanGroupExchangeSha256 target = new KeyExchangeDiffieHellmanGroupExchangeSha256();
@@ -51,7 +52,8 @@ namespace Renci.SshNet.Tests.Classes.Security
         /// <summary>
         ///A test for Name
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder for actual test
         public void NameTest()
         {
             KeyExchangeDiffieHellmanGroupExchangeSha256 target = new KeyExchangeDiffieHellmanGroupExchangeSha256(); // TODO: Initialize to an appropriate value

+ 8 - 8
src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ConnectionReset.cs

@@ -18,13 +18,13 @@ namespace Renci.SshNet.Tests.Classes
             Thread.Sleep(200);
         }
 
-        [TestMethodAttribute]
+        [TestMethod]
         public void IsConnectedShouldReturnFalse()
         {
             Assert.IsFalse(Session.IsConnected);
         }
 
-        [TestMethodAttribute]
+        [TestMethod]
         public void DisconnectShouldFinishImmediately()
         {
             var stopwatch = new Stopwatch();
@@ -73,7 +73,7 @@ namespace Renci.SshNet.Tests.Classes
             Assert.AreSame(innerException.Message, connectionException.Message);
         }
 
-        [TestMethodAttribute]
+        [TestMethod]
         public void DisposeShouldFinishImmediately()
         {
             var stopwatch = new Stopwatch();
@@ -85,7 +85,7 @@ namespace Renci.SshNet.Tests.Classes
             Assert.IsTrue(stopwatch.ElapsedMilliseconds < 500);
         }
 
-        [TestMethodAttribute]
+        [TestMethod]
         public void SendMessageShouldThrowSshConnectionException()
         {
             try
@@ -101,7 +101,7 @@ namespace Renci.SshNet.Tests.Classes
             }
         }
 
-        [TestMethodAttribute]
+        [TestMethod]
         public void ISession_MessageListenerCompletedShouldBeSignaled()
         {
             var session = (ISession) Session;
@@ -110,7 +110,7 @@ namespace Renci.SshNet.Tests.Classes
             Assert.IsTrue(session.MessageListenerCompleted.WaitOne());
         }
 
-        [TestMethodAttribute]
+        [TestMethod]
         public void ISession_SendMessageShouldThrowSshConnectionException()
         {
             var session = (ISession) Session;
@@ -128,7 +128,7 @@ namespace Renci.SshNet.Tests.Classes
             }
         }
 
-        [TestMethodAttribute]
+        [TestMethod]
         public void ISession_TrySendMessageShouldReturnFalse()
         {
             var session = (ISession) Session;
@@ -138,7 +138,7 @@ namespace Renci.SshNet.Tests.Classes
             Assert.IsFalse(actual);
         }
 
-        [TestMethodAttribute]
+        [TestMethod]
         public void ISession_WaitOnHandleShouldThrowSshConnectionExceptionDetailingConnectionReset()
         {
             var session = (ISession) Session;

+ 3 - 2
src/Renci.SshNet.Tests/Classes/Sftp/SftpDownloadAsyncResultTest.cs

@@ -9,13 +9,14 @@ namespace Renci.SshNet.Tests.Classes.Sftp
     ///This is a test class for SftpDownloadAsyncResultTest and is intended
     ///to contain all SftpDownloadAsyncResultTest Unit Tests
     ///</summary>
-    [TestClass()]
+    [TestClass]
     public class SftpDownloadAsyncResultTest : TestBase
     {
         /// <summary>
         ///A test for SftpDownloadAsyncResult Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void SftpDownloadAsyncResultConstructorTest()
         {
             AsyncCallback asyncCallback = null; // TODO: Initialize to an appropriate value

+ 5 - 4
src/Renci.SshNet.Tests/Classes/Sftp/SftpFileAttributesTest.cs

@@ -1,15 +1,16 @@
-using Renci.SshNet.Sftp;
+using System;
 using Microsoft.VisualStudio.TestTools.UnitTesting;
-using System;
+using Renci.SshNet.Sftp;
 using Renci.SshNet.Tests.Common;
 
-namespace Renci.SshNet.Tests
+namespace Renci.SshNet.Tests.Classes.Sftp
 {
     /// <summary>
     ///This is a test class for SftpFileAttributesTest and is intended
     ///to contain all SftpFileAttributesTest Unit Tests
     ///</summary>
-    [TestClass()]
+    [TestClass]
+    [Ignore] // placeholders only
     public class SftpFileAttributesTest : TestBase
     {
         /// <summary>

+ 5 - 3
src/Renci.SshNet.Tests/Classes/Sftp/SftpFileSystemInformationTest.cs

@@ -8,13 +8,14 @@ namespace Renci.SshNet.Tests.Classes.Sftp
     ///This is a test class for SftpFileSytemInformationTest and is intended
     ///to contain all SftpFileSytemInformationTest Unit Tests
     ///</summary>
-    [TestClass()]
+    [TestClass]
     public class SftpFileSystemInformationTest : TestBase
     {
         /// <summary>
         ///A test for IsReadOnly
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void IsReadOnlyTest()
         {
             ulong bsize = 0; // TODO: Initialize to an appropriate value
@@ -37,7 +38,8 @@ namespace Renci.SshNet.Tests.Classes.Sftp
         /// <summary>
         ///A test for SupportsSetUid
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void SupportsSetUidTest()
         {
             ulong bsize = 0; // TODO: Initialize to an appropriate value

+ 3 - 2
src/Renci.SshNet.Tests/Classes/Sftp/SftpListDirectoryAsyncResultTest.cs

@@ -9,13 +9,14 @@ namespace Renci.SshNet.Tests
     ///This is a test class for SftpListDirectoryAsyncResultTest and is intended
     ///to contain all SftpListDirectoryAsyncResultTest Unit Tests
     ///</summary>
-    [TestClass()]
+    [TestClass]
     public class SftpListDirectoryAsyncResultTest : TestBase
     {
         /// <summary>
         ///A test for SftpListDirectoryAsyncResult Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void SftpListDirectoryAsyncResultConstructorTest()
         {
             AsyncCallback asyncCallback = null; // TODO: Initialize to an appropriate value

+ 3 - 2
src/Renci.SshNet.Tests/Classes/Sftp/SftpSynchronizeDirectoriesAsyncResultTest.cs

@@ -9,13 +9,14 @@ namespace Renci.SshNet.Tests.Classes.Sftp
     ///This is a test class for SftpSynchronizeDirectoriesAsyncResultTest and is intended
     ///to contain all SftpSynchronizeDirectoriesAsyncResultTest Unit Tests
     ///</summary>
-    [TestClass()]
+    [TestClass]
     public class SftpSynchronizeDirectoriesAsyncResultTest : TestBase
     {
         /// <summary>
         ///A test for SftpSynchronizeDirectoriesAsyncResult Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void SftpSynchronizeDirectoriesAsyncResultConstructorTest()
         {
             AsyncCallback asyncCallback = null; // TODO: Initialize to an appropriate value

+ 3 - 2
src/Renci.SshNet.Tests/Classes/Sftp/SftpUploadAsyncResultTest.cs

@@ -9,13 +9,14 @@ namespace Renci.SshNet.Tests.Classes.Sftp
     ///This is a test class for SftpUploadAsyncResultTest and is intended
     ///to contain all SftpUploadAsyncResultTest Unit Tests
     ///</summary>
-    [TestClass()]
+    [TestClass]
     public class SftpUploadAsyncResultTest : TestBase
     {
         /// <summary>
         ///A test for SftpUploadAsyncResult Constructor
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
+        [Ignore] // placeholder
         public void SftpUploadAsyncResultConstructorTest()
         {
             AsyncCallback asyncCallback = null; // TODO: Initialize to an appropriate value

+ 15 - 12
src/Renci.SshNet.Tests/Common/AsyncSocketListener.cs

@@ -2,7 +2,6 @@
 using System.Net;
 using System.Net.Sockets;
 using System.Threading;
-using Renci.SshNet.Common;
 
 namespace Renci.SshNet.Tests.Common
 {
@@ -96,11 +95,22 @@ namespace Renci.SshNet.Tests.Common
         {
             // Retrieve the state object and the handler socket
             // from the asynchronous state object.
-            var state = (SocketStateObject)ar.AsyncState;
+            var state = (SocketStateObject) ar.AsyncState;
             var handler = state.Socket;
 
-            // Read data from the client socket.
-            var bytesRead = handler.EndReceive(ar);
+            int bytesRead;
+            try
+            {
+                // Read data from the client socket.
+                bytesRead = handler.EndReceive(ar);
+            }
+            catch (ObjectDisposedException)
+            {
+                // when the socket is closed, the callback will be invoked for any pending BeginReceive
+                // we could use the Socket.Connected property to detect this here, but the proper thing
+                // to do is invoke EndReceive knowing that it will throw an ObjectDisposedException
+                return;
+            }
 
             if (bytesRead > 0)
             {
@@ -109,14 +119,7 @@ namespace Renci.SshNet.Tests.Common
                 SignalBytesReceived(bytesReceived, handler);
 
                 // prepare to receive more bytes
-                try
-                {
-                    handler.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, ReadCallback, state);
-                }
-                catch (ObjectDisposedException)
-                {
-                    // when the socket is closed, an ObjectDisposedException is thrown
-                }
+                handler.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, ReadCallback, state);
             }
             else
             {

+ 20 - 6
src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj

@@ -18,7 +18,7 @@
     <DebugType>full</DebugType>
     <Optimize>false</Optimize>
     <OutputPath>bin\Debug\</OutputPath>
-    <DefineConstants>TRACE;DEBUG</DefineConstants>
+    <DefineConstants>TRACE;DEBUG;FEATURE_THREAD_COUNTDOWNEVENT</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
   </PropertyGroup>
@@ -26,7 +26,7 @@
     <DebugType>pdbonly</DebugType>
     <Optimize>true</Optimize>
     <OutputPath>bin\Release\</OutputPath>
-    <DefineConstants>TRACE</DefineConstants>
+    <DefineConstants>TRACE;FEATURE_THREAD_COUNTDOWNEVENT</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>0</WarningLevel>
   </PropertyGroup>
@@ -73,6 +73,18 @@
     <Compile Include="..\..\test\Renci.SshNet.Shared.Tests\Abstractions\ThreadAbstraction_ExecuteThread.cs">
       <Link>Classes\Abstractions\ThreadAbstraction_ExecuteThread.cs</Link>
     </Compile>
+    <Compile Include="..\..\test\Renci.SshNet.Shared.Tests\ForwardedPortStatusTest_Started.cs">
+      <Link>Classes\ForwardedPortStatusTest_Started.cs</Link>
+    </Compile>
+    <Compile Include="..\..\test\Renci.SshNet.Shared.Tests\ForwardedPortStatusTest_Starting.cs">
+      <Link>Classes\ForwardedPortStatusTest_Starting.cs</Link>
+    </Compile>
+    <Compile Include="..\..\test\Renci.SshNet.Shared.Tests\ForwardedPortStatusTest_Stopped.cs">
+      <Link>Classes\ForwardedPortStatusTest_Stopped.cs</Link>
+    </Compile>
+    <Compile Include="..\..\test\Renci.SshNet.Shared.Tests\ForwardedPortStatusTest_Stopping.cs">
+      <Link>Classes\ForwardedPortStatusTest_Stopping.cs</Link>
+    </Compile>
     <Compile Include="..\..\test\Renci.SshNet.Shared.Tests\SshMessageFactoryTest.cs">
       <Link>Classes\SshMessageFactoryTest.cs</Link>
     </Compile>
@@ -131,6 +143,7 @@
     <Compile Include="Classes\ClientAuthenticationTest_Success_MultiList_SameAllowedAuthenticationsAfterPartialSuccess.cs" />
     <Compile Include="Classes\ClientAuthenticationTest_Success_MultiList_SkipFailedAuthenticationMethod.cs" />
     <Compile Include="Classes\ClientAuthenticationTest_Success_SingleList_SameAllowedAuthenticationAfterPartialSuccess.cs" />
+    <Compile Include="Classes\Common\CountdownEventTest.cs" />
     <Compile Include="Classes\Common\ExtensionsTest_Concat.cs" />
     <Compile Include="Classes\Common\ExtensionsTest_IsEqualTo_ByteArray.cs" />
     <Compile Include="Classes\Common\ExtensionsTest_Take_Count.cs" />
@@ -140,6 +153,7 @@
     <Compile Include="Classes\ConnectionInfoTest_Authenticate_Failure.cs" />
     <Compile Include="Classes\ConnectionInfoTest_Authenticate_Success.cs" />
     <Compile Include="Classes\ForwardedPortDynamicTest_Dispose_PortStarted_ChannelBound.cs" />
+    <Compile Include="Classes\ForwardedPortDynamicTest_SessionErrorOccurred_ChannelBound.cs" />
     <Compile Include="Classes\ForwardedPortDynamicTest_Start_SessionNotConnected.cs" />
     <Compile Include="Classes\ForwardedPortDynamicTest_Start_SessionNull.cs" />
     <Compile Include="Classes\ForwardedPortDynamicTest_Stop_PortDisposed.cs" />
@@ -444,10 +458,10 @@
     <Compile Include="Common\AsyncSocketListener.cs" />
     <Compile Include="Common\HttpProxyStub.cs" />
     <Compile Include="Common\HttpRequest.cs" />
-    <Compile Include="Common\PipeStream_Close_BlockingRead.cs" />
-    <Compile Include="Common\PipeStream_Close_BlockingWrite.cs" />
-    <Compile Include="Common\PipeStream_Flush_BytesRemainingAfterRead.cs" />
-    <Compile Include="Common\PipeStream_Flush_NoBytesRemainingAfterRead.cs" />
+    <Compile Include="Classes\Common\PipeStream_Close_BlockingRead.cs" />
+    <Compile Include="Classes\Common\PipeStream_Close_BlockingWrite.cs" />
+    <Compile Include="Classes\Common\PipeStream_Flush_BytesRemainingAfterRead.cs" />
+    <Compile Include="Classes\Common\PipeStream_Flush_NoBytesRemainingAfterRead.cs" />
     <Compile Include="Common\TestBase.cs" />
     <Compile Include="Classes\Compression\CompressorTest.cs" />
     <Compile Include="Classes\Common\DerDataTest.cs" />

+ 14 - 8
src/Renci.SshNet.UAP10/Renci.SshNet.UAP10.csproj

@@ -23,7 +23,7 @@
     <DebugType>full</DebugType>
     <Optimize>false</Optimize>
     <OutputPath>bin\Debug\</OutputPath>
-    <DefineConstants>TRACE;DEBUG;FEATURE_DATAGRAMSOCKET;FEATURE_SOCKET_EAP;FEATURE_STREAM_TAP;FEATURE_THREAD_TAP;FEATURE_MEMORYSTREAM_TRYGETBUFFER;FEATURE_REFLECTION_TYPEINFO;FEATURE_ENCODING_ASCII</DefineConstants>
+    <DefineConstants>TRACE;DEBUG;FEATURE_DATAGRAMSOCKET;FEATURE_SOCKET_EAP;FEATURE_SOCKET_SETSOCKETOPTION;FEATURE_STREAM_TAP;FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_THREAD_TAP;FEATURE_MEMORYSTREAM_TRYGETBUFFER;FEATURE_REFLECTION_TYPEINFO;FEATURE_ENCODING_ASCII</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
     <DocumentationFile>bin\Debug\Renci.SshNet.xml</DocumentationFile>
@@ -34,7 +34,7 @@
     <DebugType>pdbonly</DebugType>
     <Optimize>true</Optimize>
     <OutputPath>bin\Release\</OutputPath>
-    <DefineConstants>TRACE;FEATURE_DATAGRAMSOCKET;FEATURE_SOCKET_EAP;FEATURE_STREAM_TAP;FEATURE_THREAD_TAP;FEATURE_MEMORYSTREAM_TRYGETBUFFER;FEATURE_REFLECTION_TYPEINFO;FEATURE_ENCODING_ASCII</DefineConstants>
+    <DefineConstants>TRACE;FEATURE_DATAGRAMSOCKET;FEATURE_SOCKET_EAP;FEATURE_SOCKET_SETSOCKETOPTION;FEATURE_STREAM_TAP;FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_THREAD_TAP;FEATURE_MEMORYSTREAM_TRYGETBUFFER;FEATURE_REFLECTION_TYPEINFO;FEATURE_ENCODING_ASCII</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
     <DocumentationFile>bin\Release\Renci.SshNet.xml</DocumentationFile>
@@ -44,7 +44,7 @@
     <PlatformTarget>x86</PlatformTarget>
     <DebugSymbols>true</DebugSymbols>
     <OutputPath>bin\x86\Debug\</OutputPath>
-    <DefineConstants>TRACE;DEBUG;FEATURE_REGEX_COMPILE;FEATURE_DIRECTORYINFO_ENUMERATEFILES;FEATURE_DATAGRAMSOCKET;FEATURE_SOCKET_EAP;FEATURE_STREAM_TAP;FEATURE_THREAD_TAP;FEATURE_MEMORYSTREAM_TRYGETBUFFER;FEATURE_REFLECTION_TYPEINFO;FEATURE_ENCODING_ASCII</DefineConstants>
+    <DefineConstants>TRACE;DEBUG;FEATURE_DATAGRAMSOCKET;FEATURE_SOCKET_EAP;FEATURE_SOCKET_SETSOCKETOPTION;FEATURE_STREAM_TAP;FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_THREAD_TAP;FEATURE_MEMORYSTREAM_TRYGETBUFFER;FEATURE_REFLECTION_TYPEINFO;FEATURE_ENCODING_ASCII</DefineConstants>
     <NoWarn>;2008</NoWarn>
     <DebugType>full</DebugType>
     <PlatformTarget>x86</PlatformTarget>
@@ -54,7 +54,7 @@
   <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
     <PlatformTarget>x86</PlatformTarget>
     <OutputPath>bin\x86\Release\</OutputPath>
-    <DefineConstants>TRACE;FEATURE_DATAGRAMSOCKET;FEATURE_SOCKET_EAP;FEATURE_STREAM_TAP;FEATURE_THREAD_TAP;FEATURE_MEMORYSTREAM_TRYGETBUFFER;FEATURE_REFLECTION_TYPEINFO;FEATURE_ENCODING_ASCII</DefineConstants>
+    <DefineConstants>TRACE;FEATURE_DATAGRAMSOCKET;FEATURE_SOCKET_EAP;FEATURE_SOCKET_SETSOCKETOPTION;FEATURE_STREAM_TAP;FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_THREAD_TAP;FEATURE_MEMORYSTREAM_TRYGETBUFFER;FEATURE_REFLECTION_TYPEINFO;FEATURE_ENCODING_ASCII</DefineConstants>
     <Optimize>true</Optimize>
     <NoWarn>;2008</NoWarn>
     <DebugType>pdbonly</DebugType>
@@ -66,7 +66,7 @@
     <PlatformTarget>ARM</PlatformTarget>
     <DebugSymbols>true</DebugSymbols>
     <OutputPath>bin\ARM\Debug\</OutputPath>
-    <DefineConstants>TRACE;DEBUG;FEATURE_DATAGRAMSOCKET;FEATURE_SOCKET_EAP;FEATURE_STREAM_TAP;FEATURE_THREAD_TAP;FEATURE_MEMORYSTREAM_TRYGETBUFFER;FEATURE_REFLECTION_TYPEINFO;FEATURE_ENCODING_ASCII</DefineConstants>
+    <DefineConstants>TRACE;DEBUG;FEATURE_DATAGRAMSOCKET;FEATURE_SOCKET_EAP;FEATURE_SOCKET_SETSOCKETOPTION;FEATURE_STREAM_TAP;FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_THREAD_TAP;FEATURE_MEMORYSTREAM_TRYGETBUFFER;FEATURE_REFLECTION_TYPEINFO;FEATURE_ENCODING_ASCII</DefineConstants>
     <NoWarn>;2008</NoWarn>
     <DebugType>full</DebugType>
     <PlatformTarget>ARM</PlatformTarget>
@@ -76,7 +76,7 @@
   <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|ARM'">
     <PlatformTarget>ARM</PlatformTarget>
     <OutputPath>bin\ARM\Release\</OutputPath>
-    <DefineConstants>TRACE;FEATURE_DATAGRAMSOCKET;FEATURE_SOCKET_EAP;FEATURE_STREAM_TAP;FEATURE_THREAD_TAP;FEATURE_MEMORYSTREAM_TRYGETBUFFER;FEATURE_REFLECTION_TYPEINFO;FEATURE_ENCODING_ASCII</DefineConstants>
+    <DefineConstants>TRACE;FEATURE_DATAGRAMSOCKET;FEATURE_SOCKET_EAP;FEATURE_SOCKET_SETSOCKETOPTION;FEATURE_STREAM_TAP;FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_THREAD_TAP;FEATURE_MEMORYSTREAM_TRYGETBUFFER;FEATURE_REFLECTION_TYPEINFO;FEATURE_ENCODING_ASCII</DefineConstants>
     <Optimize>true</Optimize>
     <NoWarn>;2008</NoWarn>
     <DebugType>pdbonly</DebugType>
@@ -88,7 +88,7 @@
     <PlatformTarget>x64</PlatformTarget>
     <DebugSymbols>true</DebugSymbols>
     <OutputPath>bin\x64\Debug\</OutputPath>
-    <DefineConstants>TRACE;DEBUG;FEATURE_DATAGRAMSOCKET;FEATURE_SOCKET_EAP;FEATURE_STREAM_TAP;FEATURE_THREAD_TAP;FEATURE_MEMORYSTREAM_TRYGETBUFFER;FEATURE_REFLECTION_TYPEINFO;FEATURE_ENCODING_ASCII</DefineConstants>
+    <DefineConstants>TRACE;DEBUG;FEATURE_DATAGRAMSOCKET;FEATURE_SOCKET_EAP;FEATURE_SOCKET_SETSOCKETOPTION;FEATURE_STREAM_TAP;FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_THREAD_TAP;FEATURE_MEMORYSTREAM_TRYGETBUFFER;FEATURE_REFLECTION_TYPEINFO;FEATURE_ENCODING_ASCII</DefineConstants>
     <NoWarn>;2008</NoWarn>
     <DebugType>full</DebugType>
     <PlatformTarget>x64</PlatformTarget>
@@ -98,7 +98,7 @@
   <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
     <PlatformTarget>x64</PlatformTarget>
     <OutputPath>bin\x64\Release\</OutputPath>
-    <DefineConstants>TRACE;FEATURE_DATAGRAMSOCKET;FEATURE_SOCKET_EAP;FEATURE_STREAM_TAP;FEATURE_THREAD_TAP;FEATURE_MEMORYSTREAM_TRYGETBUFFER;FEATURE_REFLECTION_TYPEINFO;FEATURE_ENCODING_ASCII</DefineConstants>
+    <DefineConstants>TRACE;FEATURE_DATAGRAMSOCKET;FEATURE_SOCKET_EAP;FEATURE_SOCKET_SETSOCKETOPTION;FEATURE_STREAM_TAP;FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_THREAD_TAP;FEATURE_MEMORYSTREAM_TRYGETBUFFER;FEATURE_REFLECTION_TYPEINFO;FEATURE_ENCODING_ASCII</DefineConstants>
     <Optimize>true</Optimize>
     <NoWarn>;2008</NoWarn>
     <DebugType>pdbonly</DebugType>
@@ -228,6 +228,9 @@
     <Compile Include="..\Renci.SshNet\Common\ChannelRequestEventArgs.cs">
       <Link>Common\ChannelRequestEventArgs.cs</Link>
     </Compile>
+    <Compile Include="..\Renci.SshNet\Common\CountdownEvent.cs">
+      <Link>Common\CountdownEvent.cs</Link>
+    </Compile>
     <Compile Include="..\Renci.SshNet\Common\DerData.cs">
       <Link>Common\DerData.cs</Link>
     </Compile>
@@ -342,6 +345,9 @@
     <Compile Include="..\Renci.SshNet\ForwardedPortRemote.cs">
       <Link>ForwardedPortRemote.cs</Link>
     </Compile>
+    <Compile Include="..\Renci.SshNet\ForwardedPortStatus.cs">
+      <Link>ForwardedPortStatus.cs</Link>
+    </Compile>
     <Compile Include="..\Renci.SshNet\HashInfo.cs">
       <Link>HashInfo.cs</Link>
     </Compile>

+ 2 - 1
src/Renci.SshNet.VS2015.sln.DotSettings

@@ -9,4 +9,5 @@
 	<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UseNullPropagation/@EntryIndexedValue">DO_NOT_SHOW</s:String>
 	<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UseStringInterpolation/@EntryIndexedValue">DO_NOT_SHOW</s:String>
 	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=HMACMD/@EntryIndexedValue">HMACMD</s:String>
-	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=HMACSHA/@EntryIndexedValue">HMACSHA</s:String></wpf:ResourceDictionary>
+	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=HMACSHA/@EntryIndexedValue">HMACSHA</s:String>
+	<s:String x:Key="/Default/Environment/UnitTesting/ExcludedCategoriesList/@EntryValue">integration,LongRunning</s:String></wpf:ResourceDictionary>

+ 6 - 0
src/Renci.SshNet.WindowsPhone/Renci.SshNet.WindowsPhone.csproj

@@ -170,6 +170,9 @@
     <Compile Include="..\Renci.SshNet\Common\ChannelRequestEventArgs.cs">
       <Link>Common\ChannelRequestEventArgs.cs</Link>
     </Compile>
+    <Compile Include="..\Renci.SshNet\Common\CountdownEvent.cs">
+      <Link>Common\CountdownEvent.cs</Link>
+    </Compile>
     <Compile Include="..\Renci.SshNet\Common\DerData.cs">
       <Link>Common\DerData.cs</Link>
     </Compile>
@@ -278,6 +281,9 @@
     <Compile Include="..\Renci.SshNet\ForwardedPortRemote.cs">
       <Link>ForwardedPortRemote.cs</Link>
     </Compile>
+    <Compile Include="..\Renci.SshNet\ForwardedPortStatus.cs">
+      <Link>ForwardedPortStatus.cs</Link>
+    </Compile>
     <Compile Include="..\Renci.SshNet\HashInfo.cs">
       <Link>HashInfo.cs</Link>
     </Compile>

+ 187 - 58
src/Renci.SshNet.WindowsPhone/Session.WP.cs

@@ -13,65 +13,194 @@ namespace Renci.SshNet
         {
             if (message == null)
                 throw new ArgumentNullException("message");
-            else if (message is DisconnectMessage)
-                this.HandleMessage((DisconnectMessage)message);
-            else if (message is IgnoreMessage)
-                this.HandleMessage((IgnoreMessage)message);
-            else if (message is UnimplementedMessage)
-                this.HandleMessage((UnimplementedMessage)message);
-            else if (message is DebugMessage)
-                this.HandleMessage((DebugMessage)message);
-            else if (message is ServiceRequestMessage)
-                this.HandleMessage((ServiceRequestMessage)message);
-            else if (message is ServiceAcceptMessage)
-                this.HandleMessage((ServiceAcceptMessage)message);
-            else if (message is KeyExchangeInitMessage)
-                this.HandleMessage((KeyExchangeInitMessage)message);
-            else if (message is NewKeysMessage)
-                this.HandleMessage((NewKeysMessage)message);
-            else if (message is RequestMessage)
-                this.HandleMessage((RequestMessage)message);
-            else if (message is FailureMessage)
-                this.HandleMessage((FailureMessage)message);
-            else if (message is SuccessMessage)
-                this.HandleMessage((SuccessMessage)message);
-            else if (message is BannerMessage)
-                this.HandleMessage((BannerMessage)message);
-            else if (message is GlobalRequestMessage)
-                this.HandleMessage((GlobalRequestMessage)message);
-            else if (message is RequestSuccessMessage)
-                this.HandleMessage((RequestSuccessMessage)message);
-            else if (message is RequestFailureMessage)
-                this.HandleMessage((RequestFailureMessage)message);
-            else if (message is ChannelOpenMessage)
-                this.HandleMessage((ChannelOpenMessage)message);
-            else if (message is ChannelOpenConfirmationMessage)
-                this.HandleMessage((ChannelOpenConfirmationMessage)message);
-            else if (message is ChannelOpenFailureMessage)
-                this.HandleMessage((ChannelOpenFailureMessage)message);
-            else if (message is ChannelWindowAdjustMessage)
-                this.HandleMessage((ChannelWindowAdjustMessage)message);
-            else if (message is ChannelDataMessage)
-                this.HandleMessage((ChannelDataMessage)message);
-            else if (message is ChannelExtendedDataMessage)
-                this.HandleMessage((ChannelExtendedDataMessage)message);
-            else if (message is ChannelEofMessage)
-                this.HandleMessage((ChannelEofMessage)message);
-            else if (message is ChannelCloseMessage)
-                this.HandleMessage((ChannelCloseMessage)message);
-            else if (message is ChannelRequestMessage)
-                this.HandleMessage((ChannelRequestMessage)message);
-            else if (message is ChannelSuccessMessage)
-                this.HandleMessage((ChannelSuccessMessage)message);
-            else if (message is ChannelFailureMessage)
-                this.HandleMessage((ChannelFailureMessage)message);
-            else
-            {
-                Debug.WriteLine("SSH.NET WARNING: unknown message type {0} - may need to add new type to Session.WP.cs, HandleMessageCore method",
-                    message.GetType().FullName);
-
-                this.HandleMessage(message);
+
+            var disconnectMessage = message as DisconnectMessage;
+            if (disconnectMessage != null)
+            {
+                HandleMessage(disconnectMessage);
+                return;
+            }
+
+            var serviceRequestMessage = message as ServiceRequestMessage;
+            if (serviceRequestMessage != null)
+            {
+                HandleMessage(serviceRequestMessage);
+                return;
+            }
+
+            var serviceAcceptMessage = message as ServiceAcceptMessage;
+            if (serviceAcceptMessage != null)
+            {
+                HandleMessage(serviceAcceptMessage);
+                return;
+            }
+
+            var keyExchangeInitMessage = message as KeyExchangeInitMessage;
+            if (keyExchangeInitMessage != null)
+            {
+                HandleMessage(keyExchangeInitMessage);
+                return;
+            }
+
+            var newKeysMessage = message as NewKeysMessage;
+            if (newKeysMessage != null)
+            {
+                HandleMessage(newKeysMessage);
+                return;
+            }
+
+            var requestMessage = message as RequestMessage;
+            if (requestMessage != null)
+            {
+                HandleMessage(requestMessage);
+                return;
+            }
+
+            var failureMessage = message as FailureMessage;
+            if (failureMessage != null)
+            {
+                HandleMessage(failureMessage);
+                return;
             }
+
+            var successMessage = message as SuccessMessage;
+            if (successMessage != null)
+            {
+                HandleMessage(successMessage);
+                return;
+            }
+
+            var bannerMessage = message as BannerMessage;
+            if (bannerMessage != null)
+            {
+                HandleMessage(bannerMessage);
+                return;
+            }
+
+            var globalRequestMessage = message as GlobalRequestMessage;
+            if (globalRequestMessage != null)
+            {
+                HandleMessage(globalRequestMessage);
+                return;
+            }
+
+            var requestSuccessMessage = message as RequestSuccessMessage;
+            if (requestSuccessMessage != null)
+            {
+                HandleMessage(requestSuccessMessage);
+                return;
+            }
+
+            var requestFailureMessage = message as RequestFailureMessage;
+            if (requestFailureMessage != null)
+            {
+                HandleMessage(requestFailureMessage);
+                return;
+            }
+
+            var channelOpenMessage = message as ChannelOpenMessage;
+            if (channelOpenMessage != null)
+            {
+                HandleMessage(channelOpenMessage);
+                return;
+            }
+
+            var channelOpenConfirmationMessage = message as ChannelOpenConfirmationMessage;
+            if (channelOpenConfirmationMessage != null)
+            {
+                HandleMessage(channelOpenConfirmationMessage);
+                return;
+            }
+
+            var channelOpenFailureMessage = message as ChannelOpenFailureMessage;
+            if (channelOpenFailureMessage != null)
+            {
+                HandleMessage(channelOpenFailureMessage);
+                return;
+            }
+
+            var channelWindowAdjustMessage = message as ChannelWindowAdjustMessage;
+            if (channelWindowAdjustMessage != null)
+            {
+                HandleMessage(channelWindowAdjustMessage);
+                return;
+            }
+
+            var channelDataMessage = message as ChannelDataMessage;
+            if (channelDataMessage != null)
+            {
+                HandleMessage(channelDataMessage);
+                return;
+            }
+
+            var channelExtendedDataMessage = message as ChannelExtendedDataMessage;
+            if (channelExtendedDataMessage != null)
+            {
+                HandleMessage(channelExtendedDataMessage);
+                return;
+            }
+
+            var channelEofMessage = message as ChannelEofMessage;
+            if (channelEofMessage != null)
+            {
+                HandleMessage(channelEofMessage);
+                return;
+            }
+
+            var channelCloseMessage = message as ChannelCloseMessage;
+            if (channelCloseMessage != null)
+            {
+                HandleMessage(channelCloseMessage);
+                return;
+            }
+
+            var channelRequestMessage = message as ChannelRequestMessage;
+            if (channelRequestMessage != null)
+            {
+                HandleMessage(channelRequestMessage);
+                return;
+            }
+
+            var channelSuccessMessage = message as ChannelSuccessMessage;
+            if (channelSuccessMessage != null)
+            {
+                HandleMessage(channelSuccessMessage);
+                return;
+            }
+
+            var channelFailureMessage = message as ChannelFailureMessage;
+            if (channelFailureMessage != null)
+            {
+                HandleMessage(channelFailureMessage);
+                return;
+            }
+
+            var ignoreMessage = message as IgnoreMessage;
+            if (ignoreMessage != null)
+            {
+                HandleMessage(ignoreMessage);
+                return;
+            }
+
+            var unimplementedMessage = message as UnimplementedMessage;
+            if (unimplementedMessage != null)
+            {
+                HandleMessage(unimplementedMessage);
+                return;
+            }
+
+            var debugMessage = message as DebugMessage;
+            if (debugMessage != null)
+            {
+                HandleMessage(debugMessage);
+                return;
+            }
+
+            Debug.WriteLine(
+                "SSH.NET WARNING: unknown message type {0} - may need to add new type to Session.WP.cs, HandleMessageCore method",
+                message.GetType().FullName);
+
+            HandleMessage(message);
         }
     }
 }

+ 15 - 3
src/Renci.SshNet.WindowsPhone8/Renci.SshNet.WindowsPhone8.csproj

@@ -23,7 +23,7 @@
     <DebugType>full</DebugType>
     <Optimize>false</Optimize>
     <OutputPath>Bin\Debug</OutputPath>
-    <DefineConstants>TRACE;DEBUG;FEATURE_REGEX_COMPILE;FEATURE_RNG_CSP;FEATURE_SOCKET_EAP;FEATURE_STREAM_APM;FEATURE_STREAM_TAP;FEATURE_DEVICEINFORMATION_APM;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_HASH_SHA1_MANAGED;FEATURE_HASH_SHA256_MANAGED;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256</DefineConstants>
+    <DefineConstants>TRACE;DEBUG;FEATURE_REGEX_COMPILE;FEATURE_RNG_CSP;FEATURE_SOCKET_EAP;FEATURE_STREAM_APM;FEATURE_STREAM_TAP;FEATURE_DEVICEINFORMATION_APM;FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_HASH_SHA1_MANAGED;FEATURE_HASH_SHA256_MANAGED;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256</DefineConstants>
     <NoStdLib>true</NoStdLib>
     <NoConfig>true</NoConfig>
     <ErrorReport>prompt</ErrorReport>
@@ -36,7 +36,7 @@
     <DebugType>none</DebugType>
     <Optimize>true</Optimize>
     <OutputPath>Bin\Release</OutputPath>
-    <DefineConstants>TRACE;FEATURE_REGEX_COMPILE;FEATURE_RNG_CSP;FEATURE_SOCKET_EAP;FEATURE_STREAM_APM;FEATURE_STREAM_TAP;FEATURE_DEVICEINFORMATION_APM;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_HASH_SHA1_MANAGED;FEATURE_HASH_SHA256_MANAGED;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256</DefineConstants>
+    <DefineConstants>TRACE;FEATURE_REGEX_COMPILE;FEATURE_RNG_CSP;FEATURE_SOCKET_EAP;FEATURE_STREAM_APM;FEATURE_STREAM_TAP;FEATURE_DEVICEINFORMATION_APM;FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_HASH_SHA1_MANAGED;FEATURE_HASH_SHA256_MANAGED;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256</DefineConstants>
     <NoStdLib>true</NoStdLib>
     <NoConfig>true</NoConfig>
     <ErrorReport>prompt</ErrorReport>
@@ -214,6 +214,9 @@
     <Compile Include="..\Renci.SshNet\Common\ChannelRequestEventArgs.cs">
       <Link>Common\ChannelRequestEventArgs.cs</Link>
     </Compile>
+    <Compile Include="..\Renci.SshNet\Common\CountdownEvent.cs">
+      <Link>Common\CountdownEvent.cs</Link>
+    </Compile>
     <Compile Include="..\Renci.SshNet\Common\DerData.cs">
       <Link>Common\DerData.cs</Link>
     </Compile>
@@ -316,12 +319,21 @@
     <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>
+    <Compile Include="..\Renci.SshNet\ForwardedPortStatus.cs">
+      <Link>ForwardedPortStatus.cs</Link>
+    </Compile>
     <Compile Include="..\Renci.SshNet\HashInfo.cs">
       <Link>HashInfo.cs</Link>
     </Compile>
@@ -937,7 +949,7 @@
   <Import Project="$(MSBuildExtensionsPath)\Microsoft\$(TargetFrameworkIdentifier)\$(TargetFrameworkVersion)\Microsoft.$(TargetFrameworkIdentifier).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. 

+ 3 - 2
src/Renci.SshNet/Abstractions/CryptoAbstraction.cs

@@ -1,8 +1,9 @@
 using System;
+using Renci.SshNet.Security.Cryptography;
 
-namespace Renci.SshNet.Security.Cryptography
+namespace Renci.SshNet.Abstractions
 {
-    internal static class HashAlgorithmFactory
+    internal static class CryptoAbstraction
     {
 #if FEATURE_RNG_CREATE || FEATURE_RNG_CSP
         private static readonly System.Security.Cryptography.RandomNumberGenerator Randomizer = CreateRandomNumberGenerator();

+ 6 - 17
src/Renci.SshNet/Channels/Channel.cs

@@ -341,9 +341,6 @@ namespace Renci.SshNet.Channels
         /// </summary>
         public void Close()
         {
-#if DEBUG_GERT
-            Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | Channel.Close");
-#endif // DEBUG_GERT
             Close(true);
         }
 
@@ -408,10 +405,6 @@ namespace Renci.SshNet.Channels
         {
             _closeMessageReceived = true;
 
-#if DEBUG_GERT
-            Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | Channel.OnClose()");
-#endif // DEBUG_GERT
-
             // close the channel
             Close(false);
 
@@ -819,12 +812,12 @@ namespace Renci.SshNet.Channels
             } while (true);
         }
 
-        private InvalidOperationException CreateRemoteChannelInfoNotAvailableException()
+        private static InvalidOperationException CreateRemoteChannelInfoNotAvailableException()
         {
             throw new InvalidOperationException("The channel has not been opened, or the open has not yet been confirmed.");
         }
 
-        private InvalidOperationException CreateChannelClosedException()
+        private static InvalidOperationException CreateChannelClosedException()
         {
             throw new InvalidOperationException("The channel is closed.");
         }
@@ -853,15 +846,12 @@ namespace Renci.SshNet.Channels
 
             if (disposing)
             {
-#if DEBUG_GERT
-                Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | Channel.Dipose(bool)");
-#endif // DEBUG_GERT
-
                 Close(false);
 
                 var session = _session;
                 if (session != null)
                 {
+                    _session = null;
                     session.ChannelWindowAdjustReceived -= OnChannelWindowAdjust;
                     session.ChannelDataReceived -= OnChannelData;
                     session.ChannelExtendedDataReceived -= OnChannelExtendedData;
@@ -872,28 +862,27 @@ namespace Renci.SshNet.Channels
                     session.ChannelFailureReceived -= OnChannelFailure;
                     session.ErrorOccured -= Session_ErrorOccured;
                     session.Disconnected -= Session_Disconnected;
-                    _session = null;
                 }
 
                 var channelClosedWaitHandle = _channelClosedWaitHandle;
                 if (channelClosedWaitHandle != null)
                 {
-                    channelClosedWaitHandle.Dispose();
                     _channelClosedWaitHandle = null;
+                    channelClosedWaitHandle.Dispose();
                 }
 
                 var channelServerWindowAdjustWaitHandle = _channelServerWindowAdjustWaitHandle;
                 if (channelServerWindowAdjustWaitHandle != null)
                 {
-                    channelServerWindowAdjustWaitHandle.Dispose();
                     _channelServerWindowAdjustWaitHandle = null;
+                    channelServerWindowAdjustWaitHandle.Dispose();
                 }
 
                 var errorOccuredWaitHandle = _errorOccuredWaitHandle;
                 if (errorOccuredWaitHandle != null)
                 {
-                    errorOccuredWaitHandle.Dispose();
                     _errorOccuredWaitHandle = null;
+                    errorOccuredWaitHandle.Dispose();
                 }
 
                 _isDisposed = true;

+ 2 - 56
src/Renci.SshNet/Channels/ChannelDirectTcpip.cs

@@ -56,24 +56,11 @@ namespace Renci.SshNet.Channels
 
             var ep = (IPEndPoint) socket.RemoteEndPoint;
 
-#if DEBUG_GERT
-            Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " |  ChannelOpenMessage send '" + LocalChannelNumber + "' | " + DateTime.Now.ToString("hh:mm:ss.fff"));
-#endif // DEBUG_GERT
-
             // open channel
             SendMessage(new ChannelOpenMessage(LocalChannelNumber, LocalWindowSize, LocalPacketSize,
                 new DirectTcpipChannelInfo(remoteHost, port, ep.Address.ToString(), (uint) ep.Port)));
-
-#if DEBUG_GERT
-            Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " |  ChannelOpenMessage sent '" + LocalChannelNumber + "' | " + DateTime.Now.ToString("hh:mm:ss.fff"));
-#endif // DEBUG_GERT
-
             //  Wait for channel to open
             WaitOnHandle(_channelOpen);
-
-#if DEBUG_GERT
-            Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " |  ChannelOpenMessage flagged '" + LocalChannelNumber + "' | " + DateTime.Now.ToString("hh:mm:ss.fff"));
-#endif // DEBUG_GERT
         }
 
         /// <summary>
@@ -101,10 +88,6 @@ namespace Renci.SshNet.Channels
 
             SocketAbstraction.ReadContinuous(_socket, buffer, 0, buffer.Length, SendData);
 
-#if DEBUG_GERT
-            Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | ChannelDirectTcpip.Bind (after) '" + LocalChannelNumber + "' | " + DateTime.Now.ToString("hh:mm:ss.fff"));
-#endif // DEBUG_GERT
-
             // even though the client has disconnected, we still want to properly close the
             // channel
             //
@@ -126,10 +109,6 @@ namespace Renci.SshNet.Channels
                 if (_socket == null)
                     return;
 
-#if DEBUG_GERT
-                Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | ChannelDirectTcpip.CloseSocket '" + LocalChannelNumber + "' | " + DateTime.Now.ToString("hh:mm:ss.fff"));
-#endif // DEBUG_GERT
-
                 // closing a socket actually disposes the socket, so we can safely dereference
                 // the field to avoid entering the lock again later
                 _socket.Dispose();
@@ -148,10 +127,6 @@ namespace Renci.SshNet.Channels
 
             lock (_socketLock)
             {
-#if DEBUG_GERT
-                Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | ChannelDirectTcpip.ShutdownSocket '" + LocalChannelNumber + "' | " + DateTime.Now.ToString("hh:mm:ss.fff"));
-#endif // DEBUG_GERT
-
                 if (_socket == null || !_socket.Connected)
                     return;
 
@@ -166,10 +141,6 @@ namespace Renci.SshNet.Channels
         /// <param name="wait"><c>true</c> to wait for the SSH_MSG_CHANNEL_CLOSE message to be received from the server; otherwise, <c>false</c>.</param>
         protected override void Close(bool wait)
         {
-#if DEBUG_GERT
-            Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | ChannelDirectTcpip.Close(bool) '" + LocalChannelNumber + "' | " + DateTime.Now.ToString("hh:mm:ss.fff"));
-#endif // DEBUG_GERT
-
             var forwardedPort = _forwardedPort;
             if (forwardedPort != null)
             {
@@ -221,10 +192,6 @@ namespace Renci.SshNet.Channels
             base.OnOpenConfirmation(remoteChannelNumber, initialWindowSize, maximumPacketSize);
 
             _channelOpen.Set();
-
-#if DEBUG_GERT
-            Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " |  ChannelOpenMessage confirmed '" + LocalChannelNumber + "' | " + DateTime.Now.ToString("hh:mm:ss.fff"));
-#endif // DEBUG_GERT
         }
 
         protected override void OnOpenFailure(uint reasonCode, string description, string language)
@@ -232,10 +199,6 @@ namespace Renci.SshNet.Channels
             base.OnOpenFailure(reasonCode, description, language);
 
             _channelOpen.Set();
-
-#if DEBUG_GERT
-            Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " |  ChannelOpenMessage failure '" + LocalChannelNumber + "' | " + DateTime.Now.ToString("hh:mm:ss.fff"));
-#endif // DEBUG_GERT
         }
 
         /// <summary>
@@ -245,11 +208,6 @@ namespace Renci.SshNet.Channels
         {
             base.OnEof();
 
-#if DEBUG_GERT
-            Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | ChannelDirectTcpip.OnEof '" + LocalChannelNumber + "' | " +
-                              DateTime.Now.ToString("hh:mm:ss"));
-#endif // DEBUG_GERT
-
             // the channel will send no more data, and hence it does not make sense to receive
             // any more data from the client to send to the remote party (and we surely won't
             // send anything anymore)
@@ -266,10 +224,6 @@ namespace Renci.SshNet.Channels
         {
             base.OnErrorOccured(exp);
 
-#if DEBUG_GERT
-            Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | ChannelDirectTcpip.OnErrorOccured '" + LocalChannelNumber + "' | " + DateTime.Now.ToString("hh:mm:ss"));
-#endif // DEBUG_GERT
-
             // signal to the client that we will not send anything anymore; this will also interrupt the
             // blocking receive in Bind if the client sends FIN/ACK in time
             //
@@ -288,10 +242,6 @@ namespace Renci.SshNet.Channels
         {
             base.OnDisconnected();
 
-#if DEBUG_GERT
-            Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | ChannelDirectTcpip.OnDisconnected '" + LocalChannelNumber + "' | " + DateTime.Now.ToString("hh:mm:ss"));
-#endif // DEBUG_GERT
-
             // the channel will accept or send no more data, and hence it does not make sense
             // to accept any more data from the client (and we surely won't send anything
             // anymore)
@@ -310,10 +260,6 @@ namespace Renci.SshNet.Channels
 
             if (disposing)
             {
-#if DEBUG_GERT
-                Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | Dispose '" + LocalChannelNumber + "' | " + DateTime.Now.ToString("hh:mm:ss"));
-#endif // DEBUG_GERT
-
                 if (_socket != null)
                 {
                     lock (_socketLock)
@@ -329,15 +275,15 @@ namespace Renci.SshNet.Channels
                 var channelOpen = _channelOpen;
                 if (channelOpen != null)
                 {
-                    channelOpen.Dispose();
                     _channelOpen = null;
+                    channelOpen.Dispose();
                 }
 
                 var channelData = _channelData;
                 if (channelData != null)
                 {
-                    channelData.Dispose();
                     _channelData = null;
+                    channelData.Dispose();
                 }
             }
         }

+ 0 - 26
src/Renci.SshNet/Channels/ChannelForwardedTcpip.cs

@@ -1,7 +1,6 @@
 using System;
 using System.Net;
 using System.Net.Sockets;
-using System.Threading;
 using Renci.SshNet.Abstractions;
 using Renci.SshNet.Common;
 using Renci.SshNet.Messages.Connection;
@@ -95,10 +94,6 @@ namespace Renci.SshNet.Channels
         /// </summary>
         private void ForwardedPort_Closing(object sender, EventArgs eventArgs)
         {
-#if DEBUG_GERT
-            Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | ChannelForwardedTcpip.ForwardedPort_Closing");
-#endif // DEBUG_GERT
-
             // signal to the server that we will not send anything anymore; this will also interrupt the
             // blocking receive in Bind if the server sends FIN/ACK in time
             //
@@ -112,10 +107,6 @@ namespace Renci.SshNet.Channels
         /// <param name="how">One of the <see cref="SocketShutdown"/> values that specifies the operation that will no longer be allowed.</param>
         private void ShutdownSocket(SocketShutdown how)
         {
-#if DEBUG_GERT
-            Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | ChannelForwardedTcpip.ShutdownSocket");
-#endif // DEBUG_GERT
-
             if (_socket == null || !_socket.Connected)
                 return;
 
@@ -139,10 +130,6 @@ namespace Renci.SshNet.Channels
 
             lock (_socketShutdownAndCloseLock)
             {
-#if DEBUG_GERT
-                Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | ChannelForwardedTcpip.CloseSocket");
-#endif // DEBUG_GERT
-
                 var socket = _socket;
                 if (socket != null)
                 {
@@ -161,10 +148,6 @@ namespace Renci.SshNet.Channels
         /// <param name="wait"><c>true</c> to wait for the SSH_MSG_CHANNEL_CLOSE message to be received from the server; otherwise, <c>false</c>.</param>
         protected override void Close(bool wait)
         {
-#if DEBUG_GERT
-            Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | ChannelForwardedTcpip.Close");
-#endif // DEBUG_GERT
-
             var forwardedPort = _forwardedPort;
             if (forwardedPort != null)
             {
@@ -185,15 +168,6 @@ namespace Renci.SshNet.Channels
             CloseSocket();
         }
 
-#if DEBUG_GERT
-        protected override void OnClose()
-        {
-            base.OnClose();
-
-            Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | OnClose");
-        }
-#endif // DEBUG_GERT
-
         /// <summary>
         /// Called when channel data is received.
         /// </summary>

+ 1 - 1
src/Renci.SshNet/ClientAuthentication.cs

@@ -108,7 +108,7 @@ namespace Renci.SshNet
             return false;
         }
 
-        private IEnumerable<IAuthenticationMethod> GetOrderedAuthenticationMethods(AuthenticationState authenticationState, IEnumerable<IAuthenticationMethod> matchingAuthenticationMethods)
+        private static IEnumerable<IAuthenticationMethod> GetOrderedAuthenticationMethods(AuthenticationState authenticationState, IEnumerable<IAuthenticationMethod> matchingAuthenticationMethods)
         {
             var skippedAuthenticationMethods = new List<IAuthenticationMethod>();
 

+ 2 - 2
src/Renci.SshNet/Common/BigInteger.cs

@@ -51,7 +51,7 @@ using System;
 using System.Collections.Generic;
 using System.Diagnostics.CodeAnalysis;
 using System.Globalization;
-using Renci.SshNet.Security.Cryptography;
+using Renci.SshNet.Abstractions;
 
 /*
 Optimization
@@ -174,7 +174,7 @@ namespace Renci.SshNet.Common
         public static BigInteger Random(int bitLength)
         {
             var bytesArray = new byte[bitLength / 8 + (((bitLength % 8) > 0) ? 1 : 0)];
-            HashAlgorithmFactory.GenerateRandom(bytesArray);
+            CryptoAbstraction.GenerateRandom(bytesArray);
             bytesArray[bytesArray.Length - 1] = (byte)(bytesArray[bytesArray.Length - 1] & 0x7F);   //  Ensure not a negative value
             return new BigInteger(bytesArray);
         }

+ 153 - 0
src/Renci.SshNet/Common/CountdownEvent.cs

@@ -0,0 +1,153 @@
+#if !FEATURE_THREAD_COUNTDOWNEVENT
+
+using System;
+using System.Threading;
+
+namespace Renci.SshNet.Common
+{
+    /// <summary>
+    /// Represents a synchronization primitive that is signaled when its count reaches zero.
+    /// </summary>
+    internal class CountdownEvent : IDisposable
+    {
+        private int _count;
+        private ManualResetEvent _event;
+        private bool _disposed;
+
+        /// <summary>
+        /// Initializes a new instance of <see cref="CountdownEvent"/> class with the specified count.
+        /// </summary>
+        /// <param name="initialCount">The number of signals initially required to set the <see cref="CountdownEvent"/>.</param>
+        /// <exception cref="ArgumentOutOfRangeException"><paramref name="initialCount"/> is less than zero.</exception>
+        /// <remarks>
+        /// If <paramref name="initialCount"/> is <c>zero</c>, the event is created in a signaled state.
+        /// </remarks>
+        public CountdownEvent(int initialCount)
+        {
+            if (initialCount < 0)
+            {
+                throw new ArgumentOutOfRangeException("initialCount");
+            }
+
+            _count = initialCount;
+
+            var initialState = _count == 0;
+            _event = new ManualResetEvent(initialState);
+        }
+
+        /// <summary>
+        /// Gets the number of remaining signals required to set the event.
+        /// </summary>
+        /// <value>
+        /// The number of remaining signals required to set the event.
+        /// </value>
+        public int CurrentCount
+        {
+            get { return _count; }
+        }
+
+        /// <summary>
+        /// Indicates whether the <see cref="CountdownEvent"/>'s current count has reached zero.
+        /// </summary>
+        /// <value>
+        /// <c>true</c> if the current count is zero; otherwise, <c>false</c>.
+        /// </value>
+        public bool IsSet
+        {
+            get { return _count == 0; }
+        }
+
+        /// <summary>
+        /// Registers a signal with the <see cref="CountdownEvent"/>, decrementing the value of <see cref="CurrentCount"/>.
+        /// </summary>
+        /// <returns>
+        /// <c>true</c> if the signal caused the count to reach zero and the event was set; otherwise, <c>false</c>.
+        /// </returns>
+        /// <exception cref="ObjectDisposedException">The current instance has already been disposed.</exception>
+        /// <exception cref="InvalidOperationException">The current instance is already set.</exception>
+        public bool Signal()
+        {
+            EnsureNotDisposed();
+
+            if (_count <= 0)
+                throw new InvalidOperationException("Invalid attempt made to decrement the event's count below zero.");
+
+            var newCount = Interlocked.Decrement(ref _count);
+            if (newCount == 0)
+            {
+                _event.Set();
+                return true;
+            }
+
+            return false;
+        }
+
+        /// <summary>
+        /// Increments the <see cref="CountdownEvent"/>'s current count by one.
+        /// </summary>
+        /// <exception cref="ObjectDisposedException">The current instance has already been disposed.</exception>
+        /// <exception cref="InvalidOperationException">The current instance is already set.</exception>
+        /// <exception cref="InvalidOperationException"><see cref="CurrentCount"/> is equal to or greather than <see cref="int.MaxValue"/>.</exception>
+        public void AddCount()
+        {
+            EnsureNotDisposed();
+
+            if (_count == int.MaxValue)
+                throw new InvalidOperationException("TODO");
+
+            Interlocked.Increment(ref _count);
+        }
+
+        /// <summary>
+        /// Blocks the current thread until the <see cref="CountdownEvent"/> is set, using a <see cref="TimeSpan"/>
+        /// to measure the timeout.
+        /// </summary>
+        /// <param name="timeout">A <see cref="TimeSpan"/> that represents the number of milliseconds to wait, or a <see cref="TimeSpan"/> that represents -1 milliseconds to wait indefinitely.</param>
+        /// <returns>
+        /// <c>true</c> if the <see cref="CountdownEvent"/> was set; otherwise, <c>false</c>.
+        /// </returns>
+        /// <exception cref="ObjectDisposedException">The current instance has already been disposed.</exception>
+        public bool Wait(TimeSpan timeout)
+        {
+            EnsureNotDisposed();
+
+            return _event.WaitOne(timeout);
+        }
+
+        /// <summary>
+        /// Releases all resources used by the current instance of the <see cref="CountdownEvent"/> class.
+        /// </summary>
+        public void Dispose()
+        {
+            Dispose(true);
+            GC.SuppressFinalize(this);
+        }
+
+        /// <summary>
+        /// Releases the unmanaged resources used by the <see cref="CountdownEvent"/>, and optionally releases the managed resources.
+        /// </summary>
+        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
+        protected virtual void Dispose(bool disposing)
+        {
+            if (disposing)
+            {
+                var theEvent = _event;
+                if (theEvent != null)
+                {
+                    _event = null;
+                    theEvent.Dispose();
+                }
+
+                _disposed = true;
+            }
+        }
+
+        private void EnsureNotDisposed()
+        {
+            if (_disposed)
+                throw new ObjectDisposedException(GetType().Name);
+        }
+    }
+}
+
+#endif // FEATURE_THREAD_COUNTDOWNEVENT

+ 2 - 2
src/Renci.SshNet/Common/HostKeyEventArgs.cs

@@ -1,5 +1,5 @@
 using System;
-using Renci.SshNet.Security.Cryptography;
+using Renci.SshNet.Abstractions;
 using Renci.SshNet.Security;
 
 namespace Renci.SshNet.Common
@@ -54,7 +54,7 @@ namespace Renci.SshNet.Common
 
             KeyLength = host.Key.KeyLength;
 
-            using (var md5 = HashAlgorithmFactory.CreateMD5())
+            using (var md5 = CryptoAbstraction.CreateMD5())
             {
                 FingerPrint = md5.ComputeHash(host.Data);
             }

+ 1 - 1
src/Renci.SshNet/Common/NetConfServerException.cs

@@ -11,7 +11,7 @@ namespace Renci.SshNet.Common
 #if FEATURE_BINARY_SERIALIZATION
     [Serializable]
 #endif // FEATURE_BINARY_SERIALIZATION
-    public partial class NetConfServerException : SshException
+    public class NetConfServerException : SshException
     {
         /// <summary>
         /// Initializes a new instance of the <see cref="NetConfServerException"/> class.

+ 1 - 1
src/Renci.SshNet/Common/ProxyException.cs

@@ -11,7 +11,7 @@ namespace Renci.SshNet.Common
 #if FEATURE_BINARY_SERIALIZATION
     [Serializable]
 #endif // FEATURE_BINARY_SERIALIZATION
-    public partial class ProxyException : SshException
+    public class ProxyException : SshException
     {
         /// <summary>
         /// Initializes a new instance of the <see cref="ScpException"/> class.

+ 1 - 1
src/Renci.SshNet/Common/SshException.cs

@@ -11,7 +11,7 @@ namespace Renci.SshNet.Common
 #if FEATURE_BINARY_SERIALIZATION
     [Serializable]
 #endif // FEATURE_BINARY_SERIALIZATION
-    public partial class SshException : Exception
+    public class SshException : Exception
     {
         /// <summary>
         /// Initializes a new instance of the <see cref="SshException"/> class.

+ 11 - 11
src/Renci.SshNet/ConnectionInfo.cs

@@ -2,11 +2,11 @@
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
+using Renci.SshNet.Abstractions;
 using Renci.SshNet.Security;
 using Renci.SshNet.Messages.Connection;
 using Renci.SshNet.Common;
 using Renci.SshNet.Messages.Authentication;
-using Renci.SshNet.Security.Cryptography;
 using Renci.SshNet.Security.Cryptography.Ciphers.Modes;
 using Renci.SshNet.Security.Cryptography.Ciphers;
 
@@ -332,17 +332,17 @@ namespace Renci.SshNet
 
             HmacAlgorithms = new Dictionary<string, HashInfo>
                 {
-                    {"hmac-md5", new HashInfo(16*8, HashAlgorithmFactory.CreateHMACMD5)},
-                    {"hmac-md5-96", new HashInfo(16*8, key => HashAlgorithmFactory.CreateHMACMD5(key, 96))},
-                    {"hmac-sha1", new HashInfo(20*8, HashAlgorithmFactory.CreateHMACSHA1)},
-                    {"hmac-sha1-96", new HashInfo(20*8, key => HashAlgorithmFactory.CreateHMACSHA1(key, 96))},
-                    {"hmac-sha2-256", new HashInfo(32*8, HashAlgorithmFactory.CreateHMACSHA256)},
-                    {"hmac-sha2-256-96", new HashInfo(32*8, key => HashAlgorithmFactory.CreateHMACSHA256(key, 96))},
-                    {"hmac-sha2-512", new HashInfo(64 * 8, HashAlgorithmFactory.CreateHMACSHA512)},
-                    {"hmac-sha2-512-96", new HashInfo(64 * 8,  key => HashAlgorithmFactory.CreateHMACSHA512(key, 96))},
+                    {"hmac-md5", new HashInfo(16*8, CryptoAbstraction.CreateHMACMD5)},
+                    {"hmac-md5-96", new HashInfo(16*8, key => CryptoAbstraction.CreateHMACMD5(key, 96))},
+                    {"hmac-sha1", new HashInfo(20*8, CryptoAbstraction.CreateHMACSHA1)},
+                    {"hmac-sha1-96", new HashInfo(20*8, key => CryptoAbstraction.CreateHMACSHA1(key, 96))},
+                    {"hmac-sha2-256", new HashInfo(32*8, CryptoAbstraction.CreateHMACSHA256)},
+                    {"hmac-sha2-256-96", new HashInfo(32*8, key => CryptoAbstraction.CreateHMACSHA256(key, 96))},
+                    {"hmac-sha2-512", new HashInfo(64 * 8, CryptoAbstraction.CreateHMACSHA512)},
+                    {"hmac-sha2-512-96", new HashInfo(64 * 8,  key => CryptoAbstraction.CreateHMACSHA512(key, 96))},
                     //{"umac-64@openssh.com", typeof(HMacSha1)},
-                    {"hmac-ripemd160", new HashInfo(160, HashAlgorithmFactory.CreateHMACRIPEMD160)},
-                    {"hmac-ripemd160@openssh.com", new HashInfo(160, HashAlgorithmFactory.CreateHMACRIPEMD160)},
+                    {"hmac-ripemd160", new HashInfo(160, CryptoAbstraction.CreateHMACRIPEMD160)},
+                    {"hmac-ripemd160@openssh.com", new HashInfo(160, CryptoAbstraction.CreateHMACRIPEMD160)},
                     //{"none", typeof(...)},
                 };
 

+ 11 - 10
src/Renci.SshNet/ForwardedPort.cs

@@ -31,7 +31,7 @@ namespace Renci.SshNet
         }
 
         /// <summary>
-        /// Gets or sets a value indicating whether port forwarding is started.
+        /// Gets a value indicating whether port forwarding is started.
         /// </summary>
         /// <value>
         /// <c>true</c> if port forwarding is started; otherwise, <c>false</c>.
@@ -73,10 +73,10 @@ namespace Renci.SshNet
         {
             CheckDisposed();
 
-            if (!IsStarted)
-                return;
-
-            StopPort(Session.ConnectionInfo.Timeout);
+            if (IsStarted)
+            {
+                StopPort(Session.ConnectionInfo.Timeout);
+            }
         }
 
         /// <summary>
@@ -93,9 +93,10 @@ namespace Renci.SshNet
         {
             RaiseClosing();
 
-            if (Session != null)
+            var session = Session;
+            if (session != null)
             {
-                Session.ErrorOccured -= Session_ErrorOccured;
+                session.ErrorOccured -= Session_ErrorOccured;
             }
         }
 
@@ -107,10 +108,10 @@ namespace Renci.SshNet
         {
             if (disposing)
             {
-                if (Session != null)
+                var session = Session;
+                if (session != null)
                 {
-                    Session.ErrorOccured -= Session_ErrorOccured;
-                    StopPort(Session.ConnectionInfo.Timeout);
+                    StopPort(session.ConnectionInfo.Timeout);
                     Session = null;
                 }
             }

+ 179 - 244
src/Renci.SshNet/ForwardedPortDynamic.NET.cs

@@ -1,5 +1,4 @@
 using System;
-using System.Diagnostics;
 using System.Linq;
 using System.Text;
 using System.Net;
@@ -14,10 +13,12 @@ namespace Renci.SshNet
     public partial class ForwardedPortDynamic
     {
         private Socket _listener;
-        private int _pendingRequests;
+        private CountdownEvent _pendingChannelCountdown;
 
         partial void InternalStart()
         {
+            InitializePendingChannelCountdown();
+
             var ip = IPAddress.Any;
             if (!string.IsNullOrEmpty(BoundHost))
             {
@@ -26,242 +27,195 @@ namespace Renci.SshNet
 
             var ep = new IPEndPoint(ip, (int) BoundPort);
 
-            _listener = new Socket(ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
-            // TODO: decide if we want to have blocking socket
-#if FEATURE_SOCKET_SETSOCKETOPTION
-            _listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, true);
-            _listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.NoDelay, true);
-#endif //FEATURE_SOCKET_SETSOCKETOPTION
+            _listener = new Socket(ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp) {NoDelay = true};
             _listener.Bind(ep);
             _listener.Listen(5);
 
             Session.ErrorOccured += Session_ErrorOccured;
             Session.Disconnected += Session_Disconnected;
 
-            _listenerCompleted = new ManualResetEvent(false);
+            // consider port started when we're listening for inbound connections
+            _status = ForwardedPortStatus.Started;
+
+            StartAccept(null);
+        }
+
+        private void StartAccept(SocketAsyncEventArgs e)
+        {
+            if (e == null)
+            {
+                e = new SocketAsyncEventArgs();
+                e.Completed += AcceptCompleted;
+            }
+            else
+            {
+                // clear the socket as we're reusing the context object
+                e.AcceptSocket = null;
+            }
 
-            ThreadAbstraction.ExecuteThread(() =>
+            // only accept new connections while we are started
+            if (IsStarted)
+            {
+                try
                 {
-                    try
+                    if (!_listener.AcceptAsync(e))
                     {
-#if FEATURE_SOCKET_EAP
-                        StartAccept();
-#elif FEATURE_SOCKET_APM
-                        _listener.BeginAccept(AcceptCallback, _listener);
-#elif FEATURE_SOCKET_TAP
-#error Accepting new socket connections is not implemented.
-#else
-#error Accepting new socket connections is not implemented.
-#endif
-
-                        // wait until listener is stopped
-                        _listenerCompleted.WaitOne();
+                        AcceptCompleted(null, e);
                     }
-                    catch (ObjectDisposedException)
-                    {
-                        // BeginAccept / AcceptAsync will throw an ObjectDisposedException when the
-                        // server is closed before the listener has started accepting connections.
-                        //
-                        // As we start accepting connection on a separate thread, this is possible
-                        // when the listener is stopped right after it was started.
-
-                        // mark listener stopped
-                        _listenerCompleted.Set();
-                    }
-                    catch (Exception ex)
-                    {
-                        RaiseExceptionEvent(ex);
-
-                        // mark listener stopped
-                        _listenerCompleted.Set();
-                    }
-                    finally
+                }
+                catch (ObjectDisposedException)
+                {
+                    if (_status == ForwardedPortStatus.Stopped || _status == ForwardedPortStatus.Stopped)
                     {
-                        if (Session != null)
-                        {
-                            Session.ErrorOccured -= Session_ErrorOccured;
-                            Session.Disconnected -= Session_Disconnected;
-                        }
+                        // ignore ObjectDisposedException while stopping or stopped
+                        return;
                     }
-                });
-        }
-
-        private void Session_Disconnected(object sender, EventArgs e)
-        {
-            StopListener();
-        }
-
-        private void Session_ErrorOccured(object sender, ExceptionEventArgs e)
-        {
-            StopListener();
-        }
-
-#if FEATURE_SOCKET_EAP
-        private void StartAccept()
-        {
-            var args = new SocketAsyncEventArgs();
-            args.Completed += AcceptCompleted;
 
-            if (!_listener.AcceptAsync(args))
-            {
-                AcceptCompleted(null, args);
+                    throw;
+                }
             }
         }
 
-        private void AcceptCompleted(object sender, SocketAsyncEventArgs acceptAsyncEventArgs)
+        private void AcceptCompleted(object sender, SocketAsyncEventArgs e)
         {
-            if (acceptAsyncEventArgs.SocketError == SocketError.OperationAborted)
+            if (e.SocketError == SocketError.OperationAborted || e.SocketError == SocketError.NotSocket)
             {
                 // server was stopped
                 return;
             }
 
-            if (acceptAsyncEventArgs.SocketError != SocketError.Success)
+            // capture client socket
+            var clientSocket = e.AcceptSocket;
+
+            if (e.SocketError != SocketError.Success)
             {
                 // accept new connection
-                StartAccept();
-                // dispose broken socket
-                acceptAsyncEventArgs.AcceptSocket.Dispose();
+                StartAccept(e);
+                // dispose broken client socket
+                CloseClientSocket(clientSocket);
                 return;
             }
 
             // accept new connection
-            StartAccept();
+            StartAccept(e);
             // process connection
-            ProcessAccept(acceptAsyncEventArgs.AcceptSocket);
+            ProcessAccept(clientSocket);
         }
-#elif FEATURE_SOCKET_APM
-        private void AcceptCallback(IAsyncResult ar)
-        {
-            // Get the socket that handles the client request
-            var serverSocket = (Socket) ar.AsyncState;
-
-            Socket clientSocket;
 
-            try
-            {
-                clientSocket = serverSocket.EndAccept(ar);
-            }
-            catch (ObjectDisposedException)
+        private void ProcessAccept(Socket clientSocket)
+        {
+            // close the client socket if we're no longer accepting new connections
+            if (!IsStarted)
             {
-                // when the server socket is closed, an ObjectDisposedException is thrown
-                // by Socket.EndAccept(IAsyncResult)
+                CloseClientSocket(clientSocket);
                 return;
             }
 
-            // accept new connection
-            _listener.BeginAccept(AcceptCallback, _listener);
-            // process connection
-            ProcessAccept(clientSocket);
-        }
-#endif
-
-        private void ProcessAccept(Socket remoteSocket)
-        {
-            Interlocked.Increment(ref _pendingRequests);
+            // capture the countdown event that we're adding a count to, as we need to make sure that we'll be signaling
+            // that same instance; the instance field for the countdown event is re-initialized when the port is restarted
+            // and at that time there may still be pending requests
+            var pendingChannelCountdown = _pendingChannelCountdown;
 
-#if DEBUG_GERT
-            Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | " + remoteSocket.RemoteEndPoint + " | ForwardedPortDynamic.ProcessAccept | " + DateTime.Now.ToString("hh:mm:ss.fff"));
-#endif // DEBUG_GERT
+            pendingChannelCountdown.AddCount();
 
             try
             {
-#if FEATURE_SOCKET_SETSOCKETOPTION
-                remoteSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, true);
-                remoteSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.NoDelay, true);
-#endif //FEATURE_SOCKET_SETSOCKETOPTION
-
                 using (var channel = Session.CreateChannelDirectTcpip())
                 {
                     channel.Exception += Channel_Exception;
 
                     try
                     {
-                        if (!HandleSocks(channel, remoteSocket, Session.ConnectionInfo.Timeout))
+                        if (!HandleSocks(channel, clientSocket, Session.ConnectionInfo.Timeout))
                         {
-                            CloseSocket(remoteSocket);
+                            CloseClientSocket(clientSocket);
                             return;
                         }
 
                         // start receiving from client socket (and sending to server)
                         channel.Bind();
                     }
-#if DEBUG_GERT
-                    catch (SocketException ex)
-                    {
-                        Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | " + ex.SocketErrorCode + " | " + DateTime.Now.ToString("hh:mm:ss.fff") + " | " + ex);
-                    }
-#endif // DEBUG_GERT
                     finally
                     {
                         channel.Close();
                     }
                 }
             }
-            catch (SocketException ex)
-            {
-                // ignore exception thrown by interrupting the blocking receive as part of closing
-                // the forwarded port
-                if (ex.SocketErrorCode != SocketError.Interrupted)
-                {
-#if DEBUG_GERT
-                    RaiseExceptionEvent(new Exception("ID: " + Thread.CurrentThread.ManagedThreadId, ex));
-#else
-                    RaiseExceptionEvent(ex);
-#endif // DEBUG_GERT
-                }
-                CloseSocket(remoteSocket);
-            }
             catch (Exception exp)
             {
-#if DEBUG_GERT
-                Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | " + exp + " | " + DateTime.Now.ToString("hh:mm:ss.fff"));
-#endif // DEBUG_GERT
                 RaiseExceptionEvent(exp);
-                CloseSocket(remoteSocket);
+                CloseClientSocket(clientSocket);
             }
             finally
             {
-                Interlocked.Decrement(ref _pendingRequests);
+                // take into account that CountdownEvent has since been disposed; when stopping the port we
+                // wait for a given time for the channels to close, but once that timeout period has elapsed
+                // the CountdownEvent will be disposed
+                try
+                {
+                    pendingChannelCountdown.Signal();
+                }
+                catch (ObjectDisposedException)
+                {
+                }
+            }
+        }
+
+        /// <summary>
+        /// Initializes the <see cref="CountdownEvent"/>.
+        /// </summary>
+        /// <remarks>
+        /// <para>
+        /// When the port is started for the first time, a <see cref="CountdownEvent"/> is created with an initial count
+        /// of <c>1</c>.
+        /// </para>
+        /// <para>
+        /// On subsequent (re)starts, we'll dispose the current <see cref="CountdownEvent"/> and create a new one with
+        /// initial count of <c>1</c>.
+        /// </para>
+        /// </remarks>
+        private void InitializePendingChannelCountdown()
+        {
+            var original = Interlocked.Exchange(ref _pendingChannelCountdown, new CountdownEvent(1));
+            if (original != null)
+            {
+                original.Dispose();
             }
         }
 
-        private bool HandleSocks(IChannelDirectTcpip channel, Socket remoteSocket, TimeSpan timeout)
+        private bool HandleSocks(IChannelDirectTcpip channel, Socket clientSocket, TimeSpan timeout)
         {
             // create eventhandler which is to be invoked to interrupt a blocking receive
             // when we're closing the forwarded port
-            EventHandler closeClientSocket = (_, args) => CloseSocket(remoteSocket);
+            EventHandler closeClientSocket = (_, args) => CloseClientSocket(clientSocket);
 
             Closing += closeClientSocket;
 
             try
             {
-#if DEBUG_GERT
-                Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | Before ReadByte for version | " + DateTime.Now.ToString("hh:mm:ss.fff"));
-#endif // DEBUG_GERT
-
-                var version = SocketAbstraction.ReadByte(remoteSocket, timeout);
-                if (version == -1)
-                {
-                    return false;
-                }
-
-#if DEBUG_GERT
-                Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | After ReadByte for version | " + DateTime.Now.ToString("hh:mm:ss.fff"));
-#endif // DEBUG_GERT
-
-                if (version == 4)
+                var version = SocketAbstraction.ReadByte(clientSocket, timeout);
+                switch (version)
                 {
-                    return HandleSocks4(remoteSocket, channel, timeout);
+                    case -1:
+                        // SOCKS client closed connection
+                        return false;
+                    case 4:
+                        return HandleSocks4(clientSocket, channel, timeout);
+                    case 5:
+                        return HandleSocks5(clientSocket, channel, timeout);
+                    default:
+                        throw new NotSupportedException(string.Format("SOCKS version {0} is not supported.", version));
                 }
-                else if (version == 5)
-                {
-                    return HandleSocks5(remoteSocket, channel, timeout);
-                }
-                else
+            }
+            catch (SocketException ex)
+            {
+                // ignore exception thrown by interrupting the blocking receive as part of closing
+                // the forwarded port
+                if (ex.SocketErrorCode != SocketError.Interrupted)
                 {
-                    throw new NotSupportedException(string.Format("SOCKS version {0} is not supported.", version));
+                    RaiseExceptionEvent(ex);
                 }
+                return false;
             }
             finally
             {
@@ -272,75 +226,97 @@ namespace Renci.SshNet
 
         }
 
-        private static void CloseSocket(Socket socket)
+        private static void CloseClientSocket(Socket clientSocket)
         {
-#if DEBUG_GERT
-            Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | ForwardedPortDynamic.CloseSocket | " + DateTime.Now.ToString("hh:mm:ss.fff"));
-#endif // DEBUG_GERT
-
-            if (socket.Connected)
+            if (clientSocket.Connected)
             {
-                socket.Shutdown(SocketShutdown.Both);
-                socket.Dispose();
+                try
+                {
+                    clientSocket.Shutdown(SocketShutdown.Send);
+                }
+                catch (Exception)
+                {
+                    // ignore exception when client socket was already closed
+                }
             }
+
+            clientSocket.Dispose();
         }
 
+        /// <summary>
+        /// Interrupts the listener, and unsubscribes from <see cref="Session"/> events.
+        /// </summary>
         partial void StopListener()
         {
-            //  if the port is not started then there's nothing to stop
-            if (!IsStarted)
-                return;
-
             // close listener socket
-            _listener.Dispose();
+            var listener = _listener;
+            if (listener != null)
+            {
+                listener.Dispose();
+            }
 
-            // allow listener thread to stop
-            _listenerCompleted.Set();
+            // unsubscribe from session events
+            var session = Session;
+            if (session != null)
+            {
+                session.ErrorOccured -= Session_ErrorOccured;
+                session.Disconnected -= Session_Disconnected;
+            }
         }
 
         /// <summary>
-        /// Waits for pending requests to finish, and channels to close.
+        /// Waits for pending channels to close.
         /// </summary>
-        /// <param name="timeout">The maximum time to wait for the forwarded port to stop.</param>
+        /// <param name="timeout">The maximum time to wait for the pending channels to close.</param>
         partial void InternalStop(TimeSpan timeout)
         {
-            if (timeout == TimeSpan.Zero)
-                return;
-
-            var stopWatch = new Stopwatch();
-            stopWatch.Start();
-
-            // break out of loop when one of the following conditions are met:
-            // * the forwarded port is restarted
-            // * all pending requests have been processed and corresponding channel are closed
-            // * the specified timeout has elapsed
-            while (!IsStarted)
-            {
-                // break out of loop when all pending requests have been processed
-                if (Interlocked.CompareExchange(ref _pendingRequests, 0, 0) == 0)
-                    break;
-                // break out of loop when specified timeout has elapsed
-                if (stopWatch.Elapsed >= timeout && timeout != SshNet.Session.InfiniteTimeSpan)
-                    break;
-                // give channels time to process pending requests
-                ThreadAbstraction.Sleep(50);
-            }
-
-            stopWatch.Stop();
+            _pendingChannelCountdown.Signal();
+            _pendingChannelCountdown.Wait(timeout);
         }
 
         partial void InternalDispose(bool disposing)
         {
             if (disposing)
             {
-                if (_listener != null)
+                var listener = _listener;
+                if (listener != null)
                 {
-                    _listener.Dispose();
                     _listener = null;
+                    listener.Dispose();
                 }
+
+                var pendingRequestsCountdown = _pendingChannelCountdown;
+                if (pendingRequestsCountdown != null)
+                {
+                    _pendingChannelCountdown = null;
+                    pendingRequestsCountdown.Dispose();
+                }
+            }
+        }
+
+        private void Session_Disconnected(object sender, EventArgs e)
+        {
+            var session = Session;
+            if (session != null)
+            {
+                StopPort(session.ConnectionInfo.Timeout);
             }
         }
 
+        private void Session_ErrorOccured(object sender, ExceptionEventArgs e)
+        {
+            var session = Session;
+            if (session != null)
+            {
+                StopPort(session.ConnectionInfo.Timeout);
+            }
+        }
+
+        private void Channel_Exception(object sender, ExceptionEventArgs e)
+        {
+            RaiseExceptionEvent(e.Exception);
+        }
+
         private bool HandleSocks4(Socket socket, IChannelDirectTcpip channel, TimeSpan timeout)
         {
             var commandCode = SocketAbstraction.ReadByte(socket, timeout);
@@ -400,10 +376,6 @@ namespace Renci.SshNet
 
         private bool HandleSocks5(Socket socket, IChannelDirectTcpip channel, TimeSpan timeout)
         {
-#if DEBUG_GERT
-            Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " |  Handling Socks5: " + socket.LocalEndPoint +  " | " + socket.RemoteEndPoint + " | " + DateTime.Now.ToString("hh:mm:ss.fff"));
-#endif // DEBUG_GERT
-
             var authenticationMethodsCount = SocketAbstraction.ReadByte(socket, timeout);
             if (authenticationMethodsCount == -1)
             {
@@ -411,10 +383,6 @@ namespace Renci.SshNet
                 return false;
             }
 
-#if DEBUG_GERT
-            Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " |  After ReadByte for authenticationMethodsCount | " + DateTime.Now.ToString("hh:mm:ss.fff"));
-#endif // DEBUG_GERT
-
             var authenticationMethods = new byte[authenticationMethodsCount];
             if (SocketAbstraction.Read(socket, authenticationMethods, 0, authenticationMethods.Length, timeout) == 0)
             {
@@ -422,19 +390,11 @@ namespace Renci.SshNet
                 return false;
             }
 
-#if DEBUG_GERT
-            Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " |  After Read for authenticationMethods | " + DateTime.Now.ToString("hh:mm:ss.fff"));
-#endif // DEBUG_GERT
-
             if (authenticationMethods.Min() == 0)
             {
                 // no user authentication is one of the authentication methods supported
                 // by the SOCKS client
                 SocketAbstraction.Send(socket, new byte[] { 0x05, 0x00 }, 0, 2);
-
-#if DEBUG_GERT
-                Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " |  After Send for authenticationMethods 0 | " + DateTime.Now.ToString("hh:mm:ss.fff"));
-#endif // DEBUG_GERT
             }
             else
             {
@@ -444,9 +404,6 @@ namespace Renci.SshNet
                 // we continue business as usual but expect the client to close the connection
                 // so one of the subsequent reads should return -1 signaling that the client
                 // has effectively closed the connection
-#if DEBUG_GERT
-                Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " |  After Send for authenticationMethods 2 | " + DateTime.Now.ToString("hh:mm:ss.fff"));
-#endif // DEBUG_GERT
             }
 
             var version = SocketAbstraction.ReadByte(socket, timeout);
@@ -516,7 +473,7 @@ namespace Renci.SshNet
                             return false;
                         }
 
-                        ipAddress = IPAddress.Parse(SshData.Ascii.GetString(addressBuffer));
+                        ipAddress = IPAddress.Parse(SshData.Ascii.GetString(addressBuffer, 0, addressBuffer.Length));
 
                         //var hostName = new Common.ASCIIEncoding().GetString(addressBuffer);
 
@@ -551,24 +508,10 @@ namespace Renci.SshNet
 
             RaiseRequestReceived(host, port);
 
-#if DEBUG_GERT
-            Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " |  Before channel open | " + DateTime.Now.ToString("hh:mm:ss.fff"));
-
-            var stopWatch = new Stopwatch();
-            stopWatch.Start();
-#endif // DEBUG_GERT
-
             channel.Open(host, port, this, socket);
 
-#if DEBUG_GERT
-            stopWatch.Stop();
-
-            Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " |  After channel open | " + DateTime.Now.ToString("hh:mm:ss.fff") + " => " + stopWatch.ElapsedMilliseconds);
-#endif // DEBUG_GERT
-
             SocketAbstraction.SendByte(socket, 0x05);
 
-
             if (channel.IsOpen)
             {
                 SocketAbstraction.SendByte(socket, 0x00);
@@ -601,15 +544,6 @@ namespace Renci.SshNet
             return true;
         }
 
-        private void Channel_Exception(object sender, ExceptionEventArgs e)
-        {
-#if DEBUG_GERT
-            Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | Channel_Exception | " +
-                              DateTime.Now.ToString("hh:mm:ss.fff"));
-#endif // DEBUG_GERT
-            RaiseExceptionEvent(e.Exception);
-        }
-
         /// <summary>
         /// Reads a null terminated string from a socket.
         /// </summary>
@@ -644,3 +578,4 @@ namespace Renci.SshNet
         }
     }
 }
+

+ 26 - 26
src/Renci.SshNet/ForwardedPortDynamic.cs

@@ -1,5 +1,4 @@
 using System;
-using System.Threading;
 
 namespace Renci.SshNet
 {
@@ -9,7 +8,7 @@ namespace Renci.SshNet
     /// </summary>
     public partial class ForwardedPortDynamic : ForwardedPort
     {
-        private EventWaitHandle _listenerCompleted;
+        private ForwardedPortStatus _status;
 
         /// <summary>
         /// Gets the bound host.
@@ -22,22 +21,21 @@ namespace Renci.SshNet
         public uint BoundPort { get; private set; }
 
         /// <summary>
-        /// Gets or sets a value indicating whether port forwarding is started.
+        /// Gets a value indicating whether port forwarding is started.
         /// </summary>
         /// <value>
         /// <c>true</c> if port forwarding is started; otherwise, <c>false</c>.
         /// </value>
         public override bool IsStarted
         {
-            get { return _listenerCompleted != null && !_listenerCompleted.WaitOne(0); }
+            get { return _status == ForwardedPortStatus.Started; }
         }
 
         /// <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)
+        public ForwardedPortDynamic(uint port) : this(string.Empty, port)
         {
         }
 
@@ -50,6 +48,7 @@ namespace Renci.SshNet
         {
             BoundHost = host;
             BoundPort = port;
+            _status = ForwardedPortStatus.Stopped;
         }
 
         /// <summary>
@@ -57,7 +56,18 @@ namespace Renci.SshNet
         /// </summary>
         protected override void StartPort()
         {
-            InternalStart();
+            if (!ForwardedPortStatus.ToStarting(ref _status))
+                return;
+
+            try
+            {
+                InternalStart();
+            }
+            catch (Exception)
+            {
+                _status = ForwardedPortStatus.Stopped;
+                throw;
+            }
         }
 
         /// <summary>
@@ -67,16 +77,17 @@ namespace Renci.SshNet
         /// <param name="timeout">The maximum amount of time to wait for pending requests to finish processing.</param>
         protected override void StopPort(TimeSpan timeout)
         {
-            if (IsStarted)
-            {
-                // prevent new requests from getting processed before we signal existing
-                // channels that the port is closing
-                StopListener();
-                // signal existing channels that the port is closing
-                base.StopPort(timeout);
-            }
+            if (!ForwardedPortStatus.ToStopping(ref _status))
+                return;
+
+            // signal existing channels that the port is closing
+            base.StopPort(timeout);
+            // prevent new requests from getting processed
+            StopListener();
             // wait for open channels to close
             InternalStop(timeout);
+            // mark port stopped
+            _status = ForwardedPortStatus.Stopped;
         }
 
         /// <summary>
@@ -133,17 +144,6 @@ namespace Renci.SshNet
                 return;
 
             base.Dispose(disposing);
-
-            if (disposing)
-            {
-                var listenerCompleted = _listenerCompleted;
-                if (listenerCompleted != null)
-                {
-                    listenerCompleted.Dispose();
-                    _listenerCompleted = null;
-                }
-            }
-
             InternalDispose(disposing);
 
             _isDisposed = true;

+ 160 - 136
src/Renci.SshNet/ForwardedPortLocal.NET.cs

@@ -1,5 +1,4 @@
 using System;
-using System.Diagnostics;
 using System.Net.Sockets;
 using System.Net;
 using System.Threading;
@@ -8,26 +7,19 @@ using Renci.SshNet.Common;
 
 namespace Renci.SshNet
 {
-    /// <summary>
-    /// Provides functionality for local port forwarding
-    /// </summary>
     public partial class ForwardedPortLocal
     {
         private Socket _listener;
-        private int _pendingRequests;
+        private CountdownEvent _pendingChannelCountdown;
 
         partial void InternalStart()
         {
             var addr = DnsAbstraction.GetHostAddresses(BoundHost)[0];
             var ep = new IPEndPoint(addr, (int) BoundPort);
 
-            _listener = new Socket(ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
-            // TODO: decide if we want to have blocking socket
-#if FEATURE_SOCKET_SETSOCKETOPTION
-            _listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.NoDelay, true);
-#endif // FEATURE_SOCKET_SETSOCKETOPTION
+            _listener = new Socket(ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp) {NoDelay = true};
             _listener.Bind(ep);
-            _listener.Listen(1);
+            _listener.Listen(5);
 
             // update bound port (in case original was passed as zero)
             BoundPort = (uint)((IPEndPoint)_listener.LocalEndPoint).Port;
@@ -35,125 +27,111 @@ namespace Renci.SshNet
             Session.ErrorOccured += Session_ErrorOccured;
             Session.Disconnected += Session_Disconnected;
 
-            _listenerTaskCompleted = new ManualResetEvent(false);
+            InitializePendingChannelCountdown();
 
-            ThreadAbstraction.ExecuteThread(() =>
-                {
-                    try
-                    {
-#if FEATURE_SOCKET_EAP
-                        StartAccept();
-#elif FEATURE_SOCKET_APM
-                        _listener.BeginAccept(AcceptCallback, _listener);
-#elif FEATURE_SOCKET_TAP
-#error Accepting new socket connections is not implemented.
-#else
-#error Accepting new socket connections is not implemented.
-#endif
-
-                        // wait until listener is stopped
-                        _listenerTaskCompleted.WaitOne();
-                    }
-                    catch (ObjectDisposedException)
-                    {
-                        // BeginAccept / AcceptAsync will throw an ObjectDisposedException when the
-                        // server is closed before the listener has started accepting connections.
-                        //
-                        // As we start accepting connection on a separate thread, this is possible
-                        // when the listener is stopped right after it was started.
-
-                        // mark listener stopped
-                        _listenerTaskCompleted.Set();
-                    }
-                    catch (Exception ex)
-                    {
-                        RaiseExceptionEvent(ex);
+            // consider port started when we're listening for inbound connections
+            _status = ForwardedPortStatus.Started;
 
-                        // mark listener stopped
-                        _listenerTaskCompleted.Set();
-                    }
-                    finally
-                    {
-                        if (Session != null)
-                        {
-                            Session.Disconnected -= Session_Disconnected;
-                            Session.ErrorOccured -= Session_ErrorOccured;
-                        }
-                    }
-                });
+            try
+            {
+                StartAccept(null);
+            }
+            catch (ObjectDisposedException)
+            {
+                // AcceptAsync will throw an ObjectDisposedException when the server is closed before
+                // the listener has started accepting connections.
+                //
+                // this is only possible when the listener is stopped (from another thread) right
+                // after it was started.
+                StopPort(Session.ConnectionInfo.Timeout);
+            }
+            catch (Exception ex)
+            {
+                StopPort(Session.ConnectionInfo.Timeout);
+                RaiseExceptionEvent(ex);
+            }
         }
 
-#if FEATURE_SOCKET_EAP
-        private void StartAccept()
+        private void StartAccept(SocketAsyncEventArgs e)
         {
-            var args = new SocketAsyncEventArgs();
-            args.Completed += AcceptCompleted;
+            if (e == null)
+            {
+                e = new SocketAsyncEventArgs();
+                e.Completed += AcceptCompleted;
+            }
+            else
+            {
+                // clear the socket as we're reusing the context object
+                e.AcceptSocket = null;
+            }
 
-            if (!_listener.AcceptAsync(args))
+            // only accept new connections while we are started
+            if (IsStarted)
             {
-                AcceptCompleted(null, args);
+                try
+                {
+                    if (!_listener.AcceptAsync(e))
+                    {
+                        AcceptCompleted(null, e);
+                    }
+                }
+                catch (ObjectDisposedException)
+                {
+                    if (_status == ForwardedPortStatus.Stopped || _status == ForwardedPortStatus.Stopped)
+                    {
+                        // ignore ObjectDisposedException while stopping or stopped
+                        return;
+                    }
+
+                    throw;
+                }
             }
         }
 
-        private void AcceptCompleted(object sender, SocketAsyncEventArgs acceptAsyncEventArgs)
+        private void AcceptCompleted(object sender, SocketAsyncEventArgs e)
         {
-            if (acceptAsyncEventArgs.SocketError == SocketError.OperationAborted)
+            if (e.SocketError == SocketError.OperationAborted || e.SocketError == SocketError.NotSocket)
             {
                 // server was stopped
                 return;
             }
 
-            if (acceptAsyncEventArgs.SocketError != SocketError.Success)
+            // capture client socket
+            var clientSocket = e.AcceptSocket;
+
+            if (e.SocketError != SocketError.Success)
             {
                 // accept new connection
-                StartAccept();
-                // dispose broken socket
-                acceptAsyncEventArgs.AcceptSocket.Dispose();
+                StartAccept(e);
+                // dispose broken client socket
+                CloseClientSocket(clientSocket);
                 return;
             }
 
             // accept new connection
-            StartAccept();
+            StartAccept(e);
             // process connection
-            ProcessAccept(acceptAsyncEventArgs.AcceptSocket);
+            ProcessAccept(clientSocket);
         }
-#elif FEATURE_SOCKET_APM
-        private void AcceptCallback(IAsyncResult ar)
-        {
-            // Get the socket that handles the client request
-            var serverSocket = (Socket) ar.AsyncState;
-
-            Socket clientSocket;
 
-            try
-            {
-                clientSocket = serverSocket.EndAccept(ar);
-            }
-            catch (ObjectDisposedException)
+        private void ProcessAccept(Socket clientSocket)
+        {
+            // close the client socket if we're no longer accepting new connections
+            if (!IsStarted)
             {
-                // when the server socket is closed, an ObjectDisposedException is thrown
-                // by Socket.EndAccept(IAsyncResult)
+                CloseClientSocket(clientSocket);
                 return;
             }
 
-            // accept new connection
-            _listener.BeginAccept(AcceptCallback, _listener);
-            // process connection
-            ProcessAccept(clientSocket);
-        }
-#endif
+            // capture the countdown event that we're adding a count to, as we need to make sure that we'll be signaling
+            // that same instance; the instance field for the countdown event is re-initialized when the port is restarted
+            // and at that time there may still be pending requests
+            var pendingChannelCountdown = _pendingChannelCountdown;
 
-        private void ProcessAccept(Socket clientSocket)
-        {
-            Interlocked.Increment(ref _pendingRequests);
+            pendingChannelCountdown.AddCount();
 
             try
             {
-#if FEATURE_SOCKET_SETSOCKETOPTION
-                clientSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, true);
-                clientSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.NoDelay, true);
-#endif //FEATURE_SOCKET_SETSOCKETOPTION
-
                 var originatorEndPoint = (IPEndPoint) clientSocket.RemoteEndPoint;
 
                 RaiseRequestReceived(originatorEndPoint.Address.ToString(),
@@ -170,83 +148,129 @@ namespace Renci.SshNet
             catch (Exception exp)
             {
                 RaiseExceptionEvent(exp);
-                CloseSocket(clientSocket);
+                CloseClientSocket(clientSocket);
             }
             finally
             {
-                Interlocked.Decrement(ref _pendingRequests);
+                // take into account that CountdownEvent has since been disposed; when stopping the port we
+                // wait for a given time for the channels to close, but once that timeout period has elapsed
+                // the CountdownEvent will be disposed
+                try
+                {
+                    pendingChannelCountdown.Signal();
+                }
+                catch (ObjectDisposedException)
+                {
+                }
             }
         }
 
-        private static void CloseSocket(Socket socket)
+        /// <summary>
+        /// Initializes the <see cref="CountdownEvent"/>.
+        /// </summary>
+        /// <remarks>
+        /// <para>
+        /// When the port is started for the first time, a <see cref="CountdownEvent"/> is created with an initial count
+        /// of <c>1</c>.
+        /// </para>
+        /// <para>
+        /// On subsequent (re)starts, we'll dispose the current <see cref="CountdownEvent"/> and create a new one with
+        /// initial count of <c>1</c>.
+        /// </para>
+        /// </remarks>
+        private void InitializePendingChannelCountdown()
         {
-            if (socket.Connected)
+            var original = Interlocked.Exchange(ref _pendingChannelCountdown, new CountdownEvent(1));
+            if (original != null)
             {
-                socket.Shutdown(SocketShutdown.Both);
-                socket.Dispose();
+                original.Dispose();
             }
         }
 
-        partial void InternalStop(TimeSpan timeout)
+        private static void CloseClientSocket(Socket clientSocket)
         {
-            if (timeout == TimeSpan.Zero)
-                return;
-
-            var stopWatch = new Stopwatch();
-            stopWatch.Start();
-
-            while (true)
+            if (clientSocket.Connected)
             {
-                // break out of loop when all pending requests have been processed
-                if (Interlocked.CompareExchange(ref _pendingRequests, 0, 0) == 0)
-                    break;
-                // break out of loop when specified timeout has elapsed
-                if (stopWatch.Elapsed >= timeout && timeout != SshNet.Session.InfiniteTimeSpan)
-                    break;
-                // give channels time to process pending requests
-                ThreadAbstraction.Sleep(50);
+                try
+                {
+                    clientSocket.Shutdown(SocketShutdown.Send);
+                }
+                catch (Exception)
+                {
+                    // ignore exception when client socket was already closed
+                }
             }
 
-            stopWatch.Stop();
+            clientSocket.Dispose();
         }
 
         /// <summary>
-        /// Interrupts the listener, and waits for the listener loop to finish.
+        /// Interrupts the listener, and unsubscribes from <see cref="Session"/> events.
         /// </summary>
-        /// <remarks>
-        /// When the forwarded port is stopped, then any further action is skipped.
-        /// </remarks>
         partial void StopListener()
         {
-            if (!IsStarted)
-                return;
-
             // close listener socket
-            _listener.Dispose();
-            // allow listener thread to stop
-            _listenerTaskCompleted.Set();
+            var listener = _listener;
+            if (listener != null)
+            {
+                listener.Dispose();
+            }
+
+            // unsubscribe from session events
+            var session = Session;
+            if (session != null)
+            {
+                session.ErrorOccured -= Session_ErrorOccured;
+                session.Disconnected -= Session_Disconnected;
+            }
+        }
+
+        /// <summary>
+        /// Waits for pending channels to close.
+        /// </summary>
+        /// <param name="timeout">The maximum time to wait for the pending channels to close.</param>
+        partial void InternalStop(TimeSpan timeout)
+        {
+            _pendingChannelCountdown.Signal();
+            _pendingChannelCountdown.Wait(timeout);
         }
 
         partial void InternalDispose(bool disposing)
         {
             if (disposing)
             {
-                if (_listener != null)
+                var listener = _listener;
+                if (listener != null)
                 {
-                    _listener.Dispose();
                     _listener = null;
+                    listener.Dispose();
+                }
+
+                var pendingRequestsCountdown = _pendingChannelCountdown;
+                if (pendingRequestsCountdown != null)
+                {
+                    _pendingChannelCountdown = null;
+                    pendingRequestsCountdown.Dispose();
                 }
             }
         }
 
-        private void Session_ErrorOccured(object sender, ExceptionEventArgs e)
+        private void Session_Disconnected(object sender, EventArgs e)
         {
-            StopListener();
+            var session = Session;
+            if (session != null)
+            {
+                StopPort(session.ConnectionInfo.Timeout);
+            }
         }
 
-        private void Session_Disconnected(object sender, EventArgs e)
+        private void Session_ErrorOccured(object sender, ExceptionEventArgs e)
         {
-            StopListener();
+            var session = Session;
+            if (session != null)
+            {
+                StopPort(session.ConnectionInfo.Timeout);
+            }
         }
 
         private void Channel_Exception(object sender, ExceptionEventArgs e)

+ 26 - 24
src/Renci.SshNet/ForwardedPortLocal.cs

@@ -1,5 +1,4 @@
 using System;
-using System.Threading;
 
 namespace Renci.SshNet
 {
@@ -8,7 +7,7 @@ namespace Renci.SshNet
     /// </summary>
     public partial class ForwardedPortLocal : ForwardedPort, IDisposable
     {
-        private EventWaitHandle _listenerTaskCompleted;
+        private ForwardedPortStatus _status;
 
         /// <summary>
         /// Gets the bound host.
@@ -31,14 +30,14 @@ namespace Renci.SshNet
         public uint Port { get; private set; }
 
         /// <summary>
-        /// Gets or sets a value indicating whether port forwarding is started.
+        /// Gets a value indicating whether port forwarding is started.
         /// </summary>
         /// <value>
         /// <c>true</c> if port forwarding is started; otherwise, <c>false</c>.
         /// </value>
         public override bool IsStarted
         {
-            get { return _listenerTaskCompleted != null && !_listenerTaskCompleted.WaitOne(0); }
+            get { return _status == ForwardedPortStatus.Started; }
         }
 
         /// <summary>
@@ -98,6 +97,7 @@ namespace Renci.SshNet
             BoundPort = boundPort;
             Host = host;
             Port = port;
+            _status = ForwardedPortStatus.Stopped;
         }
 
         /// <summary>
@@ -105,7 +105,18 @@ namespace Renci.SshNet
         /// </summary>
         protected override void StartPort()
         {
-            InternalStart();
+            if (!ForwardedPortStatus.ToStarting(ref _status))
+                return;
+
+            try
+            {
+                InternalStart();
+            }
+            catch (Exception)
+            {
+                _status = ForwardedPortStatus.Stopped;
+                throw;
+            }
         }
 
         /// <summary>
@@ -115,16 +126,18 @@ namespace Renci.SshNet
         /// <param name="timeout">The maximum amount of time to wait for pending requests to finish processing.</param>
         protected override void StopPort(TimeSpan timeout)
         {
-            if (IsStarted)
-            {
-                // prevent new requests from getting processed before we signal existing
-                // channels that the port is closing
-                StopListener();
-                // signal existing channels that the port is closing
-                base.StopPort(timeout);
-            }
+            if (!ForwardedPortStatus.ToStopping(ref _status))
+                return;
+
+            // signal existing channels that the port is closing
+            base.StopPort(timeout);
             // wait for open channels to close
             InternalStop(timeout);
+            // prevent new requests from getting processed before we signal existing
+            // channels that the port is closing
+            StopListener();
+            // mark port stopped
+            _status = ForwardedPortStatus.Stopped;
         }
 
         /// <summary>
@@ -174,17 +187,6 @@ namespace Renci.SshNet
                 return;
 
             base.Dispose(disposing);
-
-            if (disposing)
-            {
-                var listenerTaskCompleted = _listenerTaskCompleted;
-                if (listenerTaskCompleted != null)
-                {
-                    listenerTaskCompleted.Dispose();
-                    _listenerTaskCompleted = null;
-                }
-            }
-
             InternalDispose(disposing);
 
             _isDisposed = true;

+ 92 - 44
src/Renci.SshNet/ForwardedPortRemote.cs

@@ -13,21 +13,21 @@ namespace Renci.SshNet
     /// </summary>
     public class ForwardedPortRemote : ForwardedPort, IDisposable
     {
+        private ForwardedPortStatus _status;
         private bool _requestStatus;
 
         private EventWaitHandle _globalRequestResponse = new AutoResetEvent(false);
-        private int _pendingRequests;
-        private bool _isStarted;
+        private CountdownEvent _pendingChannelCountdown;
 
         /// <summary>
-        /// Gets or sets a value indicating whether port forwarding is started.
+        /// Gets a value indicating whether port forwarding is started.
         /// </summary>
         /// <value>
         /// <c>true</c> if port forwarding is started; otherwise, <c>false</c>.
         /// </value>
         public override bool IsStarted
         {
-            get { return _isStarted; }
+            get { return _status == ForwardedPortStatus.Started; }
         }
 
         /// <summary>
@@ -97,6 +97,7 @@ namespace Renci.SshNet
             BoundPort = boundPort;
             HostAddress = hostAddress;
             Port = port;
+            _status = ForwardedPortStatus.Stopped;
         }
 
         /// <summary>
@@ -133,46 +134,57 @@ namespace Renci.SshNet
         /// </summary>
         protected override void StartPort()
         {
-            Session.RegisterMessage("SSH_MSG_REQUEST_FAILURE");
-            Session.RegisterMessage("SSH_MSG_REQUEST_SUCCESS");
-            Session.RegisterMessage("SSH_MSG_CHANNEL_OPEN");
+            if (!ForwardedPortStatus.ToStarting(ref _status))
+                return;
+
+            InitializePendingChannelCountdown();
+
+            try
+            {
+                Session.RegisterMessage("SSH_MSG_REQUEST_FAILURE");
+                Session.RegisterMessage("SSH_MSG_REQUEST_SUCCESS");
+                Session.RegisterMessage("SSH_MSG_CHANNEL_OPEN");
 
-            Session.RequestSuccessReceived += Session_RequestSuccess;
-            Session.RequestFailureReceived += Session_RequestFailure;
-            Session.ChannelOpenReceived += Session_ChannelOpening;
+                Session.RequestSuccessReceived += Session_RequestSuccess;
+                Session.RequestFailureReceived += Session_RequestFailure;
+                Session.ChannelOpenReceived += Session_ChannelOpening;
 
-            // send global request to start direct tcpip
-            Session.SendMessage(new GlobalRequestMessage(GlobalRequestName.TcpIpForward, true, BoundHost, BoundPort));
-            // wat for response on global request to start direct tcpip
-            Session.WaitOnHandle(_globalRequestResponse);
+                // send global request to start forwarding
+                Session.SendMessage(new GlobalRequestMessage(GlobalRequestName.TcpIpForward, true, BoundHost, BoundPort));
+                // wat for response on global request to start direct tcpip
+                Session.WaitOnHandle(_globalRequestResponse);
 
-            if (!_requestStatus)
+                if (!_requestStatus)
+                {
+                    throw new SshException(string.Format(CultureInfo.CurrentCulture, "Port forwarding for '{0}' port '{1}' failed to start.", Host, Port));
+                }
+            }
+            catch (Exception)
             {
-                // when the request to start port forward was rejected, then we're no longer
+                // mark port stopped
+                _status = ForwardedPortStatus.Stopped;
+
+                // when the request to start port forward was rejected or failed, then we're no longer
                 // interested in these events
                 Session.RequestSuccessReceived -= Session_RequestSuccess;
                 Session.RequestFailureReceived -= Session_RequestFailure;
                 Session.ChannelOpenReceived -= Session_ChannelOpening;
 
-                throw new SshException(string.Format(CultureInfo.CurrentCulture, "Port forwarding for '{0}' port '{1}' failed to start.", Host, Port));
+                throw;
             }
 
-            _isStarted = true;
+            _status = ForwardedPortStatus.Started;
         }
 
         /// <summary>
         /// Stops remote port forwarding.
         /// </summary>
-        /// <param name="timeout">The maximum amount of time to wait for pending requests to finish processing.</param>
+        /// <param name="timeout">The maximum amount of time to wait for the port to stop.</param>
         protected override void StopPort(TimeSpan timeout)
         {
-            // if the port not started, then there's nothing to stop
-            if (!IsStarted)
+            if (!ForwardedPortStatus.ToStopping(ref _status))
                 return;
 
-            // mark forwarded port stopped, this also causes open of new channels to be rejected
-            _isStarted = false;
-
             base.StopPort(timeout);
 
             // send global request to cancel direct tcpip
@@ -187,21 +199,11 @@ namespace Renci.SshNet
             Session.RequestFailureReceived -= Session_RequestFailure;
             Session.ChannelOpenReceived -= Session_ChannelOpening;
 
-            var startWaiting = DateTime.Now;
+            // wait for pending channels to close
+            _pendingChannelCountdown.Signal();
+            _pendingChannelCountdown.Wait(timeout);
 
-            while (true)
-            {
-                // break out of loop when all pending requests have been processed
-                if (Interlocked.CompareExchange(ref _pendingRequests, 0, 0) == 0)
-                    break;
-                // determine time elapsed since waiting for pending requests to finish
-                var elapsed = DateTime.Now - startWaiting;
-                // break out of loop when specified timeout has elapsed
-                if (elapsed >= timeout && timeout != SshNet.Session.InfiniteTimeSpan)
-                    break;
-                // give channels time to process pending requests
-                ThreadAbstraction.Sleep(50);
-            }
+            _status = ForwardedPortStatus.Stopped;
         }
 
         /// <summary>
@@ -223,7 +225,7 @@ namespace Renci.SshNet
                 //  Ensure this is the corresponding request
                 if (info.ConnectedAddress == BoundHost && info.ConnectedPort == BoundPort)
                 {
-                    if (!_isStarted)
+                    if (!IsStarted)
                     {
                         Session.SendMessage(new ChannelOpenFailureMessage(channelOpenMessage.LocalChannelNumber, "", ChannelOpenFailureMessage.AdministrativelyProhibited));
                         return;
@@ -231,13 +233,21 @@ namespace Renci.SshNet
 
                     ThreadAbstraction.ExecuteThread(() =>
                         {
-                            Interlocked.Increment(ref _pendingRequests);
+                            // capture the countdown event that we're adding a count to, as we need to make sure that we'll be signaling
+                            // that same instance; the instance field for the countdown event is re-initialize when the port is restarted
+                            // and that time there may still be pending requests
+                            var pendingChannelCountdown = _pendingChannelCountdown;
+
+                            pendingChannelCountdown.AddCount();
 
                             try
                             {
                                 RaiseRequestReceived(info.OriginatorAddress, info.OriginatorPort);
 
-                                using (var channel = Session.CreateChannelForwardedTcpip(channelOpenMessage.LocalChannelNumber, channelOpenMessage.InitialWindowSize, channelOpenMessage.MaximumPacketSize))
+                                using (
+                                    var channel =
+                                        Session.CreateChannelForwardedTcpip(channelOpenMessage.LocalChannelNumber,
+                                            channelOpenMessage.InitialWindowSize, channelOpenMessage.MaximumPacketSize))
                                 {
                                     channel.Exception += Channel_Exception;
                                     channel.Bind(new IPEndPoint(HostAddress, (int) Port), this);
@@ -250,13 +260,44 @@ namespace Renci.SshNet
                             }
                             finally
                             {
-                                Interlocked.Decrement(ref _pendingRequests);
+                                // take into account that CountdownEvent has since been disposed; when stopping the port we
+                                // wait for a given time for the channels to close, but once that timeout period has elapsed
+                                // the CountdownEvent will be disposed
+                                try
+                                {
+                                    pendingChannelCountdown.Signal();
+                                }
+                                catch (ObjectDisposedException)
+                                {
+                                }
                             }
                         });
                 }
             }
         }
 
+        /// <summary>
+        /// Initializes the <see cref="CountdownEvent"/>.
+        /// </summary>
+        /// <remarks>
+        /// <para>
+        /// When the port is started for the first time, a <see cref="CountdownEvent"/> is created with an initial count
+        /// of <c>1</c>.
+        /// </para>
+        /// <para>
+        /// On subsequent (re)starts, we'll dispose the current <see cref="CountdownEvent"/> and create a new one with
+        /// initial count of <c>1</c>.
+        /// </para>
+        /// </remarks>
+        private void InitializePendingChannelCountdown()
+        {
+            var original = Interlocked.Exchange(ref _pendingChannelCountdown, new CountdownEvent(1));
+            if (original != null)
+            {
+                original.Dispose();
+            }
+        }
+
         private void Channel_Exception(object sender, ExceptionEventArgs exceptionEventArgs)
         {
             RaiseExceptionEvent(exceptionEventArgs.Exception);
@@ -308,17 +349,24 @@ namespace Renci.SshNet
                 var session = Session;
                 if (session != null)
                 {
+                    Session = null;
                     session.RequestSuccessReceived -= Session_RequestSuccess;
                     session.RequestFailureReceived -= Session_RequestFailure;
                     session.ChannelOpenReceived -= Session_ChannelOpening;
-                    Session = null;
                 }
 
                 var globalRequestResponse = _globalRequestResponse;
                 if (globalRequestResponse != null)
                 {
-                    globalRequestResponse.Dispose();
                     _globalRequestResponse = null;
+                    globalRequestResponse.Dispose();
+                }
+
+                var pendingRequestsCountdown = _pendingChannelCountdown;
+                if (pendingRequestsCountdown != null)
+                {
+                    _pendingChannelCountdown = null;
+                    pendingRequestsCountdown.Dispose();
                 }
             }
 

+ 141 - 0
src/Renci.SshNet/ForwardedPortStatus.cs

@@ -0,0 +1,141 @@
+using System;
+using System.Globalization;
+using System.Threading;
+
+namespace Renci.SshNet
+{
+    internal class ForwardedPortStatus
+    {
+        private readonly int _value;
+        private readonly string _name;
+
+        public static readonly ForwardedPortStatus Stopped = new ForwardedPortStatus(1, "Stopped");
+        public static readonly ForwardedPortStatus Stopping = new ForwardedPortStatus(2, "Stopping");
+        public static readonly ForwardedPortStatus Started = new ForwardedPortStatus(3, "Started");
+        public static readonly ForwardedPortStatus Starting = new ForwardedPortStatus(4, "Starting");
+
+        private ForwardedPortStatus(int value, string name)
+        {
+            _value = value;
+            _name = name;
+        }
+
+        public override bool Equals(object other)
+        {
+            if (ReferenceEquals(other, null))
+                return false;
+
+            if (ReferenceEquals(this, other))
+                return true;
+
+            var forwardedPortStatus = other as ForwardedPortStatus;
+            if (forwardedPortStatus == null)
+                return false;
+
+            return forwardedPortStatus._value == _value;
+        }
+
+        public static bool operator ==(ForwardedPortStatus left, ForwardedPortStatus right)
+        {
+            // check if lhs is null
+            if (ReferenceEquals(left, null))
+            {
+                // check if both lhs and rhs are null
+                return (ReferenceEquals(right, null));
+            }
+
+            return left.Equals(right);
+        }
+
+        public static bool operator !=(ForwardedPortStatus left, ForwardedPortStatus right)
+        {
+            return !(left==right);
+        }
+
+        public override int GetHashCode()
+        {
+            return _value;
+        }
+
+        public override string ToString()
+        {
+            return _name;
+        }
+
+        /// <summary>
+        /// Returns a value indicating whether <paramref name="status"/> has been changed to <see cref="Stopping"/>.
+        /// </summary>
+        /// <param name="status">The status to transition from.</param>
+        /// <returns>
+        /// <c>true</c> if <paramref name="status"/> has been changed to <see cref="Stopping"/>; otherwise, <c>false</c>.
+        /// </returns>
+        /// <exception cref="InvalidOperationException">Cannot transition <paramref name="status"/> to <see cref="Stopping"/>.</exception>
+        /// <remarks>
+        /// While a transition from <see cref="Stopped"/> to <see cref="Stopping"/> is not possible, this method will
+        /// return <c>false</c> for any such attempts.  This is related to concurrency.
+        /// </remarks>
+        public static bool ToStopping(ref ForwardedPortStatus status)
+        {
+            // attempt to transition from Started to Stopping
+            var previousStatus = Interlocked.CompareExchange(ref status, Stopping, Started);
+            if (previousStatus == Stopping || previousStatus == Stopped)
+            {
+                // status is already Stopping or Stopped, so no transition to Stopping is necessary
+                return false;
+            }
+
+            // we've successfully transitioned from Started to Stopping
+            if (status == Stopping)
+                return true;
+
+            // attempt to transition from Starting to Stopping
+            previousStatus = Interlocked.CompareExchange(ref status, Stopping, Starting);
+            if (previousStatus == Stopping || previousStatus == Stopped)
+            {
+                // status is already Stopping or Stopped, so no transition to Stopping is necessary
+                return false;
+            }
+
+            // we've successfully transitioned from Starting to Stopping
+            if (status == Stopping)
+                return true;
+
+            // there's no valid transition from status to Stopping
+            throw new InvalidOperationException(string.Format("Forwarded port cannot transition from '{0}' to '{1}'.",
+                                                              previousStatus,
+                                                              Stopping));
+        }
+
+        /// <summary>
+        /// Returns a value indicating whether <paramref name="status"/> has been changed to <see cref="Starting"/>.
+        /// </summary>
+        /// <param name="status">The status to transition from.</param>
+        /// <returns>
+        /// <c>true</c> if <paramref name="status"/> has been changed to <see cref="Starting"/>; otherwise, <c>false</c>.
+        /// </returns>
+        /// <exception cref="InvalidOperationException">Cannot transition <paramref name="status"/> to <see cref="Starting"/>.</exception>
+        /// <remarks>
+        /// While a transition from <see cref="Started"/> to <see cref="Starting"/> is not possible, this method will
+        /// return <c>false</c> for any such attempts.  This is related to concurrency.
+        /// </remarks>
+        public static bool ToStarting(ref ForwardedPortStatus status)
+        {
+            // attemp to transition from Stopped to Starting
+            var previousStatus = Interlocked.CompareExchange(ref status, Starting, Stopped);
+            if (previousStatus == Starting || previousStatus == Started)
+            {
+                // port is already Starting or Started, so no transition to Starting is necessary
+                return false;
+            }
+
+            // we've successfully transitioned from Stopped to Starting
+            if (status == Starting)
+                return true;
+
+            // there's no valid transition from status to Starting
+            throw new InvalidOperationException(string.Format("Forwarded port cannot transition from '{0}' to '{1}'.",
+                                                              previousStatus,
+                                                              Starting));
+        }
+    }
+}

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio