This is an automated email from the ASF dual-hosted git repository. lgoldstein pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/mina-sshd.git
The following commit(s) were added to refs/heads/master by this push: new 270caaa Propagate SCP file transfer ACK data to ScpTransferListener before validating it 270caaa is described below commit 270caaafdbfddfba05f0b8de2ed69b237b557f54 Author: Lyor Goldstein <lgoldst...@apache.org> AuthorDate: Fri Dec 4 17:34:17 2020 +0200 Propagate SCP file transfer ACK data to ScpTransferListener before validating it --- CHANGES.md | 1 + .../org/apache/sshd/cli/client/ScpCommandMain.java | 11 ++++++++++- .../java/org/apache/sshd/scp/common/ScpHelper.java | 22 +++++++++++++++++----- .../sshd/scp/common/ScpTransferEventListener.java | 19 +++++++++++++++++++ .../sshd/scp/client/AbstractScpTestSupport.java | 10 ++++++++++ 5 files changed, 57 insertions(+), 6 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 0f0d63c..12357d2 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -33,6 +33,7 @@ or `-key-file` command line option. * [SSHD-1079](https://issues.apache.org/jira/browse/SSHD-1079) Experimental async mode on the local port forwarder * [SSHD-1086](https://issues.apache.org/jira/browse/SSHD-1086) Added SFTP aware directory scanning helper classes * [SSHD-1089](https://issues.apache.org/jira/browse/SSHD-1089) Added wrappers for one-time single session usage of SFTP/SCP clients +* Propagate SCP file transfer ACK data to ScpTransferListener before validating it. ## Behavioral changes and enhancements diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/client/ScpCommandMain.java b/sshd-cli/src/main/java/org/apache/sshd/cli/client/ScpCommandMain.java index cfaf2d8..3990cd9 100644 --- a/sshd-cli/src/main/java/org/apache/sshd/cli/client/ScpCommandMain.java +++ b/sshd-cli/src/main/java/org/apache/sshd/cli/client/ScpCommandMain.java @@ -54,6 +54,7 @@ import org.apache.sshd.scp.client.ScpRemote2RemoteTransferHelper; import org.apache.sshd.scp.client.ScpRemote2RemoteTransferListener; import org.apache.sshd.scp.common.ScpLocation; import org.apache.sshd.scp.common.ScpTransferEventListener; +import org.apache.sshd.scp.common.helpers.ScpAckInfo; import org.apache.sshd.scp.common.helpers.ScpReceiveDirCommandDetails; import org.apache.sshd.scp.common.helpers.ScpReceiveFileCommandDetails; import org.apache.sshd.scp.common.helpers.ScpTimestampCommandDetails; @@ -241,7 +242,7 @@ public class ScpCommandMain extends SshClientCliSupport { /* -------------------------------------------------------------------------------- */ - @SuppressWarnings("checkstyle:ParameterNumber") + @SuppressWarnings({ "checkstyle:ParameterNumber", "checkstyle:anoninnerlength" }) public static void xferLocalToRemote( BufferedReader stdin, PrintStream stdout, PrintStream stderr, String[] args, ScpLocation source, ScpLocation target, Collection<Option> options, @@ -285,6 +286,14 @@ public class ScpCommandMain extends SshClientCliSupport { logEvent("endFileEvent", session, op, file, length, perms, thrown); } + @Override + public void handleFileEventAckInfo( + Session session, FileOperation op, Path file, long length, + Set<PosixFilePermission> perms, ScpAckInfo ackInfo) + throws IOException { + logEvent("ackInfo(" + ackInfo + ")", session, op, file, length, perms, null); + } + private void logEvent( String name, Session session, FileOperation op, Path file, long length, Collection<PosixFilePermission> perms, Throwable thrown) { diff --git a/sshd-scp/src/main/java/org/apache/sshd/scp/common/ScpHelper.java b/sshd-scp/src/main/java/org/apache/sshd/scp/common/ScpHelper.java index 5db850c..a22bdcb 100644 --- a/sshd-scp/src/main/java/org/apache/sshd/scp/common/ScpHelper.java +++ b/sshd-scp/src/main/java/org/apache/sshd/scp/common/ScpHelper.java @@ -354,12 +354,13 @@ public class ScpHelper extends AbstractLoggingBean implements SessionHolder<Sess Session session = getSession(); String name = details.getName(); Set<PosixFilePermission> perms = details.getPermissions(); + Path file; try (InputStream is = new LimitInputStream(this.in, length); OutputStream os = resolver.resolveTargetStream(session, name, length, perms, IoUtils.EMPTY_OPEN_OPTIONS)) { sendOk(); - Path file = resolver.getEventListenerFilePath(); + file = resolver.getEventListenerFilePath(); listener.startFileEvent(session, FileOperation.RECEIVE, file, length, perms); try { IoUtils.copy(is, os, bufSize); @@ -379,7 +380,7 @@ public class ScpHelper extends AbstractLoggingBean implements SessionHolder<Sess if (debugEnabled) { log.debug("receiveStream({})[{}] ACK={}", this, resolver, ackInfo); } - validateAckReplyCode("receiveStream", resolver, ackInfo); + validateFileOperationAckReplyCode(header, session, FileOperation.RECEIVE, file, length, perms, ackInfo); } public String readLine() throws IOException { @@ -564,8 +565,9 @@ public class ScpHelper extends AbstractLoggingBean implements SessionHolder<Sess validateAckReplyCode(cmd, resolver, ackInfo); Session session = getSession(); + Path path; try (InputStream in = resolver.resolveSourceStream(session, fileSize, perms, IoUtils.EMPTY_OPEN_OPTIONS)) { - Path path = resolver.getEventListenerFilePath(); + path = resolver.getEventListenerFilePath(); listener.startFileEvent(session, FileOperation.SEND, path, fileSize, perms); try { IoUtils.copy(in, out, bufSize); @@ -582,7 +584,8 @@ public class ScpHelper extends AbstractLoggingBean implements SessionHolder<Sess if (debugEnabled) { log.debug("sendStream({})[{}] command='{}' ACK={}", this, resolver, cmd, ackInfo); } - validateAckReplyCode("sendStream", resolver, ackInfo); + + validateFileOperationAckReplyCode(cmd, session, FileOperation.SEND, path, fileSize, perms, ackInfo); } protected void validateOperationReadyCode(String command, Object location, ScpAckInfo ackInfo) @@ -590,6 +593,14 @@ public class ScpHelper extends AbstractLoggingBean implements SessionHolder<Sess validateCommandStatusCode(command, location, ackInfo, false); } + protected void validateFileOperationAckReplyCode( + String command, Session session, FileOperation op, Path file, + long fileSize, Set<PosixFilePermission> perms, ScpAckInfo ackInfo) + throws IOException { + listener.handleFileEventAckInfo(session, op, file, fileSize, perms, ackInfo); + validateAckReplyCode(command, file, ackInfo); + } + protected void validateAckReplyCode(String command, Object location, ScpAckInfo ackInfo) throws IOException { validateCommandStatusCode(command, location, ackInfo, false); @@ -695,7 +706,8 @@ public class ScpHelper extends AbstractLoggingBean implements SessionHolder<Sess if (debugEnabled) { log.debug("sendDir({})[{}] 'E' command ACK={}", this, path, ackInfo); } - validateAckReplyCode(ScpDirEndCommandDetails.HEADER, path, ackInfo); + + validateAckReplyCode(cmd, path, ackInfo); } protected ScpAckInfo sendAcknowledgedCommand(String cmd) throws IOException { diff --git a/sshd-scp/src/main/java/org/apache/sshd/scp/common/ScpTransferEventListener.java b/sshd-scp/src/main/java/org/apache/sshd/scp/common/ScpTransferEventListener.java index dc685f4..2cf9996 100644 --- a/sshd-scp/src/main/java/org/apache/sshd/scp/common/ScpTransferEventListener.java +++ b/sshd-scp/src/main/java/org/apache/sshd/scp/common/ScpTransferEventListener.java @@ -26,6 +26,7 @@ import java.util.Set; import org.apache.sshd.common.session.Session; import org.apache.sshd.common.util.SshdEventListener; +import org.apache.sshd.scp.common.helpers.ScpAckInfo; /** * Can be registered in order to receive events about SCP transfers @@ -78,6 +79,24 @@ public interface ScpTransferEventListener extends SshdEventListener { } /** + * Called after {@link #endFileEvent(Session, FileOperation, Path, long, Set, Throwable)} if no exception was thrown + * and the peer's ACK was successfully read + * + * @param session The client/server {@link Session} through which the transfer is being executed + * @param op The {@link FileOperation} + * @param file The <U>local</U> referenced file {@link Path} + * @param length Size (in bytes) of transferred data + * @param perms A {@link Set} of {@link PosixFilePermission}s to be applied once transfer is complete + * @param ackInfo The {@link ScpAckInfo} received after a file transfer - <U>before</U> validating it + * @throws IOException If failed to handle the event + */ + default void handleFileEventAckInfo( + Session session, FileOperation op, Path file, long length, Set<PosixFilePermission> perms, ScpAckInfo ackInfo) + throws IOException { + // ignored + } + + /** * @param session The client/server {@link Session} through which the transfer is being executed * @param op The {@link FileOperation} * @param file The <U>local</U> referenced folder {@link Path} diff --git a/sshd-scp/src/test/java/org/apache/sshd/scp/client/AbstractScpTestSupport.java b/sshd-scp/src/test/java/org/apache/sshd/scp/client/AbstractScpTestSupport.java index 317e1f5..80bd14c 100644 --- a/sshd-scp/src/test/java/org/apache/sshd/scp/client/AbstractScpTestSupport.java +++ b/sshd-scp/src/test/java/org/apache/sshd/scp/client/AbstractScpTestSupport.java @@ -32,6 +32,7 @@ import org.apache.sshd.common.file.FileSystemFactory; import org.apache.sshd.common.file.virtualfs.VirtualFileSystemFactory; import org.apache.sshd.common.session.Session; import org.apache.sshd.scp.common.ScpTransferEventListener; +import org.apache.sshd.scp.common.helpers.ScpAckInfo; import org.apache.sshd.scp.server.ScpCommandFactory; import org.apache.sshd.server.SshServer; import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider; @@ -45,6 +46,7 @@ import org.junit.Before; * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> */ public abstract class AbstractScpTestSupport extends BaseTestSupport { + @SuppressWarnings("checkstyle:anoninnerlength") protected static final ScpTransferEventListener DEBUG_LISTENER = new ScpTransferEventListener() { @Override public void startFolderEvent( @@ -70,6 +72,14 @@ public abstract class AbstractScpTestSupport extends BaseTestSupport { logEvent("endFileEvent", s, op, file, true, length, perms, thrown); } + @Override + public void handleFileEventAckInfo( + Session session, FileOperation op, Path file, long length, + Set<PosixFilePermission> perms, ScpAckInfo ackInfo) + throws IOException { + logEvent("ackInfo(" + ackInfo + ")", session, op, file, true, length, perms, null); + } + private void logEvent( String type, Session s, FileOperation op, Path path, boolean isFile, long length, Collection<PosixFilePermission> perms, Throwable t) {