diff --git a/Renci.SshClient/Renci.SshClient.Tests/Security/TestKeyExchange.cs b/Renci.SshClient/Renci.SshClient.Tests/Security/TestKeyExchange.cs
index 4f13baa8..b0a8f191 100644
--- a/Renci.SshClient/Renci.SshClient.Tests/Security/TestKeyExchange.cs
+++ b/Renci.SshClient/Renci.SshClient.Tests/Security/TestKeyExchange.cs
@@ -1,8 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
using Renci.SshClient.Security;
using Renci.SshClient.Tests.Properties;
@@ -68,6 +64,18 @@ namespace Renci.SshClient.Tests.Security
}
}
- // TODO: Add test to test re-keying
+ [TestMethod]
+ public void Test_KeyExchange_Rekeying()
+ {
+ var connectionInfo = new PasswordConnectionInfo(Resources.HOST, 22, Resources.USERNAME, Resources.PASSWORD);
+
+ using (var client = new SshClient(connectionInfo))
+ {
+ client.Connect();
+ // TODO: Add test to test re-keying
+ Assert.Inconclusive();
+ client.Disconnect();
+ }
+ }
}
}
diff --git a/Renci.SshClient/Renci.SshClient/BaseClient.cs b/Renci.SshClient/Renci.SshClient/BaseClient.cs
index 72642fa0..43f8ba17 100644
--- a/Renci.SshClient/Renci.SshClient/BaseClient.cs
+++ b/Renci.SshClient/Renci.SshClient/BaseClient.cs
@@ -1,4 +1,5 @@
using System;
+using System.IO;
using System.Threading;
using Renci.SshClient.Common;
@@ -68,6 +69,11 @@ namespace Renci.SshClient
}
}
+ ///
+ /// Occurs when an error occurred.
+ ///
+ public event EventHandler ErrorOccurred;
+
///
/// Initializes a new instance of the class.
///
@@ -92,6 +98,7 @@ namespace Renci.SshClient
this.Session = new Session(this.ConnectionInfo);
this.Session.Connect();
+ this.Session.ErrorOccured += Session_ErrorOccured;
this.OnConnected();
}
@@ -164,12 +171,20 @@ namespace Renci.SshClient
throw new SshConnectionException("Client not connected.");
}
+ private void Session_ErrorOccured(object sender, System.IO.ErrorEventArgs e)
+ {
+ if (this.ErrorOccurred != null)
+ {
+ this.ErrorOccurred(this, e);
+ }
+ }
+
#region IDisposable Members
private bool _isDisposed = false;
///
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages.
///
public void Dispose()
{
@@ -181,17 +196,19 @@ namespace Renci.SshClient
///
/// Releases unmanaged and - optionally - managed resources
///
- /// true to release both managed and unmanaged resources; false to release only unmanaged resources.
+ /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages.
protected virtual void Dispose(bool disposing)
{
// Check to see if Dispose has already been called.
if (!this._isDisposed)
{
// If disposing equals true, dispose all managed
- // and unmanaged resources.
+ // and unmanaged ResourceMessages.
if (disposing)
{
- // Dispose managed resources.
+ // Dispose managed ResourceMessages.
+ this.Session.ErrorOccured -= Session_ErrorOccured;
+
if (this.Session != null)
{
this.Session.Dispose();
@@ -202,7 +219,6 @@ namespace Renci.SshClient
this._keepAliveTimer.Dispose();
this._keepAliveTimer = null;
}
-
}
// Note disposing has been done.
diff --git a/Renci.SshClient/Renci.SshClient/ChannelAsyncResult.cs b/Renci.SshClient/Renci.SshClient/ChannelAsyncResult.cs
index f3f9b98d..3ddc256e 100644
--- a/Renci.SshClient/Renci.SshClient/ChannelAsyncResult.cs
+++ b/Renci.SshClient/Renci.SshClient/ChannelAsyncResult.cs
@@ -12,7 +12,7 @@ namespace Renci.SshClient
/// Gets or sets the command that async result was created for.
///
/// The channel.
- internal SshCommand Command { get; private set; }
+ private SshCommand _command;
///
/// Gets or sets the bytes received. If SFTP only file bytes are counted.
@@ -60,7 +60,20 @@ namespace Renci.SshClient
/// The command.
internal CommandAsyncResult(SshCommand command)
{
- this.Command = command;
+ this._command = command;
+ }
+
+ ///
+ /// Validates that command object is valid to be used with this instance.
+ ///
+ /// The command.
+ public void ValidateCommand(SshCommand command)
+ {
+ if (this._command != command)
+ {
+ throw new InvalidOperationException("Invalid IAsyncResult parameter");
+ }
+
}
}
}
diff --git a/Renci.SshClient/Renci.SshClient/Channels/ChannelDirectTcpip.cs b/Renci.SshClient/Renci.SshClient/Channels/ChannelDirectTcpip.cs
index a94674f5..8d18fb2a 100644
--- a/Renci.SshClient/Renci.SshClient/Channels/ChannelDirectTcpip.cs
+++ b/Renci.SshClient/Renci.SshClient/Channels/ChannelDirectTcpip.cs
@@ -120,7 +120,8 @@ namespace Renci.SshClient.Channels
// data transfer can take longer then connection specified timeout
System.Threading.WaitHandle.WaitAny(new WaitHandle[] { this._channelEof, readerTaskError });
- this._socket.Close();
+ this._socket.Dispose();
+ this._socket = null;
// Wait for task to finish and will throw any errors if any
readerTask.Wait();
@@ -162,6 +163,12 @@ namespace Renci.SshClient.Channels
protected override void Dispose(bool disposing)
{
+ if (this._socket != null)
+ {
+ this._socket.Dispose();
+ this._socket = null;
+ }
+
if (this._channelEof != null)
{
this._channelEof.Dispose();
diff --git a/Renci.SshClient/Renci.SshClient/ForwardedPortLocal.cs b/Renci.SshClient/Renci.SshClient/ForwardedPortLocal.cs
index 4dd50587..08aedd7e 100644
--- a/Renci.SshClient/Renci.SshClient/ForwardedPortLocal.cs
+++ b/Renci.SshClient/Renci.SshClient/ForwardedPortLocal.cs
@@ -3,8 +3,6 @@ using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
using Renci.SshClient.Channels;
-using Renci.SshClient.Common;
-using System.Diagnostics;
namespace Renci.SshClient
{
@@ -37,7 +35,6 @@ namespace Renci.SshClient
{
while (true)
{
- // TODO: Check what the best place to call dispose for this socket.
var socket = this._listener.AcceptSocket();
Task.Factory.StartNew(() =>
@@ -99,7 +96,7 @@ namespace Renci.SshClient
private bool _isDisposed = false;
///
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages.
///
public void Dispose()
{
@@ -111,17 +108,17 @@ namespace Renci.SshClient
///
/// Releases unmanaged and - optionally - managed resources
///
- /// true to release both managed and unmanaged resources; false to release only unmanaged resources.
+ /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages.
protected virtual void Dispose(bool disposing)
{
// Check to see if Dispose has already been called.
if (!this._isDisposed)
{
// If disposing equals true, dispose all managed
- // and unmanaged resources.
+ // and unmanaged ResourceMessages.
if (disposing)
{
- // Dispose managed resources.
+ // Dispose managed ResourceMessages.
if (this._listenerTask != null)
{
this._listenerTask.Dispose();
diff --git a/Renci.SshClient/Renci.SshClient/Security/HMac.cs b/Renci.SshClient/Renci.SshClient/Security/HMac.cs
index 80a2a7ad..ee77f9e9 100644
--- a/Renci.SshClient/Renci.SshClient/Security/HMac.cs
+++ b/Renci.SshClient/Renci.SshClient/Security/HMac.cs
@@ -1,20 +1,18 @@
using System;
using System.Collections.Generic;
-using System.Linq;
-using System.Text;
+using System.Security.Cryptography;
namespace Renci.SshClient.Security
{
///
- /// Represents base class for hash algoritm classes
+ /// Represents base class for hash algorithm classes
///
public abstract class HMac : Algorithm, IDisposable
{
///
/// Instance of initialized hash algorithm that being used
///
- protected System.Security.Cryptography.HMAC _hmac;
- // TODO: Refactor code so this member will not be protected
+ protected abstract HMAC Hash { get; }
///
/// Initializes algorithm with specified key.
@@ -29,7 +27,7 @@ namespace Renci.SshClient.Security
/// The hash
internal byte[] ComputeHash(byte[] hashData)
{
- return this._hmac.ComputeHash(hashData);
+ return this.Hash.ComputeHash(hashData);
}
///
@@ -42,16 +40,14 @@ namespace Renci.SshClient.Security
{
get
{
- return this._hmac.HashSize;
+ return this.Hash.HashSize;
}
}
#region IDisposable Members
- private bool _disposed = false;
-
///
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages.
///
public void Dispose()
{
@@ -63,27 +59,9 @@ namespace Renci.SshClient.Security
///
/// Releases unmanaged and - optionally - managed resources
///
- /// true to release both managed and unmanaged resources; false to release only unmanaged resources.
+ /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages.
protected virtual void Dispose(bool disposing)
{
- // Check to see if Dispose has already been called.
- if (!this._disposed)
- {
- // If disposing equals true, dispose all managed
- // and unmanaged resources.
- if (disposing)
- {
- // Dispose managed resources.
- if (this._hmac != null)
- {
- this._hmac.Dispose();
- this._hmac = null;
- }
- }
-
- // Note disposing has been done.
- this._disposed = true;
- }
}
///
diff --git a/Renci.SshClient/Renci.SshClient/Security/HMacMD5.cs b/Renci.SshClient/Renci.SshClient/Security/HMacMD5.cs
index 20df3210..ea33cad3 100644
--- a/Renci.SshClient/Renci.SshClient/Security/HMacMD5.cs
+++ b/Renci.SshClient/Renci.SshClient/Security/HMacMD5.cs
@@ -1,7 +1,6 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Linq;
-using System.Text;
+using System.Security.Cryptography;
namespace Renci.SshClient.Security
{
@@ -10,6 +9,13 @@ namespace Renci.SshClient.Security
///
internal class HMacMD5 : HMac
{
+ private HMACMD5 _hash;
+
+ protected override HMAC Hash
+ {
+ get { return this._hash; }
+ }
+
///
/// Gets algorithm name.
///
@@ -24,7 +30,19 @@ namespace Renci.SshClient.Security
/// The hash key.
public override void Init(IEnumerable key)
{
- this._hmac = new System.Security.Cryptography.HMACMD5(key.Take(16).ToArray());
+ this._hash = new HMACMD5(key.Take(16).ToArray());
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ // Dispose managed ResourceMessages.
+ if (this._hash != null)
+ {
+ this._hash.Dispose();
+ this._hash = null;
+ }
+
+ base.Dispose(disposing);
}
}
}
diff --git a/Renci.SshClient/Renci.SshClient/Security/HMacSha1.cs b/Renci.SshClient/Renci.SshClient/Security/HMacSha1.cs
index 3ef8f9f6..d83db0d8 100644
--- a/Renci.SshClient/Renci.SshClient/Security/HMacSha1.cs
+++ b/Renci.SshClient/Renci.SshClient/Security/HMacSha1.cs
@@ -1,7 +1,5 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Linq;
-using System.Text;
using System.Security.Cryptography;
namespace Renci.SshClient.Security
@@ -11,6 +9,13 @@ namespace Renci.SshClient.Security
///
internal class HMacSha1 : HMac
{
+ private HMACSHA1 _hash;
+
+ protected override HMAC Hash
+ {
+ get { return this._hash; }
+ }
+
///
/// Gets algorithm name.
///
@@ -25,7 +30,19 @@ namespace Renci.SshClient.Security
/// The hash key.
public override void Init(IEnumerable key)
{
- this._hmac = new System.Security.Cryptography.HMACSHA1(key.Take(20).ToArray());
+ this._hash = new HMACSHA1(key.Take(20).ToArray());
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ // Dispose managed ResourceMessages.
+ if (this._hash != null)
+ {
+ this._hash.Dispose();
+ this._hash = null;
+ }
+
+ base.Dispose(disposing);
}
}
}
diff --git a/Renci.SshClient/Renci.SshClient/Security/KeyExchange.cs b/Renci.SshClient/Renci.SshClient/Security/KeyExchange.cs
index 6cd4dfbd..7a9e0e75 100644
--- a/Renci.SshClient/Renci.SshClient/Security/KeyExchange.cs
+++ b/Renci.SshClient/Renci.SshClient/Security/KeyExchange.cs
@@ -156,6 +156,10 @@ namespace Renci.SshClient.Security
}
}
+ ///
+ /// Creates the server side cipher to use.
+ ///
+ ///
public Cipher CreateServerCipher()
{
// Resolve Session ID
@@ -177,6 +181,10 @@ namespace Renci.SshClient.Security
return serverCipher;
}
+ ///
+ /// Creates the client side cipher to use.
+ ///
+ ///
public Cipher CreateClientCipher()
{
// Resolve Session ID
@@ -198,6 +206,10 @@ namespace Renci.SshClient.Security
return clientCipher;
}
+ ///
+ /// Creates the server side hash algorithm to use.
+ ///
+ ///
public HMac CreateServerHash()
{
// Resolve Session ID
@@ -211,6 +223,10 @@ namespace Renci.SshClient.Security
return serverHMac;
}
+ ///
+ /// Creates the client side hash algorithm to use.
+ ///
+ ///
public HMac CreateClientHash()
{
// Resolve Session ID
@@ -224,6 +240,10 @@ namespace Renci.SshClient.Security
return clientHMac;
}
+ ///
+ /// Creates the compression algorithm to use to deflate data.
+ ///
+ ///
public Compressor CreateCompressor()
{
if (this._compressionType == null)
@@ -236,6 +256,10 @@ namespace Renci.SshClient.Security
return compressor;
}
+ ///
+ /// Creates the compression algorithm to use to inflate data.
+ ///
+ ///
public Compressor CreateDecompressor()
{
if (this._compressionType == null)
@@ -374,7 +398,7 @@ namespace Renci.SshClient.Security
#region IDisposable Members
///
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages.
///
public void Dispose()
{
@@ -386,7 +410,7 @@ namespace Renci.SshClient.Security
///
/// Releases unmanaged and - optionally - managed resources
///
- /// true to release both managed and unmanaged resources; false to release only unmanaged resources.
+ /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages.
protected virtual void Dispose(bool disposing)
{
}
diff --git a/Renci.SshClient/Renci.SshClient/Session.cs b/Renci.SshClient/Renci.SshClient/Session.cs
index 2817221d..db6cc2fb 100644
--- a/Renci.SshClient/Renci.SshClient/Session.cs
+++ b/Renci.SshClient/Renci.SshClient/Session.cs
@@ -430,6 +430,7 @@ namespace Renci.SshClient
Name = messageAttribute.Name,
Number = messageAttribute.Number,
Enabled = false,
+ Activated = false,
Type = type,
}).ToList();
@@ -440,11 +441,8 @@ namespace Renci.SshClient
Match versionMatch = null;
// Get server version from the server,
// ignore text lines which are sent before if any
- NetworkStream ns = null;
- try
+ using (var ns = new NetworkStream(this._socket))
{
-
- ns = new NetworkStream(this._socket);
using (var sr = new StreamReader(ns))
{
while (true)
@@ -464,14 +462,6 @@ namespace Renci.SshClient
}
}
}
- finally
- {
- if (ns != null)
- {
- ns.Dispose();
- ns = null;
- }
- }
// Get server SSH version
var version = versionMatch.Result("${protoversion}");
@@ -528,6 +518,21 @@ namespace Renci.SshClient
throw new SshAuthenticationException("User cannot be authenticated.");
}
+ // Register Connection messages
+ this.RegisterMessage("SSH_MSG_GLOBAL_REQUEST");
+ this.RegisterMessage("SSH_MSG_REQUEST_SUCCESS");
+ this.RegisterMessage("SSH_MSG_REQUEST_FAILURE");
+ this.RegisterMessage("SSH_MSG_CHANNEL_OPEN_CONFIRMATION");
+ this.RegisterMessage("SSH_MSG_CHANNEL_OPEN_FAILURE");
+ this.RegisterMessage("SSH_MSG_CHANNEL_WINDOW_ADJUST");
+ this.RegisterMessage("SSH_MSG_CHANNEL_EXTENDED_DATA");
+ this.RegisterMessage("SSH_MSG_CHANNEL_REQUEST");
+ this.RegisterMessage("SSH_MSG_CHANNEL_SUCCESS");
+ this.RegisterMessage("SSH_MSG_CHANNEL_FAILURE");
+ this.RegisterMessage("SSH_MSG_CHANNEL_DATA");
+ this.RegisterMessage("SSH_MSG_CHANNEL_EOF");
+ this.RegisterMessage("SSH_MSG_CHANNEL_CLOSE");
+
Monitor.Pulse(this);
}
}
@@ -1025,22 +1030,12 @@ namespace Renci.SshClient
{
this._keyExchangeCompletedWaitHandle.Reset();
- // Connection type messages are not allowed during key exchange phase
- this.UnRegisterMessage("SSH_MSG_GLOBAL_REQUEST");
- this.UnRegisterMessage("SSH_MSG_REQUEST_SUCCESS");
- this.UnRegisterMessage("SSH_MSG_REQUEST_FAILURE");
- this.UnRegisterMessage("SSH_MSG_CHANNEL_OPEN_CONFIRMATION");
- this.UnRegisterMessage("SSH_MSG_CHANNEL_OPEN_FAILURE");
- this.UnRegisterMessage("SSH_MSG_CHANNEL_WINDOW_ADJUST");
- this.UnRegisterMessage("SSH_MSG_CHANNEL_EXTENDED_DATA");
- this.UnRegisterMessage("SSH_MSG_CHANNEL_REQUEST");
- this.UnRegisterMessage("SSH_MSG_CHANNEL_SUCCESS");
- this.UnRegisterMessage("SSH_MSG_CHANNEL_FAILURE");
- this.UnRegisterMessage("SSH_MSG_CHANNEL_DATA");
- this.UnRegisterMessage("SSH_MSG_CHANNEL_EOF");
- this.UnRegisterMessage("SSH_MSG_CHANNEL_CLOSE");
- // TODO: Replace it with algorithm which disables all messages but relevant for key exchange
-
+ // Disable all registered messages except key exchange related
+ foreach (var messageMetadata in this._messagesMetadata)
+ {
+ if (messageMetadata.Activated == true && messageMetadata.Number < 20 || messageMetadata.Number > 30)
+ messageMetadata.Enabled = false;
+ }
var keyExchangeAlgorithmName = (from c in this.ConnectionInfo.KeyExchangeAlgorithms.Keys
from s in message.KeyExchangeAlgorithms
@@ -1116,20 +1111,12 @@ namespace Renci.SshClient
this._keyExchange = null;
}
- // Register Connection messages
- this.RegisterMessage("SSH_MSG_GLOBAL_REQUEST");
- this.RegisterMessage("SSH_MSG_REQUEST_SUCCESS");
- this.RegisterMessage("SSH_MSG_REQUEST_FAILURE");
- this.RegisterMessage("SSH_MSG_CHANNEL_OPEN_CONFIRMATION");
- this.RegisterMessage("SSH_MSG_CHANNEL_OPEN_FAILURE");
- this.RegisterMessage("SSH_MSG_CHANNEL_WINDOW_ADJUST");
- this.RegisterMessage("SSH_MSG_CHANNEL_EXTENDED_DATA");
- this.RegisterMessage("SSH_MSG_CHANNEL_REQUEST");
- this.RegisterMessage("SSH_MSG_CHANNEL_SUCCESS");
- this.RegisterMessage("SSH_MSG_CHANNEL_FAILURE");
- this.RegisterMessage("SSH_MSG_CHANNEL_DATA");
- this.RegisterMessage("SSH_MSG_CHANNEL_EOF");
- this.RegisterMessage("SSH_MSG_CHANNEL_CLOSE");
+ // Enable all active registered messages
+ foreach (var messageMetadata in this._messagesMetadata)
+ {
+ if (messageMetadata.Activated == true)
+ messageMetadata.Enabled = true;
+ }
if (this.NewKeysReceived != null)
{
@@ -1460,7 +1447,7 @@ namespace Renci.SshClient
{
Parallel.ForEach(
from m in this._messagesMetadata where m.Name == messageName select m,
- (item) => { item.Enabled = true; });
+ (item) => { item.Enabled = true; item.Activated = true; });
}
}
@@ -1474,7 +1461,7 @@ namespace Renci.SshClient
{
Parallel.ForEach(
from m in this._messagesMetadata where m.Name == messageName select m,
- (item) => { item.Enabled = false; });
+ (item) => { item.Enabled = false; item.Activated = false; });
}
}
@@ -1486,8 +1473,7 @@ namespace Renci.SshClient
private Message LoadMessage(IEnumerable data)
{
var messageType = data.FirstOrDefault();
-
- var messageMetadata = (from m in this._messagesMetadata where m.Number == messageType && m.Enabled == true select m).SingleOrDefault();
+ var messageMetadata = (from m in this._messagesMetadata where m.Number == messageType && m.Enabled && m.Activated select m).SingleOrDefault();
if (messageMetadata == null)
throw new SshException(string.Format("Message type {0} is not valid.", messageType));
@@ -1512,8 +1498,6 @@ namespace Renci.SshClient
{
var message = this.ReceiveMessage();
- //Debug.WriteLine(string.Format("{0} : {1}", DateTime.Now, message));
-
if (message == null)
{
throw new NullReferenceException("The 'message' variable cannot be null");
@@ -1527,10 +1511,6 @@ namespace Renci.SshClient
catch (Exception exp)
{
this.RaiseError(exp);
- // TODO: This exception can be swallowed if it occurs while running in the background, look for possible solutions
- // It can be swollen if call async, when asynch method is finished ensure all async method check for exception to be raised
- // Or if it is running in port forwarding mode, some event should be raised at this case to handle and error and if not handle then throw
- // throw an exception and let it be swallowed then if not handled by the event handler
}
}
@@ -1568,7 +1548,7 @@ namespace Renci.SshClient
private bool _disposed = false;
///
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages.
///
public void Dispose()
{
@@ -1580,14 +1560,14 @@ namespace Renci.SshClient
///
/// Releases unmanaged and - optionally - managed resources
///
- /// true to release both managed and unmanaged resources; false to release only unmanaged resources.
+ /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages.
protected virtual void Dispose(bool disposing)
{
// Check to see if Dispose has already been called.
if (!this._disposed)
{
// If disposing equals true, dispose all managed
- // and unmanaged resources.
+ // and unmanaged ResourceMessages.
if (disposing)
{
this._isDisconnecting = true;
@@ -1690,8 +1670,9 @@ namespace Renci.SshClient
public bool Enabled { get; set; }
+ public bool Activated { get; set; }
+
public Type Type { get; set; }
}
-
}
}
diff --git a/Renci.SshClient/Renci.SshClient/SftpClient.cs b/Renci.SshClient/Renci.SshClient/SftpClient.cs
index 9a42999e..93fea58f 100644
--- a/Renci.SshClient/Renci.SshClient/SftpClient.cs
+++ b/Renci.SshClient/Renci.SshClient/SftpClient.cs
@@ -213,12 +213,10 @@ namespace Renci.SshClient
///
/// Creates a symbolic link from old path to new path.
///
- /// The old path.
- /// The new path.
- public void SymbolicLink(string linkPath, string path)
+ /// The old path.
+ /// The new path.
+ public void SymbolicLink(string path, string linkPath)
{
- // TODO: Need to be tested, currently does not work
-
// Ensure that connection is established.
this.EnsureConnection();
@@ -226,7 +224,7 @@ namespace Renci.SshClient
var linkFullPath = this._sftpSession.ResolvePath(linkPath);
- var cmd = new SymbolicLinkCommand(this._sftpSession, linkFullPath, fullPath);
+ var cmd = new SymbolicLinkCommand(this._sftpSession, fullPath, linkFullPath);
cmd.CommandTimeout = this.OperationTimeout;
@@ -413,7 +411,7 @@ namespace Renci.SshClient
///
/// Releases unmanaged and - optionally - managed resources
///
- /// true to release both managed and unmanaged resources; false to release only unmanaged resources.
+ /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages.
protected override void Dispose(bool disposing)
{
if (this._sftpSession != null)
diff --git a/Renci.SshClient/Renci.SshClient/SshCommand.cs b/Renci.SshClient/Renci.SshClient/SshCommand.cs
index 1dc86513..858f7557 100644
--- a/Renci.SshClient/Renci.SshClient/SshCommand.cs
+++ b/Renci.SshClient/Renci.SshClient/SshCommand.cs
@@ -172,11 +172,7 @@ namespace Renci.SshClient
{
CommandAsyncResult channelAsyncResult = asynchResult as CommandAsyncResult;
- // TODO: Create a method to perform this check in the method
- if (channelAsyncResult.Command != this)
- {
- throw new InvalidOperationException("Invalid IAsyncResult parameter");
- }
+ channelAsyncResult.ValidateCommand(this);
// Make sure that operation completed if not wait for it to finish
this.WaitHandle(this._asyncResult.AsyncWaitHandle);
@@ -334,7 +330,7 @@ namespace Renci.SshClient
private bool _isDisposed = false;
///
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages.
///
public void Dispose()
{
@@ -346,42 +342,42 @@ namespace Renci.SshClient
///
/// Releases unmanaged and - optionally - managed resources
///
- /// true to release both managed and unmanaged resources; false to release only unmanaged resources.
+ /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages.
protected virtual void Dispose(bool disposing)
{
// Check to see if Dispose has already been called.
if (!this._isDisposed)
{
// If disposing equals true, dispose all managed
- // and unmanaged resources.
+ // and unmanaged ResourceMessages.
if (disposing)
{
this._session.Disconnected -= Session_Disconnected;
this._session.ErrorOccured -= Session_ErrorOccured;
- // Dispose managed resources.
+ // Dispose managed ResourceMessages.
if (this.OutputStream != null)
{
this.OutputStream.Dispose();
this.OutputStream = null;
}
- // Dispose managed resources.
+ // Dispose managed ResourceMessages.
if (this.ExtendedOutputStream != null)
{
this.ExtendedOutputStream.Dispose();
this.ExtendedOutputStream = null;
}
- // Dispose managed resources.
+ // Dispose managed ResourceMessages.
if (this._sessionErrorOccuredWaitHandle != null)
{
this._sessionErrorOccuredWaitHandle.Dispose();
this._sessionErrorOccuredWaitHandle = null;
}
- // Dispose managed resources.
+ // Dispose managed ResourceMessages.
if (this._channel != null)
{
this._channel.DataReceived -= Channel_DataReceived;