diff --git a/src/Renci.SshNet/BaseClient.cs b/src/Renci.SshNet/BaseClient.cs
index 2ed1e265..b89e2a71 100644
--- a/src/Renci.SshNet/BaseClient.cs
+++ b/src/Renci.SshNet/BaseClient.cs
@@ -1,6 +1,7 @@
using System;
using System.Net.Sockets;
using System.Threading;
+using Renci.SshNet.Abstractions;
using Renci.SshNet.Common;
using Renci.SshNet.Messages.Transport;
@@ -221,6 +222,8 @@ namespace Renci.SshNet
/// The method was called after the client was disposed.
public void Disconnect()
{
+ DiagnosticAbstraction.Log(string.Format("{0} Disconnecting client", DateTime.Now.Ticks));
+
CheckDisposed();
OnDisconnecting();
@@ -317,6 +320,8 @@ namespace Renci.SshNet
///
public void Dispose()
{
+ DiagnosticAbstraction.Log(string.Format("{0} Disposing client", DateTime.Now.Ticks));
+
Dispose(true);
GC.SuppressFinalize(this);
}
diff --git a/src/Renci.SshNet/Common/SshData.cs b/src/Renci.SshNet/Common/SshData.cs
index f1383282..3016e0ee 100644
--- a/src/Renci.SshNet/Common/SshData.cs
+++ b/src/Renci.SshNet/Common/SshData.cs
@@ -47,20 +47,7 @@ namespace Renci.SshNet.Common
private byte[] _loadedData;
private int _offset;
-
- ///
- /// Gets the index that represents zero in current data type.
- ///
- ///
- /// The index of the zero reader.
- ///
- protected virtual int ZeroReaderIndex
- {
- get
- {
- return 0;
- }
- }
+ private int _count;
///
/// Gets the size of the message in bytes.
@@ -99,30 +86,41 @@ namespace Renci.SshNet.Common
internal T OfType() where T : SshData, new()
{
var result = new T();
- result.LoadBytes(_loadedData, _offset);
- result.LoadData();
+ result.Load(_loadedData, _offset, _count);
return result;
}
///
/// Loads data from specified bytes.
///
- /// Bytes array.
- /// is null.
- public void Load(byte[] value)
+ /// Bytes array.
+ /// is null.
+ public void Load(byte[] data)
{
- Load(value, 0);
+ if (data == null)
+ throw new ArgumentNullException("data");
+
+ LoadInternal(data, 0, data.Length);
}
///
/// Loads data from the specified buffer.
///
- /// Bytes array.
- /// The zero-based offset in at which to begin reading SSH data.
- /// is null.
- public void Load(byte[] value, int offset)
+ /// Bytes array.
+ /// The zero-based offset in at which to begin reading SSH data.
+ /// The number of bytes to load.
+ /// is null.
+ public void Load(byte[] data, int offset, int count)
{
- LoadBytes(value, offset);
+ if (data == null)
+ throw new ArgumentNullException("data");
+
+ LoadInternal(data, offset, count);
+ }
+
+ private void LoadInternal(byte[] value, int offset, int count)
+ {
+ LoadBytes(value, offset, count);
LoadData();
}
@@ -136,40 +134,19 @@ namespace Renci.SshNet.Common
///
protected abstract void SaveData();
- ///
- /// Loads data bytes into internal buffer.
- ///
- /// The bytes.
- /// is null.
- protected void LoadBytes(byte[] bytes)
- {
- LoadBytes(bytes, 0);
- }
-
///
/// Loads data bytes into internal buffer.
///
/// The bytes.
/// The zero-based offset in at which to begin reading SSH data.
- /// is null.
- protected void LoadBytes(byte[] bytes, int offset)
+ /// The number of bytes to load.
+ private void LoadBytes(byte[] bytes, int offset, int count)
{
- if (bytes == null)
- throw new ArgumentNullException("bytes");
-
_loadedData = bytes;
_offset = offset;
+ _count = count;
- _stream = new SshDataStream(bytes);
- ResetReader();
- }
-
- ///
- /// Resets internal data reader index.
- ///
- protected void ResetReader()
- {
- _stream.Position = ZeroReaderIndex + _offset;
+ _stream = new SshDataStream(bytes, _offset, count);
}
///
diff --git a/src/Renci.SshNet/Common/SshDataStream.cs b/src/Renci.SshNet/Common/SshDataStream.cs
index 36ee3f8f..408d8704 100644
--- a/src/Renci.SshNet/Common/SshDataStream.cs
+++ b/src/Renci.SshNet/Common/SshDataStream.cs
@@ -30,6 +30,18 @@ namespace Renci.SshNet.Common
{
}
+ ///
+ /// Initializes a new non-resizable instance of the class based on the specified byte array.
+ ///
+ /// The array of unsigned bytes from which to create the current stream.
+ /// The zero-based offset in at which to begin reading SSH data.
+ /// The number of bytes to load.
+ /// is null.
+ public SshDataStream(byte[] buffer, int offset, int count)
+ : base(buffer, offset, count)
+ {
+ }
+
///
/// Gets a value indicating whether all data from the SSH data stream has been read.
///
diff --git a/src/Renci.SshNet/Messages/Message.cs b/src/Renci.SshNet/Messages/Message.cs
index b438d0e9..1107ba1a 100644
--- a/src/Renci.SshNet/Messages/Message.cs
+++ b/src/Renci.SshNet/Messages/Message.cs
@@ -12,20 +12,6 @@ namespace Renci.SshNet.Messages
///
public abstract class Message : SshData
{
- ///
- /// Gets the index that represents zero in current data type.
- ///
- ///
- /// The index of the zero reader.
- ///
- protected override int ZeroReaderIndex
- {
- get
- {
- return 1;
- }
- }
-
///
/// Gets the size of the message in bytes.
///
diff --git a/src/Renci.SshNet/Messages/Transport/KeyExchangeDhInitMessage.cs b/src/Renci.SshNet/Messages/Transport/KeyExchangeDhInitMessage.cs
index bf6dd21d..f15c1d30 100644
--- a/src/Renci.SshNet/Messages/Transport/KeyExchangeDhInitMessage.cs
+++ b/src/Renci.SshNet/Messages/Transport/KeyExchangeDhInitMessage.cs
@@ -49,7 +49,6 @@ namespace Renci.SshNet.Messages.Transport
///
protected override void LoadData()
{
- ResetReader();
_eBytes = ReadBinary();
}
diff --git a/src/Renci.SshNet/Messages/Transport/KeyExchangeDhReplyMessage.cs b/src/Renci.SshNet/Messages/Transport/KeyExchangeDhReplyMessage.cs
index 76532e35..5979484c 100644
--- a/src/Renci.SshNet/Messages/Transport/KeyExchangeDhReplyMessage.cs
+++ b/src/Renci.SshNet/Messages/Transport/KeyExchangeDhReplyMessage.cs
@@ -56,7 +56,6 @@ namespace Renci.SshNet.Messages.Transport
///
protected override void LoadData()
{
- ResetReader();
HostKey = ReadBinary();
_fBytes = ReadBinary();
Signature = ReadBinary();
diff --git a/src/Renci.SshNet/Messages/Transport/KeyExchangeEcdhInitMessage.cs b/src/Renci.SshNet/Messages/Transport/KeyExchangeEcdhInitMessage.cs
index d7b31981..d0222fb8 100644
--- a/src/Renci.SshNet/Messages/Transport/KeyExchangeEcdhInitMessage.cs
+++ b/src/Renci.SshNet/Messages/Transport/KeyExchangeEcdhInitMessage.cs
@@ -51,7 +51,6 @@ namespace Renci.SshNet.Messages.Transport
///
protected override void LoadData()
{
- ResetReader();
QC = ReadBinary();
}
diff --git a/src/Renci.SshNet/Messages/Transport/KeyExchangeEcdhReplyMessage.cs b/src/Renci.SshNet/Messages/Transport/KeyExchangeEcdhReplyMessage.cs
index b8af8875..4815ad8a 100644
--- a/src/Renci.SshNet/Messages/Transport/KeyExchangeEcdhReplyMessage.cs
+++ b/src/Renci.SshNet/Messages/Transport/KeyExchangeEcdhReplyMessage.cs
@@ -49,7 +49,6 @@
///
protected override void LoadData()
{
- ResetReader();
KS = ReadBinary();
QS = ReadBinary();
Signature = ReadBinary();
diff --git a/src/Renci.SshNet/Messages/Transport/KeyExchangeInitMessage.cs b/src/Renci.SshNet/Messages/Transport/KeyExchangeInitMessage.cs
index 00e6f6d4..7c8e9338 100644
--- a/src/Renci.SshNet/Messages/Transport/KeyExchangeInitMessage.cs
+++ b/src/Renci.SshNet/Messages/Transport/KeyExchangeInitMessage.cs
@@ -140,8 +140,6 @@ namespace Renci.SshNet.Messages.Transport
///
protected override void LoadData()
{
- ResetReader();
-
Cookie = ReadBytes(16);
KeyExchangeAlgorithms = ReadNamesList();
ServerHostKeyAlgorithms = ReadNamesList();
diff --git a/src/Renci.SshNet/PrivateKeyFile.cs b/src/Renci.SshNet/PrivateKeyFile.cs
index 5df9d927..f4467234 100644
--- a/src/Renci.SshNet/PrivateKeyFile.cs
+++ b/src/Renci.SshNet/PrivateKeyFile.cs
@@ -391,7 +391,7 @@ namespace Renci.SshNet
{
public SshDataReader(byte[] data)
{
- LoadBytes(data);
+ Load(data);
}
public new uint ReadUInt32()
diff --git a/src/Renci.SshNet/Session.NET.cs b/src/Renci.SshNet/Session.NET.cs
index 4c0c951d..93a9b334 100644
--- a/src/Renci.SshNet/Session.NET.cs
+++ b/src/Renci.SshNet/Session.NET.cs
@@ -1,4 +1,6 @@
-using System.Net.Sockets;
+using System;
+using System.Net.Sockets;
+using Renci.SshNet.Abstractions;
namespace Renci.SshNet
{
@@ -70,10 +72,12 @@ namespace Renci.SshNet
// actually received
lock (_socketReadLock)
{
+ DiagnosticAbstraction.Log(string.Format("[{0}] {1} Checking socket", ToHex(SessionId), DateTime.Now.Ticks));
+
// reset waithandle, as we're only interested in reads that take
// place between Poll and the Available check
_bytesReadFromSocket.Reset();
- var connectionClosedOrDataAvailable = _socket.Poll(1000, SelectMode.SelectRead);
+ var connectionClosedOrDataAvailable = _socket.Poll(100, SelectMode.SelectRead);
isConnected = !(connectionClosedOrDataAvailable && _socket.Available == 0);
if (!isConnected)
{
@@ -83,6 +87,8 @@ namespace Renci.SshNet
// shortly after
isConnected = _bytesReadFromSocket.WaitOne(500);
}
+
+ DiagnosticAbstraction.Log(string.Format("[{0}] {1} Checked socket", ToHex(SessionId), DateTime.Now.Ticks));
}
}
#endif // FEATURE_SOCKET_POLL
diff --git a/src/Renci.SshNet/Session.cs b/src/Renci.SshNet/Session.cs
index 44296f0d..0e5657a6 100644
--- a/src/Renci.SshNet/Session.cs
+++ b/src/Renci.SshNet/Session.cs
@@ -657,7 +657,7 @@ namespace Renci.SshNet
///
public void Disconnect()
{
- DiagnosticAbstraction.Log(string.Format("[{0}] Disconnecting session", ToHex(SessionId)));
+ DiagnosticAbstraction.Log(string.Format("[{0}] {1} Disconnecting session", ToHex(SessionId), DateTime.Now.Ticks));
Disconnect(DisconnectReason.ByApplication, "Connection terminated by the client.");
@@ -892,12 +892,16 @@ namespace Renci.SshNet
// Read first block - which starts with the packet length
var firstBlock = Read(blockSize);
+#if DEBUG_GERT
DiagnosticAbstraction.Log(string.Format("[{0}] FirstBlock [{1}]: {2}", ToHex(SessionId), blockSize, ToHex(firstBlock)));
+#endif // DEBUG_GERT
if (_serverCipher != null)
{
firstBlock = _serverCipher.Decrypt(firstBlock);
+#if DEBUG_GERT
DiagnosticAbstraction.Log(string.Format("[{0}] FirstBlock decrypted [{1}]: {2}", ToHex(SessionId), firstBlock.Length, ToHex(firstBlock)));
+#endif // DEBUG_GERT
}
var packetLength = (uint)(firstBlock[0] << 24 | firstBlock[1] << 16 | firstBlock[2] << 8 | firstBlock[3]);
@@ -927,14 +931,17 @@ namespace Renci.SshNet
{
var nextBlocks = Read(bytesToRead);
+#if DEBUG_GERT
DiagnosticAbstraction.Log(string.Format("[{0}] NextBlocks [{1}]: {2}", ToHex(SessionId), bytesToRead, ToHex(nextBlocks)));
+#endif // DEBUG_GERT
if (serverHash != null)
{
Buffer.BlockCopy(nextBlocks, nextBlocks.Length - serverHash.Length, serverHash, 0, serverHash.Length);
nextBlocks = nextBlocks.Take(nextBlocks.Length - serverHash.Length);
-
+#if DEBUG_GERT
DiagnosticAbstraction.Log(string.Format("[{0}] ServerHash [{1}]: {2}", ToHex(SessionId), serverHash.Length, ToHex(serverHash)));
+#endif // DEBUG_GERT
}
if (nextBlocks.Length > 0)
@@ -942,7 +949,9 @@ namespace Renci.SshNet
if (_serverCipher != null)
{
nextBlocks = _serverCipher.Decrypt(nextBlocks);
+#if DEBUG_GERT
DiagnosticAbstraction.Log(string.Format("[{0}] NextBlocks decrypted [{1}]: {2}", ToHex(SessionId), nextBlocks.Length, ToHex(nextBlocks)));
+#endif // DEBUG_GERT
}
nextBlocks.CopyTo(data, blockSize + inboundPacketSequenceLength);
@@ -970,13 +979,17 @@ namespace Renci.SshNet
// data now only contains the decompressed payload, and as such the offset is reset to zero
messagePayloadOffset = 0;
+ // the length of the payload is now the complete decompressed content
+ messagePayloadLength = data.Length;
}
+#if DEBUG_GERT
DiagnosticAbstraction.Log(string.Format("[{0}] Message info (Sequence:{1},MessagePayloadLength:{2})", ToHex(SessionId), _inboundPacketSequence, messagePayloadLength));
+#endif // DEBUG_GERT
_inboundPacketSequence++;
- return LoadMessage(data, messagePayloadOffset);
+ return LoadMessage(data, messagePayloadOffset, messagePayloadLength);
}
private void SendDisconnect(DisconnectReason reasonCode, string message)
@@ -1006,7 +1019,7 @@ namespace Renci.SshNet
OnMessageReceived(message);
}
- #region Handle transport messages
+#region Handle transport messages
///
/// Invoked via reflection.
@@ -1076,9 +1089,9 @@ namespace Renci.SshNet
OnNewKeysReceived(message);
}
- #endregion
+#endregion
- #region Handle User Authentication messages
+#region Handle User Authentication messages
///
/// Invoked via reflection.
@@ -1112,9 +1125,9 @@ namespace Renci.SshNet
OnUserAuthenticationBannerReceived(message);
}
- #endregion
+#endregion
- #region Handle connection messages
+#region Handle connection messages
///
/// Invoked via reflection.
@@ -1228,9 +1241,9 @@ namespace Renci.SshNet
OnChannelFailureReceived(message);
}
- #endregion
+#endregion
- #region Handle received message events
+#region Handle received message events
///
/// Called when received.
@@ -1238,7 +1251,7 @@ namespace Renci.SshNet
/// message.
protected virtual void OnDisconnectReceived(DisconnectMessage message)
{
- DiagnosticAbstraction.Log(string.Format("[{0}] Disconnect received: {1} {2}", ToHex(SessionId), message.ReasonCode, message.Description));
+ DiagnosticAbstraction.Log(string.Format("[{0}] {1} Disconnect received: {2} {3}", ToHex(SessionId), DateTime.Now.Ticks, message.ReasonCode, message.Description));
_exception = new SshConnectionException(string.Format(CultureInfo.InvariantCulture, "The connection was closed by the server: {0} ({1}).", message.Description, message.ReasonCode), message.ReasonCode);
_exceptionWaitHandle.Set();
@@ -1606,7 +1619,7 @@ namespace Renci.SshNet
handlers(this, new MessageEventArgs(message));
}
- #endregion
+#endregion
private void KeyExchange_HostKeyReceived(object sender, HostKeyEventArgs e)
{
@@ -1631,7 +1644,7 @@ namespace Renci.SshNet
return buffer;
}
- #region Message loading functions
+#region Message loading functions
///
/// Registers SSH message with the session.
@@ -1656,19 +1669,22 @@ namespace Renci.SshNet
///
/// An array of bytes from which to construct the message.
/// The zero-based byte offset in at which to begin reading.
+ /// The number of bytes to load.
///
/// A message constructed from .
///
/// The type of the message is not supported.
- private Message LoadMessage(byte[] data, int offset)
+ private Message LoadMessage(byte[] data, int offset, int count)
{
var messageType = data[offset];
var message = _sshMessageFactory.Create(messageType);
+#if DEBUG_GERT
DiagnosticAbstraction.Log(string.Format("[{0}] Loading message with offset '{1}': {2}", ToHex(SessionId), offset, ToHex(data, 0)));
+#endif // DEBUG_GERT
- message.Load(data, offset);
+ message.Load(data, offset + 1, count - 1);
DiagnosticAbstraction.Log(string.Format("[{0}] ReceiveMessage from server: '{1}': '{2}'.", ToHex(SessionId), message.GetType().Name, message));
@@ -1698,7 +1714,7 @@ namespace Renci.SshNet
return ToHex(bytes, 0);
}
- #endregion
+#endregion
///
/// Gets a value indicating whether the socket is connected.
@@ -1824,8 +1840,11 @@ namespace Renci.SshNet
_socket.Shutdown(SocketShutdown.Send);
SocketAbstraction.ClearReadBuffer(_socket);
}
+
+ DiagnosticAbstraction.Log(string.Format("[{0}] {1} Disposing socket", ToHex(SessionId), DateTime.Now.Ticks));
_socket.Dispose();
_socket = null;
+ DiagnosticAbstraction.Log(string.Format("[{0}] {1} Disposed socket", ToHex(SessionId), DateTime.Now.Ticks));
}
}
}
@@ -2178,7 +2197,7 @@ namespace Renci.SshNet
return;
}
- DiagnosticAbstraction.Log(string.Format("[{0}] Raised exception: {1}", ToHex(SessionId), exp));
+ DiagnosticAbstraction.Log(string.Format("[{0}] {1} Raised exception: {2}", ToHex(SessionId), DateTime.Now.Ticks, exp));
_exception = exp;
@@ -2190,6 +2209,7 @@ namespace Renci.SshNet
if (connectionException != null)
{
+ DiagnosticAbstraction.Log(string.Format("[{0}] {1} Disconnecting after exception: {2}", ToHex(SessionId), DateTime.Now.Ticks, exp));
Disconnect(connectionException.DisconnectReason, exp.ToString());
}
}
@@ -2215,7 +2235,7 @@ namespace Renci.SshNet
_keyExchangeInProgress = false;
}
- #region IDisposable implementation
+#region IDisposable implementation
private bool _disposed;
@@ -2313,9 +2333,9 @@ namespace Renci.SshNet
Dispose(false);
}
- #endregion IDisposable implementation
+#endregion IDisposable implementation
- #region ISession implementation
+#region ISession implementation
///
/// Gets or sets the connection info.
@@ -2400,6 +2420,6 @@ namespace Renci.SshNet
return TrySendMessage(message);
}
- #endregion ISession implementation
+#endregion ISession implementation
}
}
\ No newline at end of file
diff --git a/src/Renci.SshNet/Sftp/SftpMessage.cs b/src/Renci.SshNet/Sftp/SftpMessage.cs
index 13e27213..90fbd0e4 100644
--- a/src/Renci.SshNet/Sftp/SftpMessage.cs
+++ b/src/Renci.SshNet/Sftp/SftpMessage.cs
@@ -9,21 +9,11 @@ namespace Renci.SshNet.Sftp
{
internal abstract class SftpMessage : SshData
{
- public static SftpMessage Load(uint protocolVersion, byte[] data, Encoding encoding)
+ public static SftpMessage Load(uint protocolVersion, byte[] data, int offset, int count, Encoding encoding)
{
- var messageType = (SftpMessageTypes) data[4]; // skip packet length bytes
+ var messageType = (SftpMessageTypes) data[offset];
- return Load(protocolVersion, data, messageType, encoding);
- }
-
- protected override int ZeroReaderIndex
- {
- get
- {
- // 4 bytes for the length of the SFTP data
- // 1 byte for the SFTP message type
- return 5;
- }
+ return Load(protocolVersion, messageType, data, offset + 1, count - 1, encoding);
}
///
@@ -34,7 +24,12 @@ namespace Renci.SshNet.Sftp
///
protected override int BufferCapacity
{
- get { return ZeroReaderIndex; }
+ get
+ {
+ // 4 bytes for the length of the SFTP data
+ // 1 byte for the SFTP message type
+ return 5;
+ }
}
public abstract SftpMessageTypes SftpMessageType { get; }
@@ -84,7 +79,7 @@ namespace Renci.SshNet.Sftp
return SftpFileAttributes.FromBytes(DataStream);
}
- private static SftpMessage Load(uint protocolVersion, byte[] data, SftpMessageTypes messageType, Encoding encoding)
+ private static SftpMessage Load(uint protocolVersion, SftpMessageTypes messageType, byte[] data, int offset, int count, Encoding encoding)
{
SftpMessage message;
@@ -115,7 +110,7 @@ namespace Renci.SshNet.Sftp
throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Message type '{0}' is not supported.", messageType));
}
- message.Load(data);
+ message.Load(data, offset, data.Length - offset);
return message;
}
diff --git a/src/Renci.SshNet/Sftp/SftpSession.cs b/src/Renci.SshNet/Sftp/SftpSession.cs
index c66ff71c..0fb775c6 100644
--- a/src/Renci.SshNet/Sftp/SftpSession.cs
+++ b/src/Renci.SshNet/Sftp/SftpSession.cs
@@ -163,55 +163,149 @@ namespace Renci.SshNet.Sftp
protected override void OnDataReceived(byte[] data)
{
- // Add channel data to internal data holder
- _data.AddRange(data);
+ const int packetLengthByteCount = 4;
+ const int sftpMessageTypeByteCount = 1;
+ const int minimumChannelDataLength = packetLengthByteCount + sftpMessageTypeByteCount;
- while (_data.Count > 4 + 1)
+ var offset = 0;
+ var count = data.Length;
+
+ // improve performance and reduce GC pressure by not buffering channel data if the received
+ // chunk contains the complete packet data.
+ //
+ // for this, the buffer should be empty and the chunk should contain at least the packet length
+ // and the type of the SFTP message
+ if (_data.Count == 0)
{
- // Extract packet length
- var packetLength = (_data[0] << 24 | _data[1] << 16 | _data[2] << 8 | _data[3]);
-
- // Check if complete packet data is available
- if (_data.Count < packetLength + 4)
+ while (count >= minimumChannelDataLength)
{
- // Wait for complete message to arrive first
- break;
- }
+ // extract packet length
+ var packetDataLength = data[offset] << 24 | data[offset + 1] << 16 | data[offset + 2] << 8 |
+ data[offset + 3];
- var packetLengthIncludingBytesForLength = packetLength + 4;
+ var packetTotalLength = packetDataLength + packetLengthByteCount;
- // Create buffer to hold packet data and 4 bytes for packet length
- var packetData = new byte[packetLengthIncludingBytesForLength];
-
- // copy packet data and bytes for length to array
- _data.CopyTo(0, packetData, 0, packetLengthIncludingBytesForLength);
-
- // Remove loaded data and bytes for length from _data holder
- _data.RemoveRange(0, packetLengthIncludingBytesForLength);
-
- // Load SFTP Message and handle it
- var response = SftpMessage.Load(ProtocolVersion, packetData, Encoding);
-
- try
- {
- var versionResponse = response as SftpVersionResponse;
- if (versionResponse != null)
+ // check if complete packet data (or more) is available
+ if (count >= packetTotalLength)
{
- ProtocolVersion = versionResponse.Version;
- _supportedExtensions = versionResponse.Extentions;
+ // load and process SFTP message
+ if (!TryLoadSftpMessage(data, offset + packetLengthByteCount, packetDataLength))
+ {
+ return;
+ }
- _sftpVersionConfirmed.Set();
+ // remove processed bytes from the number of bytes to process as the channel
+ // data we received may contain (part of) another message
+ count -= packetTotalLength;
+
+ // move offset beyond bytes we just processed
+ offset += packetTotalLength;
}
else
{
- HandleResponse(response as SftpResponse);
+ // we don't have a complete message
+ break;
}
}
- catch (Exception exp)
+
+ // check if there is channel data left to process or buffer
+ if (count == 0)
{
- RaiseError(exp);
+ return;
+ }
+
+ // check if we processed part of the channel data we received
+ if (offset > 0)
+ {
+ // add (remaining) channel data to internal data holder
+ var remainingChannelData = new byte[count];
+ Buffer.BlockCopy(data, offset, remainingChannelData, 0, count);
+ _data.AddRange(remainingChannelData);
+ }
+ else
+ {
+ // add (remaining) channel data to internal data holder
+ _data.AddRange(data);
+ }
+
+ // skip further processing as we'll need a new chunk to complete the message
+ return;
+ }
+
+ // add (remaining) channel data to internal data holder
+ _data.AddRange(data);
+
+ while (_data.Count >= minimumChannelDataLength)
+ {
+ // extract packet length
+ var packetDataLength = _data[0] << 24 | _data[1] << 16 | _data[2] << 8 | _data[3];
+
+#if DEBUG_GERT
+ if (packetDataLength > 32 * 1024)
+ Console.WriteLine("BIG PACKAGE: " + packetDataLength + " | " + data.Length);
+#endif // DEBUG_GERT
+
+ var packetTotalLength = packetDataLength + packetLengthByteCount;
+
+ // check if complete packet data is available
+ if (_data.Count < packetTotalLength)
+ {
+ // wait for complete message to arrive first
break;
}
+
+ // create buffer to hold packet data
+ var packetData = new byte[packetDataLength];
+
+ // copy packet data and bytes for length to array
+ _data.CopyTo(packetLengthByteCount, packetData, 0, packetDataLength);
+
+ // remove loaded data and bytes for length from _data holder
+ if (_data.Count == packetTotalLength)
+ {
+ // the only buffered data is the data we're processing
+ _data.Clear();
+ }
+ else
+ {
+ // remove only the data we're processing
+ _data.RemoveRange(0, packetTotalLength);
+ }
+
+ // load and process SFTP message
+ if (!TryLoadSftpMessage(packetData, 0, packetDataLength))
+ {
+ break;
+ }
+ }
+ }
+
+ private bool TryLoadSftpMessage(byte[] packetData, int offset, int count)
+ {
+ // Load SFTP Message and handle it
+ var response = SftpMessage.Load(ProtocolVersion, packetData, offset, count, Encoding);
+
+ try
+ {
+ var versionResponse = response as SftpVersionResponse;
+ if (versionResponse != null)
+ {
+ ProtocolVersion = versionResponse.Version;
+ _supportedExtensions = versionResponse.Extentions;
+
+ _sftpVersionConfirmed.Set();
+ }
+ else
+ {
+ HandleResponse(response as SftpResponse);
+ }
+
+ return true;
+ }
+ catch (Exception exp)
+ {
+ RaiseError(exp);
+ return false;
}
}