mirror of
https://github.com/sshnet/SSH.NET.git
synced 2026-09-10 01:05:42 +00:00
Fix SFTP issue with multiple SFTP messages can arrive in one SSH packet
Fix HANDLE close event to notify about operation completion
This commit is contained in:
@@ -7,6 +7,9 @@ using Renci.SshClient.Messages.Sftp;
|
||||
|
||||
namespace Renci.SshClient.Sftp
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for all SFTP Commands
|
||||
/// </summary>
|
||||
internal abstract class SftpCommand
|
||||
{
|
||||
private SftpSession _sftpSession;
|
||||
@@ -17,6 +20,8 @@ namespace Renci.SshClient.Sftp
|
||||
|
||||
private Exception _error;
|
||||
|
||||
private bool _handleCloseMessageSent;
|
||||
|
||||
protected SftpAsyncResult AsyncResult { get; private set; }
|
||||
|
||||
public int CommandTimeout { get; set; }
|
||||
@@ -70,6 +75,12 @@ namespace Renci.SshClient.Sftp
|
||||
|
||||
protected virtual void OnStatus(StatusCodes statusCode, string errorMessage, string language)
|
||||
{
|
||||
if (this._handleCloseMessageSent)
|
||||
{
|
||||
this.OnHandleClosed();
|
||||
|
||||
this._handleCloseMessageSent = false;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnName(IEnumerable<SftpFile> files)
|
||||
@@ -107,6 +118,8 @@ namespace Renci.SshClient.Sftp
|
||||
{
|
||||
Handle = handle,
|
||||
});
|
||||
|
||||
this._handleCloseMessageSent = true;
|
||||
}
|
||||
|
||||
protected void SendReadMessage(string handle, ulong offset, uint bufferSize)
|
||||
@@ -262,19 +275,19 @@ namespace Renci.SshClient.Sftp
|
||||
if (this._requestId == e.Message.RequestId)
|
||||
{
|
||||
this.OnStatus(e.Message.StatusCode, e.Message.ErrorMessage, e.Message.Language);
|
||||
}
|
||||
|
||||
if (e.Message.StatusCode == StatusCodes.NoSuchFile ||
|
||||
e.Message.StatusCode == StatusCodes.PermissionDenied ||
|
||||
e.Message.StatusCode == StatusCodes.Failure ||
|
||||
e.Message.StatusCode == StatusCodes.BadMessage ||
|
||||
e.Message.StatusCode == StatusCodes.NoConnection ||
|
||||
e.Message.StatusCode == StatusCodes.ConnectionLost ||
|
||||
e.Message.StatusCode == StatusCodes.OperationUnsupported
|
||||
)
|
||||
{
|
||||
// Throw an exception if it was not handled by the command
|
||||
throw new SshException(e.Message.ErrorMessage);
|
||||
if (e.Message.StatusCode == StatusCodes.NoSuchFile ||
|
||||
e.Message.StatusCode == StatusCodes.PermissionDenied ||
|
||||
e.Message.StatusCode == StatusCodes.Failure ||
|
||||
e.Message.StatusCode == StatusCodes.BadMessage ||
|
||||
e.Message.StatusCode == StatusCodes.NoConnection ||
|
||||
e.Message.StatusCode == StatusCodes.ConnectionLost ||
|
||||
e.Message.StatusCode == StatusCodes.OperationUnsupported
|
||||
)
|
||||
{
|
||||
// Throw an exception if it was not handled by the command
|
||||
throw new SshException(e.Message.ErrorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@@ -89,15 +90,39 @@ namespace Renci.SshClient.Sftp
|
||||
|
||||
private void Channel_DataReceived(object sender, Common.ChannelDataEventArgs e)
|
||||
{
|
||||
var packets = new Queue<string>();
|
||||
|
||||
if (this._packetData == null)
|
||||
{
|
||||
var packetLength = (uint)(e.Data[0] << 24 | e.Data[1] << 16 | e.Data[2] << 8 | e.Data[3]);
|
||||
var dataOffset = 0;
|
||||
while (true)
|
||||
{
|
||||
// Read SFTP packet length
|
||||
var packetLength = (e.Data[dataOffset + 0] << 24 | e.Data[dataOffset + 1] << 16 | e.Data[dataOffset + 2] << 8 | e.Data[dataOffset + 3]);
|
||||
|
||||
this._packetData = new StringBuilder((int)packetLength, (int)packetLength);
|
||||
this._packetData.Append(e.Data.GetSshBytes().Skip(4).GetSshString());
|
||||
// Create data holder for SFTP packet
|
||||
this._packetData = new StringBuilder(packetLength, packetLength);
|
||||
|
||||
// Add data to the packet holder
|
||||
this._packetData.Append(e.Data.GetSshBytes().Skip(dataOffset + 4).Take((int)packetLength).GetSshString());
|
||||
|
||||
dataOffset += (packetLength + 4);
|
||||
|
||||
if (dataOffset < e.Data.Length)
|
||||
{
|
||||
// If there is another SFTP packet in current message then queue this data and read next one
|
||||
packets.Enqueue(this._packetData.ToString());
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add message data to packet data
|
||||
this._packetData.Append(e.Data);
|
||||
}
|
||||
|
||||
@@ -107,18 +132,25 @@ namespace Renci.SshClient.Sftp
|
||||
return;
|
||||
}
|
||||
|
||||
dynamic sftpMessage = SftpMessage.Load(this._packetData.ToString().GetSshBytes());
|
||||
// Add last packet to the queue of packet data that need to be proccessed
|
||||
packets.Enqueue(this._packetData.ToString());
|
||||
|
||||
this._packetData = null;
|
||||
|
||||
try
|
||||
foreach (var packetData in packets)
|
||||
{
|
||||
// TODO: Check to run on different thread
|
||||
this.HandleMessage(sftpMessage);
|
||||
}
|
||||
catch (Exception exp)
|
||||
{
|
||||
this.RaiseError(exp);
|
||||
dynamic sftpMessage = SftpMessage.Load(packetData.GetSshBytes());
|
||||
|
||||
try
|
||||
{
|
||||
// TODO: Check to run on different thread
|
||||
this.HandleMessage(sftpMessage);
|
||||
}
|
||||
catch (Exception exp)
|
||||
{
|
||||
this.RaiseError(exp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user