Skip to content

SftpFileReader returns zero bytes when downloading on Cisco devices #592

Closed
@sterlind

Description

@sterlind

Hiya!

Affected devices: Cisco IOS-XR 6.3.3, probably other Ciscos as well.
Symptoms: SftpClient.DownloadFile() writes zero bytes to the output stream.
Root cause:

Cisco IOS-XR has its own SFTP server, rife with bugs. One such bug is that if SSH2_FXP_OPEN is followed by a call to SSH2_FXP_LSTAT, it internally closes the file descriptor and sends EOF to any future SSH2_FXP_READ requests which had handles open to it.

How SSH.NET is affected:
SSH.NET requests LSTAT after OPEN, but before READ. This code in ServiceFactory.cs exposes the bug:

        public ISftpFileReader CreateSftpFileReader(string fileName, ISftpSession sftpSession, uint bufferSize)
        {
            const int defaultMaxPendingReads = 3;

            // Issue #292: Avoid overlapping SSH_FXP_OPEN and SSH_FXP_LSTAT requests for the same file as this
            // causes a performance degradation on Sun SSH
            var openAsyncResult = sftpSession.BeginOpen(fileName, Flags.Read, null, null);
            var handle = sftpSession.EndOpen(openAsyncResult);

            var statAsyncResult = sftpSession.BeginLStat(fileName, null, null);

            long? fileSize;
            int maxPendingReads;

            var chunkSize = sftpSession.CalculateOptimalReadLength(bufferSize);

            // fallback to a default maximum of pending reads when remote server does not allow us to obtain
            // the attributes of the file
            try
            {
                var fileAttributes = sftpSession.EndLStat(statAsyncResult);
                fileSize = fileAttributes.Size;
                maxPendingReads = Math.Min(10, (int) Math.Ceiling((double) fileAttributes.Size / chunkSize) + 1);
            }
            catch (SshException ex)
            {
                fileSize = null;
                maxPendingReads = defaultMaxPendingReads;

                DiagnosticAbstraction.Log(string.Format("Failed to obtain size of file. Allowing maximum {0} pending reads: {1}", maxPendingReads, ex));
            }

            return sftpSession.CreateFileReader(handle, sftpSession, chunkSize, maxPendingReads, fileSize);
        }

Moving BeginLStat/EndLStat before BeginOpen/EndOpen fixes the bug (this is also the call sequence that OpenSSH uses.) This shouldn't affect #292 , since it's still not overlapped.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions