diff --git a/Renci.SshClient/Renci.SshClient/Algorithms/KeyExchange.cs b/Renci.SshClient/Renci.SshClient/Algorithms/KeyExchange.cs
index 97ba8734..f193f693 100644
--- a/Renci.SshClient/Renci.SshClient/Algorithms/KeyExchange.cs
+++ b/Renci.SshClient/Renci.SshClient/Algorithms/KeyExchange.cs
@@ -17,7 +17,7 @@ namespace Renci.SshClient.Algorithms
///
/// The message.
///
- internal static KeyExchange Create(KeyExchangeInitMessage message, SessionInfo sessionInfo)
+ internal static KeyExchange Create(KeyExchangeInitMessage message, Session session)
{
// TODO: Determine key exchange algorithm
@@ -32,7 +32,7 @@ namespace Renci.SshClient.Algorithms
throw new InvalidDataException("Failed to negotiate key exchange algorithm.");
}
- return Settings.KeyExchangeAlgorithms[keyExchangeAlgorithm](sessionInfo);
+ return Settings.KeyExchangeAlgorithms[keyExchangeAlgorithm](session);
}
///
@@ -87,7 +87,7 @@ namespace Renci.SshClient.Algorithms
public bool IsSuccessed { get; protected set; }
- protected SessionInfo SessionInfo { get; private set; }
+ protected Session Session { get; private set; }
protected string ClientPayload { get; set; }
@@ -107,9 +107,9 @@ namespace Renci.SshClient.Algorithms
public event EventHandler Failed;
- public KeyExchange(SessionInfo sessionInfo)
+ public KeyExchange(Session session)
{
- this.SessionInfo = sessionInfo;
+ this.Session = session;
this.ServerDecompression = Compression.None;
this.ClientCompression = Compression.None;
}
@@ -191,9 +191,9 @@ namespace Renci.SshClient.Algorithms
public virtual void Finish()
{
// TODO: Validate that all required properties are set
- if (this.SessionInfo.SessionId == null)
+ if (this.Session.SessionId == null)
{
- this.SessionInfo.SessionId = this.ExchangeHash;
+ this.Session.SessionId = this.ExchangeHash;
}
// Set encryption
@@ -201,10 +201,10 @@ namespace Renci.SshClient.Algorithms
using (var clientAlgorithm = this._clientEncryptionAlgorithm())
{
// Calculate client to server initial IV
- var clientValue = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'A', this.SessionInfo.SessionId));
+ var clientValue = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'A', this.Session.SessionId));
// Calculate client to server encryption
- var clientKey = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'C', this.SessionInfo.SessionId));
+ var clientKey = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'C', this.Session.SessionId));
clientKey = this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, clientKey, clientAlgorithm.KeySize / 8);
@@ -219,10 +219,10 @@ namespace Renci.SshClient.Algorithms
using (var serverAlgorithm = this._serverDecryptionAlgorithm())
{
// Calculate server to client initial IV
- var serverValue = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'B', this.SessionInfo.SessionId));
+ var serverValue = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'B', this.Session.SessionId));
// Calculate server to client encryption
- var serverKey = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'D', this.SessionInfo.SessionId));
+ var serverKey = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'D', this.Session.SessionId));
serverKey = this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, serverKey, serverAlgorithm.KeySize / 8);
@@ -233,11 +233,11 @@ namespace Renci.SshClient.Algorithms
}
// Calculate client to server integrity
- var MACc2s = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'E', this.SessionInfo.SessionId));
+ var MACc2s = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'E', this.Session.SessionId));
var clientMac = this._clientHmacAlgorithm(MACc2s);
// Calculate server to client integrity
- var MACs2c = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'F', this.SessionInfo.SessionId));
+ var MACs2c = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'F', this.Session.SessionId));
var serverMac = this._serverHmacAlgorithm(MACs2c);
// TODO: Create compression and decompression objects if any
@@ -314,15 +314,15 @@ namespace Renci.SshClient.Algorithms
protected void SendMessage(Message message)
{
- this.SessionInfo.SendMessage(message);
+ this.Session.SendMessage(message);
}
private IEnumerable CalculateHash()
{
var hashData = new _ExchangeHashData
{
- ClientVersion = this.SessionInfo.ClientVersion,
- ServerVersion = this.SessionInfo.ServerVersion,
+ ClientVersion = this.Session.ClientVersion,
+ ServerVersion = this.Session.ServerVersion,
ClientPayload = this.ClientPayload,
ServerPayload = this.ServerPayload,
HostKey = this.HostKey,
diff --git a/Renci.SshClient/Renci.SshClient/Algorithms/KeyExchangeDiffieHellman.cs b/Renci.SshClient/Renci.SshClient/Algorithms/KeyExchangeDiffieHellman.cs
index d1f00a35..34e82ce6 100644
--- a/Renci.SshClient/Renci.SshClient/Algorithms/KeyExchangeDiffieHellman.cs
+++ b/Renci.SshClient/Renci.SshClient/Algorithms/KeyExchangeDiffieHellman.cs
@@ -43,8 +43,8 @@ namespace Renci.SshClient.Algorithms
/// Initializes a new instance of the class.
///
/// The session information.
- internal KeyExchangeDiffieHellman(SessionInfo sessionInfo)
- : base(sessionInfo)
+ internal KeyExchangeDiffieHellman(Session session)
+ : base(session)
{
}
@@ -72,7 +72,7 @@ namespace Renci.SshClient.Algorithms
E = this.ClientExchangeValue,
});
- this.SessionInfo.MessageReceived += SessionInfo_MessageReceived;
+ this.Session.MessageReceived += SessionInfo_MessageReceived;
}
@@ -80,7 +80,7 @@ namespace Renci.SshClient.Algorithms
{
base.Finish();
- this.SessionInfo.MessageReceived -= SessionInfo_MessageReceived;
+ this.Session.MessageReceived -= SessionInfo_MessageReceived;
}
private void SessionInfo_MessageReceived(object sender, MessageReceivedEventArgs e)
diff --git a/Renci.SshClient/Renci.SshClient/Channels/Channel.cs b/Renci.SshClient/Renci.SshClient/Channels/Channel.cs
index 8037f966..83d20a0f 100644
--- a/Renci.SshClient/Renci.SshClient/Channels/Channel.cs
+++ b/Renci.SshClient/Renci.SshClient/Channels/Channel.cs
@@ -38,9 +38,9 @@ namespace Renci.SshClient.Channels
public bool IsOpen { get; protected set; }
- protected SessionInfo SessionInfo { get; private set; }
+ protected Session Session { get; private set; }
- public Channel(SessionInfo sessionInfo, uint windowSize, uint packetSize)
+ public Channel(Session session, uint windowSize, uint packetSize)
{
this._initialWindowSize = windowSize;
this._maximumPacketSize = Math.Max(packetSize, 0x8000); // Ensure minimum maximum packet size of 0x8000 bytes
@@ -61,21 +61,21 @@ namespace Renci.SshClient.Channels
this.ClientChannelNumber = _channelCounter++;
}
- this.SessionInfo = sessionInfo;
+ this.Session = session;
this.ChannelData = new StringBuilder((int)this._initialWindowSize);
this.ChannelExtendedData = new StringBuilder((int)this._initialWindowSize);
this.WindowSize = this._initialWindowSize; // Initial window size
this.PacketSize = this._maximumPacketSize; // Maximum packet size
}
- public Channel(SessionInfo sessionInfo)
- : this(sessionInfo, 0x100000, 0x8000)
+ public Channel(Session session)
+ : this(session, 0x100000, 0x8000)
{
}
public virtual void Open()
{
- this.SessionInfo.MessageReceived += SessionInfo_MessageReceived;
+ this.Session.MessageReceived += SessionInfo_MessageReceived;
// Open session channel
if (!this.IsOpen)
@@ -88,7 +88,7 @@ namespace Renci.SshClient.Channels
MaximumPacketSize = this.PacketSize,
});
- this.SessionInfo.WaitHandle(this._channelOpenWaitHandle);
+ this.Session.WaitHandle(this._channelOpenWaitHandle);
}
}
@@ -102,7 +102,7 @@ namespace Renci.SshClient.Channels
});
// Wait for channel to be closed
- this.SessionInfo.WaitHandle(this._channelClosedWaitHandle);
+ this.Session.WaitHandle(this._channelClosedWaitHandle);
}
this.CloseCleanup();
@@ -130,7 +130,7 @@ namespace Renci.SshClient.Channels
protected void SendMessage(Message message)
{
- this.SessionInfo.SendMessage(message);
+ this.Session.SendMessage(message);
}
private void SessionInfo_MessageReceived(object sender, MessageReceivedEventArgs e)
@@ -251,7 +251,7 @@ namespace Renci.SshClient.Channels
this.IsOpen = false;
- this.SessionInfo.MessageReceived -= SessionInfo_MessageReceived;
+ this.Session.MessageReceived -= SessionInfo_MessageReceived;
}
}
}
diff --git a/Renci.SshClient/Renci.SshClient/Channels/ChannelSession.cs b/Renci.SshClient/Renci.SshClient/Channels/ChannelSession.cs
index 90bb69dc..eb398234 100644
--- a/Renci.SshClient/Renci.SshClient/Channels/ChannelSession.cs
+++ b/Renci.SshClient/Renci.SshClient/Channels/ChannelSession.cs
@@ -14,8 +14,8 @@ namespace Renci.SshClient.Channels
get { return ChannelTypes.Session; }
}
- public ChannelSession(SessionInfo sessionInfo)
- : base(sessionInfo, 0x100000, 0x1000)
+ public ChannelSession(Session session)
+ : base(session, 0x100000, 0x1000)
{
}
@@ -33,7 +33,7 @@ namespace Renci.SshClient.Channels
});
- this.SessionInfo.WaitHandle(this._channelEofWaitHandle);
+ this.Session.WaitHandle(this._channelEofWaitHandle);
this.Close();
@@ -49,6 +49,13 @@ namespace Renci.SshClient.Channels
this._channelEofWaitHandle.Set();
}
+ protected override void OnChannelClose()
+ {
+ base.OnChannelClose();
+
+ this._channelEofWaitHandle.Set();
+ }
+
protected override void OnChannelData(string data)
{
base.OnChannelData(data);
diff --git a/Renci.SshClient/Renci.SshClient/Channels/ChannelSftp.cs b/Renci.SshClient/Renci.SshClient/Channels/ChannelSftp.cs
index f22e7f75..fab31ec6 100644
--- a/Renci.SshClient/Renci.SshClient/Channels/ChannelSftp.cs
+++ b/Renci.SshClient/Renci.SshClient/Channels/ChannelSftp.cs
@@ -33,13 +33,13 @@ namespace Renci.SshClient.Channels
get { return ChannelTypes.Session; }
}
- public ChannelSftp(SessionInfo sessionInfo, uint windowSize, uint packetSize)
- : base(sessionInfo, windowSize, packetSize)
+ public ChannelSftp(Session session, uint windowSize, uint packetSize)
+ : base(session, windowSize, packetSize)
{
}
- public ChannelSftp(SessionInfo sessionInfo)
- : base(sessionInfo, 0x100000, 0x4000)
+ public ChannelSftp(Session session)
+ : base(session, 0x100000, 0x4000)
{
}
@@ -56,7 +56,7 @@ namespace Renci.SshClient.Channels
SubsystemName = "sftp",
});
- this.SessionInfo.WaitHandle(this._channelRequestSuccessWaitHandle);
+ this.Session.WaitHandle(this._channelRequestSuccessWaitHandle);
this.SendMessage(new InitMessage
{
@@ -263,7 +263,7 @@ namespace Renci.SshClient.Channels
private SftpMessage ReceiveMessage()
{
- this.SessionInfo.WaitHandle(this._responseMessageReceivedWaitHandle);
+ this.Session.WaitHandle(this._responseMessageReceivedWaitHandle);
var statusMessage = this._responseMessage as StatusMessage;
diff --git a/Renci.SshClient/Renci.SshClient/Renci.SshClient.csproj b/Renci.SshClient/Renci.SshClient/Renci.SshClient.csproj
index 289b9f55..7987fdbf 100644
--- a/Renci.SshClient/Renci.SshClient/Renci.SshClient.csproj
+++ b/Renci.SshClient/Renci.SshClient/Renci.SshClient.csproj
@@ -172,7 +172,6 @@
-
diff --git a/Renci.SshClient/Renci.SshClient/Services/ConnectionService.cs b/Renci.SshClient/Renci.SshClient/Services/ConnectionService.cs
index 913d9e2b..8719d78e 100644
--- a/Renci.SshClient/Renci.SshClient/Services/ConnectionService.cs
+++ b/Renci.SshClient/Renci.SshClient/Services/ConnectionService.cs
@@ -10,20 +10,10 @@ namespace Renci.SshClient.Services
get { throw new NotImplementedException(); }
}
- public ConnectionService(SessionInfo sessionInfo)
- : base(sessionInfo)
+ public ConnectionService(Session session)
+ : base(session)
{
}
-
- //public override void Request()
- //{
- // throw new NotImplementedException();
- //}
-
- //public override void Accept()
- //{
- // throw new NotImplementedException();
- //}
}
}
diff --git a/Renci.SshClient/Renci.SshClient/Services/Service.cs b/Renci.SshClient/Renci.SshClient/Services/Service.cs
index 041771b9..23ebe0fb 100644
--- a/Renci.SshClient/Renci.SshClient/Services/Service.cs
+++ b/Renci.SshClient/Renci.SshClient/Services/Service.cs
@@ -6,16 +6,16 @@ namespace Renci.SshClient.Services
{
public abstract ServiceNames ServiceName { get; }
- protected SessionInfo SessionInfo { get; private set; }
+ protected Session Session { get; private set; }
- public Service(SessionInfo sessionInfo)
+ public Service(Session session)
{
- this.SessionInfo = sessionInfo;
+ this.Session = session;
}
protected void SendMessage(Message message)
{
- this.SessionInfo.SendMessage(message);
+ this.Session.SendMessage(message);
}
}
diff --git a/Renci.SshClient/Renci.SshClient/Services/UserAuthentication.cs b/Renci.SshClient/Renci.SshClient/Services/UserAuthentication.cs
index 0dd59246..cd9fec35 100644
--- a/Renci.SshClient/Renci.SshClient/Services/UserAuthentication.cs
+++ b/Renci.SshClient/Renci.SshClient/Services/UserAuthentication.cs
@@ -5,18 +5,18 @@ namespace Renci.SshClient.Services
{
public abstract string Name { get; }
- protected SessionInfo SessionInfo { get; private set; }
+ protected Session Session { get; private set; }
- public UserAuthentication(SessionInfo sessionInfo)
+ public UserAuthentication(Session session)
{
- this.SessionInfo = sessionInfo;
+ this.Session = session;
}
public abstract bool Start();
protected void SendMessage(Message message)
{
- this.SessionInfo.SendMessage(message);
+ this.Session.SendMessage(message);
}
}
diff --git a/Renci.SshClient/Renci.SshClient/Services/UserAuthenticationHost.cs b/Renci.SshClient/Renci.SshClient/Services/UserAuthenticationHost.cs
index 9f258c29..ab39a8e6 100644
--- a/Renci.SshClient/Renci.SshClient/Services/UserAuthenticationHost.cs
+++ b/Renci.SshClient/Renci.SshClient/Services/UserAuthenticationHost.cs
@@ -10,8 +10,8 @@ namespace Renci.SshClient.Services
return "hostbased";
}
}
- public UserAuthenticationHost(SessionInfo sessionInfo)
- : base(sessionInfo)
+ public UserAuthenticationHost(Session session)
+ : base(session)
{
}
diff --git a/Renci.SshClient/Renci.SshClient/Services/UserAuthenticationPassword.cs b/Renci.SshClient/Renci.SshClient/Services/UserAuthenticationPassword.cs
index e2f9130e..546806fb 100644
--- a/Renci.SshClient/Renci.SshClient/Services/UserAuthenticationPassword.cs
+++ b/Renci.SshClient/Renci.SshClient/Services/UserAuthenticationPassword.cs
@@ -13,8 +13,8 @@ namespace Renci.SshClient.Services
}
}
- public UserAuthenticationPassword(SessionInfo sessionInfo)
- : base(sessionInfo)
+ public UserAuthenticationPassword(Session session)
+ : base(session)
{
}
@@ -24,13 +24,13 @@ namespace Renci.SshClient.Services
// TODO: Handle all user authentication messages
//Message.RegisterMessageType(MessageTypes.UserAuthenticationPasswordChangeRequired);
- if (!string.IsNullOrEmpty(this.SessionInfo.ConnectionInfo.Password))
+ if (!string.IsNullOrEmpty(this.Session.ConnectionInfo.Password))
{
this.SendMessage(new PasswordRequestMessage
{
ServiceName = ServiceNames.Connection,
- Username = this.SessionInfo.ConnectionInfo.Username,
- Password = this.SessionInfo.ConnectionInfo.Password,
+ Username = this.Session.ConnectionInfo.Username,
+ Password = this.Session.ConnectionInfo.Password,
});
return true;
}
diff --git a/Renci.SshClient/Renci.SshClient/Services/UserAuthenticationPublicKey.cs b/Renci.SshClient/Renci.SshClient/Services/UserAuthenticationPublicKey.cs
index 1d63095b..f83b478e 100644
--- a/Renci.SshClient/Renci.SshClient/Services/UserAuthenticationPublicKey.cs
+++ b/Renci.SshClient/Renci.SshClient/Services/UserAuthenticationPublicKey.cs
@@ -13,8 +13,8 @@ namespace Renci.SshClient.Services
}
}
- public UserAuthenticationPublicKey(SessionInfo sessionInfo)
- : base(sessionInfo)
+ public UserAuthenticationPublicKey(Session session)
+ : base(session)
{
}
@@ -22,16 +22,16 @@ namespace Renci.SshClient.Services
public override bool Start()
{
- if (this.SessionInfo.ConnectionInfo.KeyFile != null)
+ if (this.Session.ConnectionInfo.KeyFile != null)
{
// TODO: Complete full public key implemention which includes other messages
this.SendMessage(new PublicKeyRequestMessage
{
ServiceName = ServiceNames.Connection,
- Username = this.SessionInfo.ConnectionInfo.Username,
- PublicKeyAlgorithmName = this.SessionInfo.ConnectionInfo.KeyFile.AlgorithmName,
- PublicKeyData = this.SessionInfo.ConnectionInfo.KeyFile.PublicKey,
- Signature = this.SessionInfo.ConnectionInfo.KeyFile.GetSignature(this.SessionInfo.SessionId),
+ Username = this.Session.ConnectionInfo.Username,
+ PublicKeyAlgorithmName = this.Session.ConnectionInfo.KeyFile.AlgorithmName,
+ PublicKeyData = this.Session.ConnectionInfo.KeyFile.PublicKey,
+ Signature = this.Session.ConnectionInfo.KeyFile.GetSignature(this.Session.SessionId),
});
return true;
}
diff --git a/Renci.SshClient/Renci.SshClient/Services/UserAuthenticationService.cs b/Renci.SshClient/Renci.SshClient/Services/UserAuthenticationService.cs
index 788d8fb4..1cdcf68c 100644
--- a/Renci.SshClient/Renci.SshClient/Services/UserAuthenticationService.cs
+++ b/Renci.SshClient/Renci.SshClient/Services/UserAuthenticationService.cs
@@ -27,8 +27,8 @@ namespace Renci.SshClient.Services
public bool IsAuthenticated { get; private set; }
- public UserAuthenticationService(SessionInfo sessionInfo)
- : base(sessionInfo)
+ public UserAuthenticationService(Session session)
+ : base(session)
{
this.AuthenticationCompletedHandle = new AutoResetEvent(false);
}
@@ -41,7 +41,7 @@ namespace Renci.SshClient.Services
Message.RegisterMessageType(MessageTypes.UserAuthenticationBanner);
// Attach event handlers to handle messages
- this.SessionInfo.MessageReceived += SessionInfo_MessageReceived;
+ this.Session.MessageReceived += SessionInfo_MessageReceived;
// Request user authorization service
this.SendMessage(new ServiceRequestMessage
@@ -50,19 +50,19 @@ namespace Renci.SshClient.Services
});
// Wait for service to be accepted
- this.SessionInfo.WaitHandle(this._serviceAccepted);
+ this.Session.WaitHandle(this._serviceAccepted);
// Start by quering supported authentication methods
this.SendMessage(new RequestMessage
{
- Username = this.SessionInfo.ConnectionInfo.Username,
+ Username = this.Session.ConnectionInfo.Username,
ServiceName = ServiceNames.Connection,
});
// Wait for authentication to be completed
- this.SessionInfo.WaitHandle(this._authenticationCompleted);
+ this.Session.WaitHandle(this._authenticationCompleted);
- this.SessionInfo.MessageReceived -= SessionInfo_MessageReceived;
+ this.Session.MessageReceived -= SessionInfo_MessageReceived;
Message.UnRegisterMessageType(MessageTypes.UserAuthenticationFailure);
Message.UnRegisterMessageType(MessageTypes.UserAuthenticationSuccess);
@@ -105,7 +105,7 @@ namespace Renci.SshClient.Services
if (methodsToTry.Count() == 0)
{
- this.AuthenticationFailed(string.Format("User '{0}' cannot be authorized.", this.SessionInfo.ConnectionInfo.Username));
+ this.AuthenticationFailed(string.Format("User '{0}' cannot be authorized.", this.Session.ConnectionInfo.Username));
return;
}
@@ -116,11 +116,11 @@ namespace Renci.SshClient.Services
if (methodName == "publickey")
{
- userAuthentication = new UserAuthenticationPublicKey(this.SessionInfo);
+ userAuthentication = new UserAuthenticationPublicKey(this.Session);
}
else if (methodName == "password")
{
- userAuthentication = new UserAuthenticationPassword(this.SessionInfo);
+ userAuthentication = new UserAuthenticationPassword(this.Session);
}
this._executedMethods.Add(methodName);
if (userAuthentication != null)
diff --git a/Renci.SshClient/Renci.SshClient/Session.cs b/Renci.SshClient/Renci.SshClient/Session.cs
index f86e4a71..586f893b 100644
--- a/Renci.SshClient/Renci.SshClient/Session.cs
+++ b/Renci.SshClient/Renci.SshClient/Session.cs
@@ -9,6 +9,7 @@ using System.Security.Cryptography;
using System.Text;
using System.Threading;
using Renci.SshClient.Algorithms;
+using Renci.SshClient.Channels;
using Renci.SshClient.Common;
using Renci.SshClient.Messages;
using Renci.SshClient.Messages.Connection;
@@ -26,7 +27,8 @@ namespace Renci.SshClient
var ep = new IPEndPoint(Dns.GetHostAddresses(connectionInfo.Host)[0], connectionInfo.Port);
var socket = new Socket(ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
socket.Connect(ep);
- socket.ReceiveTimeout = 5 * 1000; // Set default receive timeout to 5 seconds
+ //socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, 1000 * 15);
+ //socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 1000 * 5);
// Get server version from the server,
// ignore text lines which are sent before if any
@@ -50,10 +52,10 @@ namespace Renci.SshClient
return session;
}
- private ConnectionInfo _connectionInfo;
-
private Socket _socket;
+ private int _waitTimeout = 5 * 1000; // Set default receive timeout to 5 seconds
+
private KeyExchange _keyExhcange;
private BackgroundWorker _messageListener;
@@ -64,6 +66,14 @@ namespace Renci.SshClient
private IDictionary _openChannels = new Dictionary();
+ private EventWaitHandle _disconnectWaitHandle = new AutoResetEvent(false);
+
+ private EventWaitHandle _exceptionWaitHandle = new AutoResetEvent(false);
+
+ private Exception _exceptionToThrow;
+
+ public event EventHandler MessageReceived;
+
protected HMAC ServerMac { get; private set; }
protected HMAC ClientMac { get; private set; }
@@ -76,7 +86,14 @@ namespace Renci.SshClient
protected Compression ClientCompression { get; private set; }
- internal SessionInfo SessionInfo { get; private set; }
+ // TODO: Consider refactor to make setter private
+ public IEnumerable SessionId { get; set; }
+
+ public string ServerVersion { get; private set; }
+
+ public string ClientVersion { get; private set; }
+
+ public ConnectionInfo ConnectionInfo { get; private set; }
public bool IsConnected
{
@@ -90,18 +107,32 @@ namespace Renci.SshClient
protected Session(ConnectionInfo connectionInfo, Socket socket, string serverVersion)
{
- this._connectionInfo = connectionInfo;
+ this.ConnectionInfo = connectionInfo;
this._socket = socket;
+ //this._socket.ReceiveTimeout = this._waitTimeout;
- this.SessionInfo = new SessionInfo(this.SendMessage, this._connectionInfo, socket.ReceiveTimeout);
- this.SessionInfo.ServerVersion = serverVersion;
- this.SessionInfo.ClientVersion = string.Format("SSH-2.0-Renci.SshClient.{0}", this.GetType().Assembly.GetName().Version);
+ this.ServerVersion = serverVersion;
+ this.ClientVersion = string.Format("SSH-2.0-Renci.SshClient.{0}", this.GetType().Assembly.GetName().Version);
+ }
+ private static IDictionary> _channels = new Dictionary>()
+ {
+ {typeof(ChannelSession), (session) => { return new ChannelSession(session);}},
+ {typeof(ChannelSftp), (session) => { return new ChannelSftp(session);}}
+ };
+
+ public T CreateChannel() where T : Channel
+ {
+ if (!this._socket.Connected)
+ {
+ throw new InvalidOperationException("Not connected");
+ }
+ return _channels[typeof(T)](this) as T;
}
public void Connect()
{
- this.Write(Encoding.ASCII.GetBytes(string.Format("{0}\n", this.SessionInfo.ClientVersion)));
+ this.Write(Encoding.ASCII.GetBytes(string.Format("{0}\n", this.ClientVersion)));
// Register Transport response messages
Message.RegisterMessageType(MessageTypes.Disconnect);
@@ -118,16 +149,16 @@ namespace Renci.SshClient
this._messageListener.RunWorkerAsync();
// Wait for key exchange to be completed
- this.SessionInfo.WaitHandle(this._keyExhangedFinishedWaitHandle);
+ this.WaitHandle(this._keyExhangedFinishedWaitHandle);
// If sessionId is not set then its not connected
- if (this.SessionInfo.SessionId == null)
+ if (this.SessionId == null)
{
this.Disconnect();
return;
}
- var authenticationService = new UserAuthenticationService(this.SessionInfo);
+ var authenticationService = new UserAuthenticationService(this);
authenticationService.AuthenticateUser();
@@ -145,7 +176,33 @@ namespace Renci.SshClient
this.DisconnectCleanup();
}
- protected abstract void SendMessage(Message message);
+ internal abstract void SendMessage(Message message);
+
+ internal void WaitHandle(EventWaitHandle waitHandle)
+ {
+ var waitHandles = new EventWaitHandle[]
+ {
+ this._disconnectWaitHandle,
+ this._exceptionWaitHandle,
+ waitHandle,
+ };
+
+ //EventWaitHandle.WaitAny(waitHandles);
+
+ var index = EventWaitHandle.WaitAny(waitHandles, this._waitTimeout);
+
+ if (this._exceptionToThrow != null)
+ {
+ var exception = this._exceptionToThrow;
+ this._exceptionToThrow = null;
+ throw exception;
+ }
+ else if (index > waitHandles.Length)
+ {
+ // TODO: Issue timeout disconnect message if approapriate
+ throw new TimeoutException();
+ }
+ }
protected abstract Message ReceiveMessage();
@@ -196,7 +253,7 @@ namespace Renci.SshClient
}
// Create key exchange algorithm
- this._keyExhcange = KeyExchange.Create(message, this.SessionInfo);
+ this._keyExhcange = KeyExchange.Create(message, this);
this._keyExhcange.Failed += delegate(object sender, KeyExchangeFailedEventArgs e)
{
@@ -261,20 +318,35 @@ namespace Renci.SshClient
{
var buffer = new byte[length];
- SocketError socketErrorCode;
+ SocketError socketErrorCode = SocketError.Success;
- this._socket.Receive(buffer, 0, length, SocketFlags.None, out socketErrorCode);
+ int bytesRead = 0;
- // Check for socket errors
- if (socketErrorCode != SocketError.Success)
+ while (bytesRead == 0 && this._socket.Connected && socketErrorCode == SocketError.Success)
{
- throw new SocketException((int)socketErrorCode);
+ var asynchResult = this._socket.BeginReceive(buffer, 0, length, SocketFlags.None, null, null);
+
+ while (!asynchResult.IsCompleted && this._socket.Connected)
+ {
+ asynchResult.AsyncWaitHandle.WaitOne(this._waitTimeout);
+ }
+ bytesRead = this._socket.EndReceive(asynchResult, out socketErrorCode);
}
- else if (!this._socket.Connected)
+
+ // Check for errors
+ if (!this._socket.Connected)
{
// If socket was closed throw an exception
throw new SocketException(995); // WSA_OPERATION_ABORTED
}
+ if (bytesRead != length && socketErrorCode == SocketError.Success)
+ {
+ throw new InvalidDataException(string.Format("Data read {0}, expected {1}", bytesRead, length));
+ }
+ else if (socketErrorCode != SocketError.Success)
+ {
+ throw new SocketException((int)socketErrorCode);
+ }
else
return buffer;
}
@@ -322,9 +394,6 @@ namespace Renci.SshClient
{
this._socket.Disconnect(false);
}
-
- // Let session know that it was disconected
- this.SessionInfo.Disconnect();
}
private void MessageListener_DoWork(object sender, DoWorkEventArgs e)
@@ -344,13 +413,19 @@ namespace Renci.SshClient
this.HandleMessage(message);
// Raise an event that message received
- this.SessionInfo.RaiseMessageReceived(this, new MessageReceivedEventArgs(message));
+ this.RaiseMessageReceived(this, new MessageReceivedEventArgs(message));
}
catch (Exception exp)
{
+ // TODO: This exception can be swolloed if it occures while running in the background, look for possible solutions
+
// In case of error issue disconntect command
this.Disconnect(DisconnectReasonCodes.ByApplication, exp.ToString());
+ // Set exception that need to be thrown by the main thread
+ this._exceptionToThrow = exp;
+
+ this._exceptionWaitHandle.Set();
// TODO: Set exp to some excpetion that can be thrown later by the main thread.
break;
@@ -358,6 +433,14 @@ namespace Renci.SshClient
}
}
+ private void RaiseMessageReceived(object sender, MessageReceivedEventArgs args)
+ {
+ if (this.MessageReceived != null)
+ {
+ this.MessageReceived(sender, args);
+ }
+ }
+
private void HandleMessage(ChannelOpenConfirmationMessage message)
{
// Keep track of open channels
diff --git a/Renci.SshClient/Renci.SshClient/SessionInfo.cs b/Renci.SshClient/Renci.SshClient/SessionInfo.cs
deleted file mode 100644
index 279ec2d3..00000000
--- a/Renci.SshClient/Renci.SshClient/SessionInfo.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using Renci.SshClient.Common;
-using Renci.SshClient.Messages;
-
-namespace Renci.SshClient
-{
- public delegate void SendMessage(Message message);
-
- internal class SessionInfo
- {
- private SendMessage _sendMessage;
-
- private EventWaitHandle _disconnectWaitHandle = new AutoResetEvent(false);
-
- private int _waitTimeout;
-
- public ConnectionInfo ConnectionInfo { get; private set; }
-
- public IEnumerable SessionId { get; set; }
-
- public string ServerVersion { get; set; }
-
- public string ClientVersion { get; set; }
-
- public SessionInfo(SendMessage sendMessage, ConnectionInfo connectionInfo, int waitTimeout)
- {
- this._sendMessage = sendMessage;
- this._waitTimeout = waitTimeout;
- this.ConnectionInfo = connectionInfo;
- }
-
- public event EventHandler MessageReceived;
-
- public void RaiseMessageReceived(object sender, MessageReceivedEventArgs args)
- {
- if (this.MessageReceived != null)
- {
- this.MessageReceived(sender, args);
- }
- }
-
- public void SendMessage(Message message)
- {
- this._sendMessage(message);
- }
-
- public void Disconnect()
- {
- this._disconnectWaitHandle.Set();
- }
-
- public void WaitHandle(EventWaitHandle waitHandle)
- {
- var waitHandles = new EventWaitHandle[]
- {
- this._disconnectWaitHandle,
- waitHandle,
- };
- var index = EventWaitHandle.WaitAny(waitHandles);
-
- //var index = EventWaitHandle.WaitAny(waitHandles, this._waitTimeout);
-
- //if (index > waitHandles.Length)
- //{
- // // TODO: Issue timeout disconnect message if approapriate
- // throw new TimeoutException();
- //}
- }
- }
-}
diff --git a/Renci.SshClient/Renci.SshClient/SessionSSHv2.cs b/Renci.SshClient/Renci.SshClient/SessionSSHv2.cs
index 0c84e58e..ab4d8c4c 100644
--- a/Renci.SshClient/Renci.SshClient/SessionSSHv2.cs
+++ b/Renci.SshClient/Renci.SshClient/SessionSSHv2.cs
@@ -22,7 +22,7 @@ namespace Renci.SshClient
{
}
- protected override void SendMessage(Message message)
+ internal override void SendMessage(Message message)
{
if (!this.IsConnected)
return;
diff --git a/Renci.SshClient/Renci.SshClient/Settings.cs b/Renci.SshClient/Renci.SshClient/Settings.cs
index 4304a6c1..9b3828b9 100644
--- a/Renci.SshClient/Renci.SshClient/Settings.cs
+++ b/Renci.SshClient/Renci.SshClient/Settings.cs
@@ -8,7 +8,7 @@ namespace Renci.SshClient
{
internal static class Settings
{
- public static IDictionary> KeyExchangeAlgorithms { get; private set; }
+ public static IDictionary> KeyExchangeAlgorithms { get; private set; }
public static IDictionary> Encryptions { get; private set; }
@@ -20,7 +20,7 @@ namespace Renci.SshClient
static Settings()
{
- Settings.KeyExchangeAlgorithms = new Dictionary>()
+ Settings.KeyExchangeAlgorithms = new Dictionary>()
{
{"diffie-hellman-group1-sha1", (a) => { return new KeyExchangeDiffieHellman(a);}}
//"diffie-hellman-group-exchange-sha1"
diff --git a/Renci.SshClient/Renci.SshClient/Sftp.cs b/Renci.SshClient/Renci.SshClient/Sftp.cs
index 4e883947..b1827e7a 100644
--- a/Renci.SshClient/Renci.SshClient/Sftp.cs
+++ b/Renci.SshClient/Renci.SshClient/Sftp.cs
@@ -16,8 +16,8 @@ namespace Renci.SshClient
{
this._session = session;
- // TODO: Keep track of all open channels to disconnect them when connection is closed
- this._channel = new ChannelSftp(this._session.SessionInfo);
+ //this._channel = new ChannelSftp(this._session);
+ this._channel = this._session.CreateChannel();
}
@@ -31,11 +31,22 @@ namespace Renci.SshClient
this._channel.UploadFile(source, fileName);
}
+ public void UploadFile(string source, string fileName)
+ {
+ this._channel.UploadFile(File.OpenRead(source), fileName);
+ }
+
public void DownloadFile(string fileName, Stream destination)
{
this._channel.DownloadFile(fileName, destination);
}
+ public void DownloadFile(string fileName, string destination)
+ {
+ var file = File.Create(destination);
+ this._channel.DownloadFile(fileName, file);
+ }
+
public void RemoveFile(string fileName)
{
this._channel.RemoveFile(fileName);
diff --git a/Renci.SshClient/Renci.SshClient/Shell.cs b/Renci.SshClient/Renci.SshClient/Shell.cs
index 5430fd48..87eafa0b 100644
--- a/Renci.SshClient/Renci.SshClient/Shell.cs
+++ b/Renci.SshClient/Renci.SshClient/Shell.cs
@@ -13,8 +13,9 @@ namespace Renci.SshClient
public string Execute(string command)
{
- // TODO: Keep track of all open channels to cdisconnect them when connection is closed
- var channel = new ChannelSession(this._session.SessionInfo);
+ //var channel = new ChannelSession(this._session);
+
+ var channel = this._session.CreateChannel();
var result = channel.Execute(command);