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 da29581 [SSHD-1202] Provide SftpErrorDataHandler callback support for SFTP client da29581 is described below commit da2958172281ef20e51681afe036bd42c9cf843a Author: Lyor Goldstein <lgoldst...@apache.org> AuthorDate: Fri Aug 6 08:10:52 2021 +0300 [SSHD-1202] Provide SftpErrorDataHandler callback support for SFTP client --- CHANGES.md | 1 + docs/sftp.md | 22 ++++ .../main/java/org/apache/sshd/cli/CliLogger.java | 2 +- .../org/apache/sshd/cli/client/ScpCommandMain.java | 2 +- .../apache/sshd/cli/client/SftpCommandMain.java | 37 ++++++- .../sshd/cli/client/SshClientCliSupport.java | 2 +- .../org/apache/sshd/cli/client/SshClientMain.java | 4 +- .../org/apache/sshd/cli/client/SshKeyScanMain.java | 2 +- .../apache/sshd/cli/client/ChannelExecMain.java | 2 +- .../sshd/client/config/hosts/HostConfigEntry.java | 6 +- .../sshd/client/config/hosts/KnownHostEntry.java | 4 +- .../common/config/ConfigFileReaderSupport.java | 4 +- .../common/config/keys/AuthorizedKeyEntry.java | 4 +- .../common/config/keys/PrivateKeyEntryDecoder.java | 2 +- .../openssh/OpenSSHDSSPrivateKeyEntryDecoder.java | 2 +- .../OpenSSHECDSAPrivateKeyEntryDecoder.java | 2 +- .../openssh/OpenSSHRSAPrivateKeyDecoder.java | 2 +- .../loader/pem/DSSPEMResourceKeyPairParser.java | 2 +- .../loader/pem/ECDSAPEMResourceKeyPairParser.java | 2 +- .../loader/pem/RSAPEMResourceKeyPairParser.java | 2 +- .../openssh/OpenSSHKeyPairResourceWriter.java | 2 +- .../sshd/common/util/buffer/BufferUtils.java | 35 ++++++ .../{NoCloseReader.java => LineDataConsumer.java} | 34 +++--- .../io/{ => input}/CloseableEmptyInputStream.java | 2 +- .../util/io/{ => input}/EmptyInputStream.java | 2 +- .../io/{ => input}/InputStreamWithChannel.java | 2 +- .../util/io/{ => input}/LimitInputStream.java | 2 +- .../util/io/{ => input}/NoCloseInputStream.java | 2 +- .../common/util/io/{ => input}/NoCloseReader.java | 2 +- .../util/io/{ => input}/NullInputStream.java | 2 +- .../common/util/io/output/LineLevelAppender.java | 118 +++++++++++++++++++++ .../util/io/output/LineLevelAppenderStream.java | 99 +++++++++++++++++ .../common/util/io/output}/LineOutputStream.java | 2 +- .../io/{ => output}/LoggingFilterOutputStream.java | 2 +- .../util/io/{ => output}/NoCloseOutputStream.java | 2 +- .../common/util/io/{ => output}/NoCloseWriter.java | 2 +- .../util/io/{ => output}/NullOutputStream.java | 2 +- .../util/io/{ => output}/NullPrintStream.java | 2 +- .../io/{ => output}/OutputStreamWithChannel.java | 2 +- .../{ => output}/SecureByteArrayOutputStream.java | 2 +- .../eddsa/Ed25519PEMResourceKeyParser.java | 2 +- .../OpenSSHEd25519PrivateKeyEntryDecoder.java | 2 +- .../openssh/OpenSSHKeyPairResourceWriterTest.java | 2 +- .../util/io/{ => input}/EmptyInputStreamTest.java | 2 +- .../util/io/{ => input}/LimitInputStreamTest.java | 2 +- .../io/{ => input}/NoCloseInputStreamTest.java | 2 +- .../util/io/{ => input}/NoCloseReaderTest.java | 2 +- .../util/io/{ => input}/NullInputStreamTest.java | 2 +- .../util/io/output}/LineOutputStreamTest.java | 2 +- .../io/{ => output}/NoCloseOutputStreamTest.java | 2 +- .../util/io/{ => output}/NoCloseWriterTest.java | 2 +- .../util/io/{ => output}/NullOutputStreamTest.java | 2 +- .../EndlessTarpitSenderSupportDevelopment.java | 2 +- .../apache/sshd/client/session/ClientSession.java | 4 +- .../apache/sshd/server/channel/ChannelSession.java | 2 +- .../sshd/server/channel/PipeDataReceiver.java | 2 +- .../java/org/apache/sshd/KeyReExchangeTest.java | 2 +- .../java/org/apache/sshd/WindowAdjustTest.java | 2 +- .../java/org/apache/sshd/client/ClientTest.java | 2 +- .../config/keys/AuthorizedKeysTestSupport.java | 4 +- .../scp/client/ScpRemote2RemoteTransferHelper.java | 2 +- .../java/org/apache/sshd/scp/common/ScpHelper.java | 2 +- .../apache/sshd/sftp/client/SftpClientFactory.java | 54 ++++++++-- .../sshd/sftp/client/SftpErrorDataHandler.java | 36 +++---- .../apache/sshd/sftp/client/fs/SftpFileSystem.java | 21 +++- .../fs/SftpFileSystemClientSessionInitializer.java | 20 ++-- .../sftp/client/fs/SftpFileSystemProvider.java | 44 ++++++-- .../sshd/sftp/client/impl/AbstractSftpClient.java | 21 +++- .../sshd/sftp/client/impl/DefaultSftpClient.java | 45 +++++--- .../sftp/client/impl/DefaultSftpClientFactory.java | 17 +-- .../sftp/client/impl/SftpInputStreamAsync.java | 2 +- .../sftp/client/impl/SftpOutputStreamAsync.java | 2 +- .../sftp/client/SftpInputStreamWithChannel.java | 2 +- .../sftp/client/SftpOutputStreamWithChannel.java | 2 +- 74 files changed, 582 insertions(+), 156 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 531127d..9c4838c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -33,3 +33,4 @@ * [SSHD-1168](https://issues.apache.org/jira/browse/SSHD-1168) OpenSSH certificates: check certificate type * [SSHD-1171](https://issues.apache.org/jira/browse/SSHD-1171) OpenSSHCertificatesTest: certificates expire in 2030 * [SSHD-1172](https://issues.apache.org/jira/browse/SSHD-1172) Expiration of OpenSshCertificates needs to compare timestamps as unsigned long +* [SSHD-1202](https://issues.apache.org/jira/browse/SSHD-1202) Provide SftpErrorDataHandler callback support for SFTP client. diff --git a/docs/sftp.md b/docs/sftp.md index 4d60f86..99a2625 100644 --- a/docs/sftp.md +++ b/docs/sftp.md @@ -228,6 +228,28 @@ configuration key. The same can be achieved for the CLI SSHD code by specifying For more advanced restrictions one needs to sub-class `SftpSubSystem` and provide a non-default `SftpSubsystemFactory` that uses the sub-classed code. +### Intercepting data sent via STDERR channel data from the server + +According to [SFTP version 4 - section 3.1](https://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-3.1) the server MAY send error data through the STDERR pipeline. +By default, the code ignores such data - however, users may register a `SftpErrorDataHandler` that will be invoked whenever such data is received from the server. + +```java +ClientSession session = ...establish a session... +SftpClientFactory factory = ...obtain a factory instance... + +try (SftpClient client = factory.createSftpClient(session, new MySftpErrorDataHandler())) { + ... +} +``` + +The same applies to the `SftpFileSystem` - users may provide a custom error data handler that will be invoked whenever such data is received from the server. + +**Note:** + +* Error data handling must be **short** or it will cause the SSH session to hang - any long/blocking processing must be done in a separate thread. +* The provided data buffer contents must be **copied** if they need to be used after the callback returns as the buffer contents might be re-used by the caller code. +* Any exception thrown during handling of the data will cause the SFTP session to terminate. + ### Using `SftpFileSystemProvider` to create an `SftpFileSystem` The code automatically registers the `SftpFileSystemProvider` as the handler for `sftp://` URL(s). Such URLs are diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/CliLogger.java b/sshd-cli/src/main/java/org/apache/sshd/cli/CliLogger.java index 04c2019..9b0c064 100644 --- a/sshd-cli/src/main/java/org/apache/sshd/cli/CliLogger.java +++ b/sshd-cli/src/main/java/org/apache/sshd/cli/CliLogger.java @@ -32,7 +32,7 @@ import org.apache.sshd.common.PropertyResolverUtils; import org.apache.sshd.common.config.ConfigFileReaderSupport; import org.apache.sshd.common.config.LogLevelValue; import org.apache.sshd.common.util.GenericUtils; -import org.apache.sshd.common.util.io.NullPrintStream; +import org.apache.sshd.common.util.io.output.NullPrintStream; import org.apache.sshd.common.util.logging.SimplifiedLog; import org.apache.sshd.common.util.logging.SimplifiedLoggerSkeleton; import org.slf4j.Logger; 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 c72f741..b089386 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 @@ -46,7 +46,7 @@ import org.apache.sshd.common.SshConstants; import org.apache.sshd.common.session.Session; import org.apache.sshd.common.util.GenericUtils; import org.apache.sshd.common.util.ReflectionUtils; -import org.apache.sshd.common.util.io.NoCloseInputStream; +import org.apache.sshd.common.util.io.input.NoCloseInputStream; import org.apache.sshd.common.util.threads.ThreadUtils; import org.apache.sshd.scp.client.ScpClient; import org.apache.sshd.scp.client.ScpClient.Option; diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SftpCommandMain.java b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SftpCommandMain.java index f81d4b2..feb5d98 100644 --- a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SftpCommandMain.java +++ b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SftpCommandMain.java @@ -29,6 +29,7 @@ import java.io.PrintStream; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.nio.channels.Channel; +import java.nio.charset.StandardCharsets; import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; @@ -66,7 +67,10 @@ import org.apache.sshd.common.util.ReflectionUtils; import org.apache.sshd.common.util.ValidateUtils; import org.apache.sshd.common.util.buffer.BufferUtils; import org.apache.sshd.common.util.io.IoUtils; -import org.apache.sshd.common.util.io.NoCloseInputStream; +import org.apache.sshd.common.util.io.input.NoCloseInputStream; +import org.apache.sshd.common.util.io.output.LineLevelAppender; +import org.apache.sshd.common.util.io.output.LineLevelAppenderStream; +import org.apache.sshd.common.util.io.output.NullOutputStream; import org.apache.sshd.common.util.threads.ThreadUtils; import org.apache.sshd.server.config.SshServerConfigFileReader; import org.apache.sshd.sftp.client.SftpClient; @@ -317,6 +321,33 @@ public class SftpCommandMain extends SshClientCliSupport implements SftpClientHo return SftpVersionSelector.resolveVersionSelector(selector); } + private static OutputStream resolveErrorDataHandlerStream(ClientSession session, Logger logger) { + String errCharset = session.getString("SftpErrorHandlerOutputCharset"); + if (GenericUtils.safeCompare(errCharset, "NONE", false) == 0) { + return new NullOutputStream(); + } + + LineLevelAppender appender = new LineLevelAppender() { + @Override + public boolean isWriteEnabled() { + return logger.isErrorEnabled(); + } + + @Override + public void writeLineData(CharSequence lineData) throws IOException { + logger.error("errorChannel: {}", lineData); + } + + @Override + public void close() throws IOException { + // ignored + } + }; + return GenericUtils.isBlank(errCharset) + ? new LineLevelAppenderStream(StandardCharsets.US_ASCII, appender) + : new LineLevelAppenderStream(errCharset, appender); + } + /* -------------------------------------------------------------------- */ public static void main(String[] args) throws Exception { @@ -356,7 +387,9 @@ public class SftpCommandMain extends SshClientCliSupport implements SftpClientHo logger.info("Using version selector={}", versionSelector); } - try (SftpClient sftpClient = clientFactory.createSftpClient(session, versionSelector); + try (OutputStream errStream = resolveErrorDataHandlerStream(session, logger); + SftpClient sftpClient = clientFactory.createSftpClient( + session, versionSelector, errStream::write); SftpCommandMain sftp = new SftpCommandMain(sftpClient)) { // TODO allow injection of extra CommandExecutor(s) via command line and/or service loading sftp.doInteractive(stdin, stdout, stderr); diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientCliSupport.java b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientCliSupport.java index 4af2024..82f87d8 100644 --- a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientCliSupport.java +++ b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientCliSupport.java @@ -82,7 +82,7 @@ import org.apache.sshd.common.util.MapEntryUtils; import org.apache.sshd.common.util.OsUtils; import org.apache.sshd.common.util.ReflectionUtils; import org.apache.sshd.common.util.ValidateUtils; -import org.apache.sshd.common.util.io.NoCloseOutputStream; +import org.apache.sshd.common.util.io.output.NoCloseOutputStream; import org.apache.sshd.common.util.net.SshdSocketAddress; import org.apache.sshd.common.util.threads.ThreadUtils; diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientMain.java b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientMain.java index 20f86fa..aa9e438 100644 --- a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientMain.java +++ b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientMain.java @@ -40,8 +40,8 @@ import org.apache.sshd.client.session.ClientSession; import org.apache.sshd.common.channel.Channel; import org.apache.sshd.common.channel.PtyChannelConfiguration; import org.apache.sshd.common.util.GenericUtils; -import org.apache.sshd.common.util.io.NoCloseInputStream; -import org.apache.sshd.common.util.io.NoCloseOutputStream; +import org.apache.sshd.common.util.io.input.NoCloseInputStream; +import org.apache.sshd.common.util.io.output.NoCloseOutputStream; import org.apache.sshd.common.util.net.SshdSocketAddress; import org.slf4j.Logger; diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshKeyScanMain.java b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshKeyScanMain.java index 0686938..e3b1205 100644 --- a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshKeyScanMain.java +++ b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshKeyScanMain.java @@ -82,7 +82,7 @@ import org.apache.sshd.common.util.GenericUtils; import org.apache.sshd.common.util.MapEntryUtils; import org.apache.sshd.common.util.ValidateUtils; import org.apache.sshd.common.util.io.IoUtils; -import org.apache.sshd.common.util.io.NoCloseInputStream; +import org.apache.sshd.common.util.io.input.NoCloseInputStream; import org.apache.sshd.common.util.logging.SimplifiedLog; import org.apache.sshd.common.util.net.SshdSocketAddress; import org.apache.sshd.common.util.security.SecurityUtils; diff --git a/sshd-cli/src/test/java/org/apache/sshd/cli/client/ChannelExecMain.java b/sshd-cli/src/test/java/org/apache/sshd/cli/client/ChannelExecMain.java index e1cfc31..ec09974 100644 --- a/sshd-cli/src/test/java/org/apache/sshd/cli/client/ChannelExecMain.java +++ b/sshd-cli/src/test/java/org/apache/sshd/cli/client/ChannelExecMain.java @@ -28,7 +28,7 @@ import org.apache.sshd.cli.CliLogger; import org.apache.sshd.client.SshClient; import org.apache.sshd.client.session.ClientSession; import org.apache.sshd.common.util.GenericUtils; -import org.apache.sshd.common.util.io.NoCloseInputStream; +import org.apache.sshd.common.util.io.input.NoCloseInputStream; import org.apache.sshd.util.test.BaseTestSupport; /** diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntry.java b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntry.java index ad8a604..88f8d93 100644 --- a/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntry.java +++ b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntry.java @@ -56,9 +56,9 @@ import org.apache.sshd.common.util.MapEntryUtils.NavigableMapBuilder; import org.apache.sshd.common.util.OsUtils; import org.apache.sshd.common.util.ValidateUtils; import org.apache.sshd.common.util.io.IoUtils; -import org.apache.sshd.common.util.io.NoCloseInputStream; -import org.apache.sshd.common.util.io.NoCloseOutputStream; -import org.apache.sshd.common.util.io.NoCloseReader; +import org.apache.sshd.common.util.io.input.NoCloseInputStream; +import org.apache.sshd.common.util.io.input.NoCloseReader; +import org.apache.sshd.common.util.io.output.NoCloseOutputStream; /** * Represents an entry in the client's configuration file as defined by the diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/KnownHostEntry.java b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/KnownHostEntry.java index 077c00d..03427c6 100644 --- a/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/KnownHostEntry.java +++ b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/KnownHostEntry.java @@ -39,8 +39,8 @@ import org.apache.sshd.common.config.keys.AuthorizedKeyEntry; import org.apache.sshd.common.config.keys.PublicKeyEntry; import org.apache.sshd.common.util.GenericUtils; import org.apache.sshd.common.util.ValidateUtils; -import org.apache.sshd.common.util.io.NoCloseInputStream; -import org.apache.sshd.common.util.io.NoCloseReader; +import org.apache.sshd.common.util.io.input.NoCloseInputStream; +import org.apache.sshd.common.util.io.input.NoCloseReader; /** * Contains a representation of an entry in the <code>known_hosts</code> file diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/ConfigFileReaderSupport.java b/sshd-common/src/main/java/org/apache/sshd/common/config/ConfigFileReaderSupport.java index 9f9ebdf..8a5b248 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/config/ConfigFileReaderSupport.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/config/ConfigFileReaderSupport.java @@ -35,8 +35,8 @@ import java.util.concurrent.TimeUnit; import org.apache.sshd.common.PropertyResolverUtils; import org.apache.sshd.common.util.GenericUtils; -import org.apache.sshd.common.util.io.NoCloseInputStream; -import org.apache.sshd.common.util.io.NoCloseReader; +import org.apache.sshd.common.util.io.input.NoCloseInputStream; +import org.apache.sshd.common.util.io.input.NoCloseReader; import org.apache.sshd.common.util.net.SshdSocketAddress; /** diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntry.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntry.java index 2628136..096fd0c 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntry.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntry.java @@ -45,8 +45,8 @@ import org.apache.sshd.common.session.SessionContext; import org.apache.sshd.common.util.GenericUtils; import org.apache.sshd.common.util.MapEntryUtils; import org.apache.sshd.common.util.ValidateUtils; -import org.apache.sshd.common.util.io.NoCloseInputStream; -import org.apache.sshd.common.util.io.NoCloseReader; +import org.apache.sshd.common.util.io.input.NoCloseInputStream; +import org.apache.sshd.common.util.io.input.NoCloseReader; /** * Represents an entry in the user's {@code authorized_keys} file according to the diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryDecoder.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryDecoder.java index 0e61acc..c662720 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryDecoder.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryDecoder.java @@ -35,7 +35,7 @@ import org.apache.sshd.common.session.SessionContext; import org.apache.sshd.common.util.GenericUtils; import org.apache.sshd.common.util.NumberUtils; import org.apache.sshd.common.util.ValidateUtils; -import org.apache.sshd.common.util.io.SecureByteArrayOutputStream; +import org.apache.sshd.common.util.io.output.SecureByteArrayOutputStream; /** * @param <PUB> Type of {@link PublicKey} diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHDSSPrivateKeyEntryDecoder.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHDSSPrivateKeyEntryDecoder.java index b543ea9..074a522 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHDSSPrivateKeyEntryDecoder.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHDSSPrivateKeyEntryDecoder.java @@ -41,7 +41,7 @@ import org.apache.sshd.common.config.keys.KeyUtils; import org.apache.sshd.common.config.keys.impl.AbstractPrivateKeyEntryDecoder; import org.apache.sshd.common.keyprovider.KeyPairProvider; import org.apache.sshd.common.session.SessionContext; -import org.apache.sshd.common.util.io.SecureByteArrayOutputStream; +import org.apache.sshd.common.util.io.output.SecureByteArrayOutputStream; import org.apache.sshd.common.util.security.SecurityUtils; /** diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHECDSAPrivateKeyEntryDecoder.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHECDSAPrivateKeyEntryDecoder.java index ee33129..3535fe7 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHECDSAPrivateKeyEntryDecoder.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHECDSAPrivateKeyEntryDecoder.java @@ -43,7 +43,7 @@ import org.apache.sshd.common.config.keys.KeyUtils; import org.apache.sshd.common.config.keys.impl.AbstractPrivateKeyEntryDecoder; import org.apache.sshd.common.config.keys.impl.ECDSAPublicKeyEntryDecoder; import org.apache.sshd.common.session.SessionContext; -import org.apache.sshd.common.util.io.SecureByteArrayOutputStream; +import org.apache.sshd.common.util.io.output.SecureByteArrayOutputStream; import org.apache.sshd.common.util.security.SecurityUtils; /** diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHRSAPrivateKeyDecoder.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHRSAPrivateKeyDecoder.java index 096a413..1df1d52 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHRSAPrivateKeyDecoder.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHRSAPrivateKeyDecoder.java @@ -41,7 +41,7 @@ import org.apache.sshd.common.config.keys.KeyUtils; import org.apache.sshd.common.config.keys.impl.AbstractPrivateKeyEntryDecoder; import org.apache.sshd.common.keyprovider.KeyPairProvider; import org.apache.sshd.common.session.SessionContext; -import org.apache.sshd.common.util.io.SecureByteArrayOutputStream; +import org.apache.sshd.common.util.io.output.SecureByteArrayOutputStream; import org.apache.sshd.common.util.security.SecurityUtils; /** diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/DSSPEMResourceKeyPairParser.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/DSSPEMResourceKeyPairParser.java index 796bf60..0a7aa05 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/DSSPEMResourceKeyPairParser.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/DSSPEMResourceKeyPairParser.java @@ -39,10 +39,10 @@ import org.apache.sshd.common.NamedResource; import org.apache.sshd.common.config.keys.FilePasswordProvider; import org.apache.sshd.common.config.keys.KeyUtils; import org.apache.sshd.common.session.SessionContext; -import org.apache.sshd.common.util.io.NoCloseInputStream; import org.apache.sshd.common.util.io.der.ASN1Object; import org.apache.sshd.common.util.io.der.ASN1Type; import org.apache.sshd.common.util.io.der.DERParser; +import org.apache.sshd.common.util.io.input.NoCloseInputStream; import org.apache.sshd.common.util.security.SecurityUtils; /** diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/ECDSAPEMResourceKeyPairParser.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/ECDSAPEMResourceKeyPairParser.java index 08fe839..9ed808d 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/ECDSAPEMResourceKeyPairParser.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/ECDSAPEMResourceKeyPairParser.java @@ -43,10 +43,10 @@ import org.apache.sshd.common.cipher.ECCurves; import org.apache.sshd.common.config.keys.FilePasswordProvider; import org.apache.sshd.common.config.keys.KeyUtils; import org.apache.sshd.common.session.SessionContext; -import org.apache.sshd.common.util.io.NoCloseInputStream; import org.apache.sshd.common.util.io.der.ASN1Object; import org.apache.sshd.common.util.io.der.ASN1Type; import org.apache.sshd.common.util.io.der.DERParser; +import org.apache.sshd.common.util.io.input.NoCloseInputStream; import org.apache.sshd.common.util.security.SecurityUtils; /** diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/RSAPEMResourceKeyPairParser.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/RSAPEMResourceKeyPairParser.java index 161a0ac..40f8ed6 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/RSAPEMResourceKeyPairParser.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/RSAPEMResourceKeyPairParser.java @@ -40,10 +40,10 @@ import org.apache.sshd.common.NamedResource; import org.apache.sshd.common.config.keys.FilePasswordProvider; import org.apache.sshd.common.config.keys.KeyUtils; import org.apache.sshd.common.session.SessionContext; -import org.apache.sshd.common.util.io.NoCloseInputStream; import org.apache.sshd.common.util.io.der.ASN1Object; import org.apache.sshd.common.util.io.der.ASN1Type; import org.apache.sshd.common.util.io.der.DERParser; +import org.apache.sshd.common.util.io.input.NoCloseInputStream; import org.apache.sshd.common.util.security.SecurityUtils; /** diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/writer/openssh/OpenSSHKeyPairResourceWriter.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/writer/openssh/OpenSSHKeyPairResourceWriter.java index dd91e85..f9402c4 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/writer/openssh/OpenSSHKeyPairResourceWriter.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/writer/openssh/OpenSSHKeyPairResourceWriter.java @@ -51,7 +51,7 @@ import org.apache.sshd.common.config.keys.loader.openssh.kdf.BCrypt; import org.apache.sshd.common.config.keys.loader.openssh.kdf.BCryptKdfOptions; import org.apache.sshd.common.config.keys.writer.KeyPairResourceWriter; import org.apache.sshd.common.util.GenericUtils; -import org.apache.sshd.common.util.io.SecureByteArrayOutputStream; +import org.apache.sshd.common.util.io.output.SecureByteArrayOutputStream; /** * A {@link KeyPairResourceWriter} for writing keys in the modern OpenSSH format, using the OpenBSD bcrypt KDF for diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java index 8c935d1..5fce736 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java @@ -65,6 +65,41 @@ public final class BufferUtils { throw new UnsupportedOperationException("No instance allowed"); } + /** + * <p> + * Finds the index of the given value in the array starting at the given index and checking up to specified number + * of elements. + * </p> + * + * <p> + * This method returns {@code -1}) for a {@code null} input array. + * </p> + * + * <p> + * A negative startIndex is treated as zero. A startIndex larger than the array length will return {@code -1}. + * </p> + * + * @param array the array to search through for the object, may be {@code null} + * @param valueToFind the value to find + * @param startIndex the index to start searching at + * @param len the number of elements to search from the start index + * @return the index of the value within the array, {@code -1} if not found or {@code null} array input + * or non-positive number of elements + */ + public static int indexOf(byte[] array, byte valueToFind, int startIndex, int len) { + if (array == null) { + return -1; + } + + for (int i = Math.max(startIndex, 0), l = 0; l < len; i++, l++) { + if (valueToFind == array[i]) { + return i; + } + } + + return -1; + } + public static void dumpHex( SimplifiedLog logger, Level level, String prefix, PropertyResolver resolver, char sep, byte... data) { dumpHex(logger, level, prefix, resolver, sep, data, 0, NumberUtils.length(data)); diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseReader.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/LineDataConsumer.java similarity index 64% copy from sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseReader.java copy to sshd-common/src/main/java/org/apache/sshd/common/util/io/LineDataConsumer.java index 9c8b218..a58e690 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseReader.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/LineDataConsumer.java @@ -19,28 +19,28 @@ package org.apache.sshd.common.util.io; -import java.io.FilterReader; import java.io.IOException; -import java.io.Reader; +import java.io.StreamCorruptedException; +import java.util.Objects; /** * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> */ -public class NoCloseReader extends FilterReader { - public NoCloseReader(Reader in) { - super(in); - } +@FunctionalInterface +public interface LineDataConsumer { + /** + * Ignores anything provided to it + */ + LineDataConsumer IGNORE = lineData -> { + // do nothing + }; - @Override - public void close() throws IOException { - // ignored - } + /** + * Throws {@link StreamCorruptedException} with the invoked line data + */ + LineDataConsumer FAIL = lineData -> { + throw new StreamCorruptedException(Objects.toString(lineData)); + }; - public static Reader resolveReader(Reader r, boolean okToClose) { - if ((r == null) || okToClose) { - return r; - } else { - return new NoCloseReader(r); - } - } + void consume(CharSequence lineData) throws IOException; } diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/CloseableEmptyInputStream.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/input/CloseableEmptyInputStream.java similarity index 98% rename from sshd-common/src/main/java/org/apache/sshd/common/util/io/CloseableEmptyInputStream.java rename to sshd-common/src/main/java/org/apache/sshd/common/util/io/input/CloseableEmptyInputStream.java index c3c2209..5eb6aba 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/util/io/CloseableEmptyInputStream.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/input/CloseableEmptyInputStream.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.sshd.common.util.io; +package org.apache.sshd.common.util.io.input; import java.io.IOException; import java.nio.channels.Channel; diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/EmptyInputStream.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/input/EmptyInputStream.java similarity index 97% rename from sshd-common/src/main/java/org/apache/sshd/common/util/io/EmptyInputStream.java rename to sshd-common/src/main/java/org/apache/sshd/common/util/io/input/EmptyInputStream.java index 4282adb..5674326 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/util/io/EmptyInputStream.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/input/EmptyInputStream.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.sshd.common.util.io; +package org.apache.sshd.common.util.io.input; import java.io.IOException; import java.io.InputStream; diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/InputStreamWithChannel.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/input/InputStreamWithChannel.java similarity index 96% rename from sshd-common/src/main/java/org/apache/sshd/common/util/io/InputStreamWithChannel.java rename to sshd-common/src/main/java/org/apache/sshd/common/util/io/input/InputStreamWithChannel.java index d847079..5cd1a5d 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/util/io/InputStreamWithChannel.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/input/InputStreamWithChannel.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.sshd.common.util.io; +package org.apache.sshd.common.util.io.input; import java.io.InputStream; import java.nio.channels.Channel; diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/LimitInputStream.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/input/LimitInputStream.java similarity index 98% rename from sshd-common/src/main/java/org/apache/sshd/common/util/io/LimitInputStream.java rename to sshd-common/src/main/java/org/apache/sshd/common/util/io/input/LimitInputStream.java index 1d5b33b..3cd9956 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/util/io/LimitInputStream.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/input/LimitInputStream.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.sshd.common.util.io; +package org.apache.sshd.common.util.io.input; import java.io.FilterInputStream; import java.io.IOException; diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseInputStream.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/input/NoCloseInputStream.java similarity index 96% rename from sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseInputStream.java rename to sshd-common/src/main/java/org/apache/sshd/common/util/io/input/NoCloseInputStream.java index b9aedee..0cf9b04 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseInputStream.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/input/NoCloseInputStream.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.sshd.common.util.io; +package org.apache.sshd.common.util.io.input; import java.io.FilterInputStream; import java.io.IOException; diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseReader.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/input/NoCloseReader.java similarity index 96% rename from sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseReader.java rename to sshd-common/src/main/java/org/apache/sshd/common/util/io/input/NoCloseReader.java index 9c8b218..a85e6d4 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseReader.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/input/NoCloseReader.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.sshd.common.util.io; +package org.apache.sshd.common.util.io.input; import java.io.FilterReader; import java.io.IOException; diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NullInputStream.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/input/NullInputStream.java similarity index 98% rename from sshd-common/src/main/java/org/apache/sshd/common/util/io/NullInputStream.java rename to sshd-common/src/main/java/org/apache/sshd/common/util/io/input/NullInputStream.java index 1b9b471..fc949ec 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NullInputStream.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/input/NullInputStream.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.sshd.common.util.io; +package org.apache.sshd.common.util.io.input; import java.io.EOFException; import java.io.IOException; diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/LineLevelAppender.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/LineLevelAppender.java new file mode 100644 index 0000000..df8c7e7 --- /dev/null +++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/LineLevelAppender.java @@ -0,0 +1,118 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.sshd.common.util.io.output; + +import java.io.Closeable; +import java.io.IOException; +import java.util.Objects; +import java.util.function.BooleanSupplier; + +import org.apache.sshd.common.util.io.LineDataConsumer; + +/** + * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> + */ +public interface LineLevelAppender extends LineDataConsumer, Closeable { + /** + * A typical line length used in many textual standards + */ + int TYPICAL_LINE_LENGTH = 80; + + LineLevelAppender EMPTY = new LineLevelAppender() { + @Override + public void writeLineData(CharSequence lineData) throws IOException { + // ignored + } + + @Override + public boolean isWriteEnabled() { + return false; + } + + @Override + public void close() throws IOException { + // do nothing + } + + @Override + public String toString() { + return "EMPTY"; + } + }; + + /** + * @return {@code true} if OK to accumulate data in work buffer + */ + boolean isWriteEnabled(); + + @Override + default void consume(CharSequence lineData) throws IOException { + writeLineData(lineData); + } + + /** + * Called by the implementation once end-of-line is detected. + * + * @param lineData The "pure" line data - excluding any CR/LF(s). + * @throws IOException If failed to write the data + */ + void writeLineData(CharSequence lineData) throws IOException; + + static LineLevelAppender wrap(Appendable appendable) { + return wrap(appendable, () -> true); + } + + static LineLevelAppender wrap(Appendable appendable, BooleanSupplier writeEnabled) { + Objects.requireNonNull(appendable, "No appendable to wrap"); + return new LineLevelAppender() { + /** + * indicates whether a line has been written + */ + private boolean writtenFirstLine; + + @Override + public void close() throws IOException { + if (appendable instanceof Closeable) { + ((Closeable) appendable).close(); + } + } + + @Override + public void writeLineData(CharSequence lineData) throws IOException { + if (writtenFirstLine) { + appendable.append(System.lineSeparator()); + } + + appendable.append(lineData); + writtenFirstLine = true; + } + + @Override + public boolean isWriteEnabled() { + return writeEnabled.getAsBoolean(); + } + + @Override + public String toString() { + return appendable.toString(); + } + }; + } +} diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/LineLevelAppenderStream.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/LineLevelAppenderStream.java new file mode 100644 index 0000000..0270cce --- /dev/null +++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/LineLevelAppenderStream.java @@ -0,0 +1,99 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.sshd.common.util.io.output; + +import java.io.IOException; +import java.io.StreamCorruptedException; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CoderResult; +import java.util.Objects; + +import org.apache.sshd.common.util.ValidateUtils; + +/** + * <P> + * Accumulates all written data into a work buffer and calls the actual writing method only when LF detected. + * <B>Note:</B> it strips CR if found before the LF + * </P> + * + * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> + */ +public class LineLevelAppenderStream extends LineOutputStream { + protected final CharsetDecoder csDecoder; + protected final LineLevelAppender appenderInstance; + protected char[] lineBuf; + + public LineLevelAppenderStream(LineLevelAppender appender) { + this(Charset.defaultCharset(), appender); + } + + public LineLevelAppenderStream(String charset, LineLevelAppender appender) { + this(Charset.forName(ValidateUtils.checkNotNullAndNotEmpty(charset, "No charset name")), appender); + } + + public LineLevelAppenderStream(Charset charset, LineLevelAppender appender) { + this(Objects.requireNonNull(charset, "No charset").newDecoder(), appender); + } + + public LineLevelAppenderStream(CharsetDecoder decoder, LineLevelAppender appender) { + csDecoder = Objects.requireNonNull(decoder, "No decoder"); + appenderInstance = Objects.requireNonNull(appender, "No appender"); + } + + public final LineLevelAppender getLineLevelAppender() { + return appenderInstance; + } + + @Override + protected void handleLine(byte[] b, int off, int len) throws IOException { + LineLevelAppender appender = getLineLevelAppender(); + if (len <= 0) { + appender.writeLineData(""); + return; + } + + ByteBuffer bb = (b[off + len - 1] == '\r') ? ByteBuffer.wrap(b, off, len - 1) : ByteBuffer.wrap(b, off, len); + char[] lineChars = ensureCharDataCapacity(len); + CharBuffer cc = CharBuffer.wrap(lineChars); + + csDecoder.reset(); + CoderResult res = csDecoder.decode(bb, cc, true); + if (res.isError() || res.isMalformed() || res.isOverflow() || res.isUnmappable()) { + throw new StreamCorruptedException("Failed to decode line bytes: " + res); + } + + cc.flip(); + appender.writeLineData(cc); + } + + protected char[] ensureCharDataCapacity(int numBytes) { + float grwFactor = csDecoder.maxCharsPerByte(); // worst case + int reqChars = (grwFactor > 0.0f) ? (int) (numBytes * grwFactor) : numBytes; + if ((lineBuf == null) || (lineBuf.length < reqChars)) { + reqChars = Math.max(reqChars, LineLevelAppender.TYPICAL_LINE_LENGTH); + lineBuf = new char[reqChars + Byte.SIZE /* a little extra to avoid numerous growths */]; + } + + return lineBuf; + } +} diff --git a/sshd-contrib/src/main/java/org/apache/sshd/contrib/common/util/io/LineOutputStream.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/LineOutputStream.java similarity index 98% rename from sshd-contrib/src/main/java/org/apache/sshd/contrib/common/util/io/LineOutputStream.java rename to sshd-common/src/main/java/org/apache/sshd/common/util/io/output/LineOutputStream.java index b5085b1..b093d1e 100644 --- a/sshd-contrib/src/main/java/org/apache/sshd/contrib/common/util/io/LineOutputStream.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/LineOutputStream.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.sshd.contrib.common.util.io; +package org.apache.sshd.common.util.io.output; import java.io.IOException; import java.io.OutputStream; diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/LoggingFilterOutputStream.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/LoggingFilterOutputStream.java similarity index 98% rename from sshd-common/src/main/java/org/apache/sshd/common/util/io/LoggingFilterOutputStream.java rename to sshd-common/src/main/java/org/apache/sshd/common/util/io/output/LoggingFilterOutputStream.java index b446de2..4511c88 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/util/io/LoggingFilterOutputStream.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/LoggingFilterOutputStream.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.sshd.common.util.io; +package org.apache.sshd.common.util.io.output; import java.io.FilterOutputStream; import java.io.IOException; diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseOutputStream.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/NoCloseOutputStream.java similarity index 96% copy from sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseOutputStream.java copy to sshd-common/src/main/java/org/apache/sshd/common/util/io/output/NoCloseOutputStream.java index 4ba16d3..ae57f3c 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseOutputStream.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/NoCloseOutputStream.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.sshd.common.util.io; +package org.apache.sshd.common.util.io.output; import java.io.FilterOutputStream; import java.io.IOException; diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseWriter.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/NoCloseWriter.java similarity index 96% rename from sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseWriter.java rename to sshd-common/src/main/java/org/apache/sshd/common/util/io/output/NoCloseWriter.java index 0f92697..a2378cf 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseWriter.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/NoCloseWriter.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.sshd.common.util.io; +package org.apache.sshd.common.util.io.output; import java.io.FilterWriter; import java.io.IOException; diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NullOutputStream.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/NullOutputStream.java similarity index 97% rename from sshd-common/src/main/java/org/apache/sshd/common/util/io/NullOutputStream.java rename to sshd-common/src/main/java/org/apache/sshd/common/util/io/output/NullOutputStream.java index 8a0435c..64ff73e 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NullOutputStream.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/NullOutputStream.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.sshd.common.util.io; +package org.apache.sshd.common.util.io.output; import java.io.EOFException; import java.io.IOException; diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NullPrintStream.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/NullPrintStream.java similarity index 98% rename from sshd-common/src/main/java/org/apache/sshd/common/util/io/NullPrintStream.java rename to sshd-common/src/main/java/org/apache/sshd/common/util/io/output/NullPrintStream.java index a21c5d2..7466a1f 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NullPrintStream.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/NullPrintStream.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.sshd.common.util.io; +package org.apache.sshd.common.util.io.output; import java.io.PrintStream; import java.util.Locale; diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/OutputStreamWithChannel.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/OutputStreamWithChannel.java similarity index 95% rename from sshd-common/src/main/java/org/apache/sshd/common/util/io/OutputStreamWithChannel.java rename to sshd-common/src/main/java/org/apache/sshd/common/util/io/output/OutputStreamWithChannel.java index 6f81872..23deb7c 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/util/io/OutputStreamWithChannel.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/OutputStreamWithChannel.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.sshd.common.util.io; +package org.apache.sshd.common.util.io.output; import java.io.OutputStream; import java.nio.channels.Channel; diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/SecureByteArrayOutputStream.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/SecureByteArrayOutputStream.java similarity index 97% rename from sshd-common/src/main/java/org/apache/sshd/common/util/io/SecureByteArrayOutputStream.java rename to sshd-common/src/main/java/org/apache/sshd/common/util/io/output/SecureByteArrayOutputStream.java index 3cd073f..a6193e9 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/util/io/SecureByteArrayOutputStream.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/output/SecureByteArrayOutputStream.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.sshd.common.util.io; +package org.apache.sshd.common.util.io.output; import java.io.ByteArrayOutputStream; import java.util.Arrays; diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/Ed25519PEMResourceKeyParser.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/Ed25519PEMResourceKeyParser.java index 054111e..83f1775 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/Ed25519PEMResourceKeyParser.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/Ed25519PEMResourceKeyParser.java @@ -43,10 +43,10 @@ import org.apache.sshd.common.config.keys.FilePasswordProvider; import org.apache.sshd.common.config.keys.loader.pem.AbstractPEMResourceKeyPairParser; import org.apache.sshd.common.session.SessionContext; import org.apache.sshd.common.util.GenericUtils; -import org.apache.sshd.common.util.io.NoCloseInputStream; import org.apache.sshd.common.util.io.der.ASN1Object; import org.apache.sshd.common.util.io.der.ASN1Type; import org.apache.sshd.common.util.io.der.DERParser; +import org.apache.sshd.common.util.io.input.NoCloseInputStream; import org.apache.sshd.common.util.security.SecurityUtils; /** diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/OpenSSHEd25519PrivateKeyEntryDecoder.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/OpenSSHEd25519PrivateKeyEntryDecoder.java index b4cd0f6..478f97d 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/OpenSSHEd25519PrivateKeyEntryDecoder.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/OpenSSHEd25519PrivateKeyEntryDecoder.java @@ -43,7 +43,7 @@ import org.apache.sshd.common.config.keys.impl.AbstractPrivateKeyEntryDecoder; import org.apache.sshd.common.keyprovider.KeyPairProvider; import org.apache.sshd.common.session.SessionContext; import org.apache.sshd.common.util.GenericUtils; -import org.apache.sshd.common.util.io.SecureByteArrayOutputStream; +import org.apache.sshd.common.util.io.output.SecureByteArrayOutputStream; import org.apache.sshd.common.util.security.SecurityUtils; /** diff --git a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/writer/openssh/OpenSSHKeyPairResourceWriterTest.java b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/writer/openssh/OpenSSHKeyPairResourceWriterTest.java index 83f345c..f042f8d 100644 --- a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/writer/openssh/OpenSSHKeyPairResourceWriterTest.java +++ b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/writer/openssh/OpenSSHKeyPairResourceWriterTest.java @@ -45,7 +45,7 @@ import org.apache.sshd.common.config.keys.AuthorizedKeyEntry; import org.apache.sshd.common.config.keys.FilePasswordProvider; import org.apache.sshd.common.config.keys.KeyUtils; import org.apache.sshd.common.config.keys.PublicKeyEntryResolver; -import org.apache.sshd.common.util.io.SecureByteArrayOutputStream; +import org.apache.sshd.common.util.io.output.SecureByteArrayOutputStream; import org.apache.sshd.common.util.io.resource.PathResource; import org.apache.sshd.common.util.security.SecurityUtils; import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory; diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/io/EmptyInputStreamTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/io/input/EmptyInputStreamTest.java similarity index 99% rename from sshd-common/src/test/java/org/apache/sshd/common/util/io/EmptyInputStreamTest.java rename to sshd-common/src/test/java/org/apache/sshd/common/util/io/input/EmptyInputStreamTest.java index 113758d..2958f0e 100644 --- a/sshd-common/src/test/java/org/apache/sshd/common/util/io/EmptyInputStreamTest.java +++ b/sshd-common/src/test/java/org/apache/sshd/common/util/io/input/EmptyInputStreamTest.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.sshd.common.util.io; +package org.apache.sshd.common.util.io.input; import java.io.IOException; import java.io.InputStream; diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/io/LimitInputStreamTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/io/input/LimitInputStreamTest.java similarity index 99% rename from sshd-common/src/test/java/org/apache/sshd/common/util/io/LimitInputStreamTest.java rename to sshd-common/src/test/java/org/apache/sshd/common/util/io/input/LimitInputStreamTest.java index e009527..4bd71db 100644 --- a/sshd-common/src/test/java/org/apache/sshd/common/util/io/LimitInputStreamTest.java +++ b/sshd-common/src/test/java/org/apache/sshd/common/util/io/input/LimitInputStreamTest.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.sshd.common.util.io; +package org.apache.sshd.common.util.io.input; import java.io.IOException; import java.io.InputStream; diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseInputStreamTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/io/input/NoCloseInputStreamTest.java similarity index 98% rename from sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseInputStreamTest.java rename to sshd-common/src/test/java/org/apache/sshd/common/util/io/input/NoCloseInputStreamTest.java index 33ab102..e22a2ba 100644 --- a/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseInputStreamTest.java +++ b/sshd-common/src/test/java/org/apache/sshd/common/util/io/input/NoCloseInputStreamTest.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.sshd.common.util.io; +package org.apache.sshd.common.util.io.input; import java.io.IOException; import java.io.InputStream; diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseReaderTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/io/input/NoCloseReaderTest.java similarity index 98% rename from sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseReaderTest.java rename to sshd-common/src/test/java/org/apache/sshd/common/util/io/input/NoCloseReaderTest.java index 14a72fc..471a012 100644 --- a/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseReaderTest.java +++ b/sshd-common/src/test/java/org/apache/sshd/common/util/io/input/NoCloseReaderTest.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.sshd.common.util.io; +package org.apache.sshd.common.util.io.input; import java.io.IOException; import java.io.InputStream; diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/io/NullInputStreamTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/io/input/NullInputStreamTest.java similarity index 98% rename from sshd-common/src/test/java/org/apache/sshd/common/util/io/NullInputStreamTest.java rename to sshd-common/src/test/java/org/apache/sshd/common/util/io/input/NullInputStreamTest.java index 31982da..0e8ee80 100644 --- a/sshd-common/src/test/java/org/apache/sshd/common/util/io/NullInputStreamTest.java +++ b/sshd-common/src/test/java/org/apache/sshd/common/util/io/input/NullInputStreamTest.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.sshd.common.util.io; +package org.apache.sshd.common.util.io.input; import java.io.EOFException; import java.io.IOException; diff --git a/sshd-contrib/src/test/java/org/apache/sshd/contrib/common/util/io/LineOutputStreamTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/io/output/LineOutputStreamTest.java similarity index 98% rename from sshd-contrib/src/test/java/org/apache/sshd/contrib/common/util/io/LineOutputStreamTest.java rename to sshd-common/src/test/java/org/apache/sshd/common/util/io/output/LineOutputStreamTest.java index 7e58da1..54b2259 100644 --- a/sshd-contrib/src/test/java/org/apache/sshd/contrib/common/util/io/LineOutputStreamTest.java +++ b/sshd-common/src/test/java/org/apache/sshd/common/util/io/output/LineOutputStreamTest.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.sshd.contrib.common.util.io; +package org.apache.sshd.common.util.io.output; import java.io.IOException; import java.io.InputStream; diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseOutputStreamTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/io/output/NoCloseOutputStreamTest.java similarity index 98% rename from sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseOutputStreamTest.java rename to sshd-common/src/test/java/org/apache/sshd/common/util/io/output/NoCloseOutputStreamTest.java index f01f132..60db28b 100644 --- a/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseOutputStreamTest.java +++ b/sshd-common/src/test/java/org/apache/sshd/common/util/io/output/NoCloseOutputStreamTest.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.sshd.common.util.io; +package org.apache.sshd.common.util.io.output; import java.io.IOException; import java.io.OutputStream; diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseWriterTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/io/output/NoCloseWriterTest.java similarity index 98% rename from sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseWriterTest.java rename to sshd-common/src/test/java/org/apache/sshd/common/util/io/output/NoCloseWriterTest.java index 090ac15..41cb5df 100644 --- a/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseWriterTest.java +++ b/sshd-common/src/test/java/org/apache/sshd/common/util/io/output/NoCloseWriterTest.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.sshd.common.util.io; +package org.apache.sshd.common.util.io.output; import java.io.IOException; import java.io.OutputStream; diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/io/NullOutputStreamTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/io/output/NullOutputStreamTest.java similarity index 98% rename from sshd-common/src/test/java/org/apache/sshd/common/util/io/NullOutputStreamTest.java rename to sshd-common/src/test/java/org/apache/sshd/common/util/io/output/NullOutputStreamTest.java index 0fe845f..0a8872e 100644 --- a/sshd-common/src/test/java/org/apache/sshd/common/util/io/NullOutputStreamTest.java +++ b/sshd-common/src/test/java/org/apache/sshd/common/util/io/output/NullOutputStreamTest.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.sshd.common.util.io; +package org.apache.sshd.common.util.io.output; import java.io.EOFException; import java.io.IOException; diff --git a/sshd-contrib/src/test/java/org/apache/sshd/contrib/server/session/EndlessTarpitSenderSupportDevelopment.java b/sshd-contrib/src/test/java/org/apache/sshd/contrib/server/session/EndlessTarpitSenderSupportDevelopment.java index 735c507..fa35b84 100644 --- a/sshd-contrib/src/test/java/org/apache/sshd/contrib/server/session/EndlessTarpitSenderSupportDevelopment.java +++ b/sshd-contrib/src/test/java/org/apache/sshd/contrib/server/session/EndlessTarpitSenderSupportDevelopment.java @@ -52,7 +52,7 @@ import org.apache.sshd.common.session.SessionListener; import org.apache.sshd.common.util.GenericUtils; import org.apache.sshd.common.util.buffer.Buffer; import org.apache.sshd.common.util.buffer.ByteArrayBuffer; -import org.apache.sshd.common.util.io.NoCloseInputStream; +import org.apache.sshd.common.util.io.input.NoCloseInputStream; import org.apache.sshd.common.util.logging.AbstractLoggingBean; import org.apache.sshd.contrib.common.io.EndlessWriteFuture; import org.apache.sshd.contrib.common.io.ImmediateWriteFuture; diff --git a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java index ff4c2f6..4700198 100644 --- a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java +++ b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java @@ -56,8 +56,8 @@ import org.apache.sshd.common.forward.PortForwardingManager; import org.apache.sshd.common.future.KeyExchangeFuture; import org.apache.sshd.common.keyprovider.KeyIdentityProvider; import org.apache.sshd.common.session.Session; -import org.apache.sshd.common.util.io.NoCloseOutputStream; -import org.apache.sshd.common.util.io.NullOutputStream; +import org.apache.sshd.common.util.io.output.NoCloseOutputStream; +import org.apache.sshd.common.util.io.output.NullOutputStream; import org.apache.sshd.common.util.net.SshdSocketAddress; /** diff --git a/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java b/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java index 963a14b..84b8b72 100644 --- a/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java +++ b/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java @@ -60,7 +60,7 @@ import org.apache.sshd.common.util.buffer.Buffer; import org.apache.sshd.common.util.buffer.ByteArrayBuffer; import org.apache.sshd.common.util.closeable.IoBaseCloseable; import org.apache.sshd.common.util.io.IoUtils; -import org.apache.sshd.common.util.io.LoggingFilterOutputStream; +import org.apache.sshd.common.util.io.output.LoggingFilterOutputStream; import org.apache.sshd.core.CoreModuleProperties; import org.apache.sshd.server.Environment; import org.apache.sshd.server.ServerFactoryManager; diff --git a/sshd-core/src/main/java/org/apache/sshd/server/channel/PipeDataReceiver.java b/sshd-core/src/main/java/org/apache/sshd/server/channel/PipeDataReceiver.java index cc7118d..d7f167f 100644 --- a/sshd-core/src/main/java/org/apache/sshd/server/channel/PipeDataReceiver.java +++ b/sshd-core/src/main/java/org/apache/sshd/server/channel/PipeDataReceiver.java @@ -26,7 +26,7 @@ import org.apache.sshd.common.PropertyResolver; import org.apache.sshd.common.channel.ChannelPipedInputStream; import org.apache.sshd.common.channel.ChannelPipedOutputStream; import org.apache.sshd.common.channel.Window; -import org.apache.sshd.common.util.io.LoggingFilterOutputStream; +import org.apache.sshd.common.util.io.output.LoggingFilterOutputStream; import org.apache.sshd.common.util.logging.AbstractLoggingBean; /** diff --git a/sshd-core/src/test/java/org/apache/sshd/KeyReExchangeTest.java b/sshd-core/src/test/java/org/apache/sshd/KeyReExchangeTest.java index a536252..874c5b1 100644 --- a/sshd-core/src/test/java/org/apache/sshd/KeyReExchangeTest.java +++ b/sshd-core/src/test/java/org/apache/sshd/KeyReExchangeTest.java @@ -52,7 +52,7 @@ import org.apache.sshd.common.session.Session; import org.apache.sshd.common.session.SessionListener; import org.apache.sshd.common.util.GenericUtils; import org.apache.sshd.common.util.ProxyUtils; -import org.apache.sshd.common.util.io.NullOutputStream; +import org.apache.sshd.common.util.io.output.NullOutputStream; import org.apache.sshd.common.util.security.SecurityUtils; import org.apache.sshd.core.CoreModuleProperties; import org.apache.sshd.server.Environment; diff --git a/sshd-core/src/test/java/org/apache/sshd/WindowAdjustTest.java b/sshd-core/src/test/java/org/apache/sshd/WindowAdjustTest.java index bb43718..a7e2a70 100644 --- a/sshd-core/src/test/java/org/apache/sshd/WindowAdjustTest.java +++ b/sshd-core/src/test/java/org/apache/sshd/WindowAdjustTest.java @@ -41,7 +41,7 @@ import org.apache.sshd.common.io.WritePendingException; import org.apache.sshd.common.util.buffer.Buffer; import org.apache.sshd.common.util.buffer.ByteArrayBuffer; import org.apache.sshd.common.util.io.IoUtils; -import org.apache.sshd.common.util.io.NoCloseOutputStream; +import org.apache.sshd.common.util.io.output.NoCloseOutputStream; import org.apache.sshd.common.util.logging.AbstractLoggingBean; import org.apache.sshd.common.util.threads.ThreadUtils; import org.apache.sshd.server.Environment; diff --git a/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java b/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java index cf2e0cd..b1ae9b1 100644 --- a/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java +++ b/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java @@ -90,7 +90,7 @@ import org.apache.sshd.common.util.GenericUtils; import org.apache.sshd.common.util.ValidateUtils; import org.apache.sshd.common.util.buffer.Buffer; import org.apache.sshd.common.util.buffer.ByteArrayBuffer; -import org.apache.sshd.common.util.io.NoCloseOutputStream; +import org.apache.sshd.common.util.io.output.NoCloseOutputStream; import org.apache.sshd.common.util.net.SshdSocketAddress; import org.apache.sshd.common.util.security.SecurityUtils; import org.apache.sshd.core.CoreModuleProperties; diff --git a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeysTestSupport.java b/sshd-core/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeysTestSupport.java index bd8a715..b6df091 100644 --- a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeysTestSupport.java +++ b/sshd-core/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeysTestSupport.java @@ -37,8 +37,8 @@ import java.util.Objects; import org.apache.sshd.common.cipher.ECCurves; import org.apache.sshd.common.util.GenericUtils; import org.apache.sshd.common.util.io.IoUtils; -import org.apache.sshd.common.util.io.NoCloseInputStream; -import org.apache.sshd.common.util.io.NoCloseReader; +import org.apache.sshd.common.util.io.input.NoCloseInputStream; +import org.apache.sshd.common.util.io.input.NoCloseReader; import org.apache.sshd.common.util.security.SecurityUtils; import org.apache.sshd.server.config.keys.AuthorizedKeysAuthenticator; import org.apache.sshd.util.test.BaseTestSupport; diff --git a/sshd-scp/src/main/java/org/apache/sshd/scp/client/ScpRemote2RemoteTransferHelper.java b/sshd-scp/src/main/java/org/apache/sshd/scp/client/ScpRemote2RemoteTransferHelper.java index fc2a036..3d87921 100644 --- a/sshd-scp/src/main/java/org/apache/sshd/scp/client/ScpRemote2RemoteTransferHelper.java +++ b/sshd-scp/src/main/java/org/apache/sshd/scp/client/ScpRemote2RemoteTransferHelper.java @@ -35,7 +35,7 @@ import org.apache.sshd.client.channel.ChannelExec; import org.apache.sshd.client.session.ClientSession; import org.apache.sshd.common.util.SelectorUtils; import org.apache.sshd.common.util.io.IoUtils; -import org.apache.sshd.common.util.io.LimitInputStream; +import org.apache.sshd.common.util.io.input.LimitInputStream; import org.apache.sshd.common.util.logging.AbstractLoggingBean; import org.apache.sshd.scp.ScpModuleProperties; import org.apache.sshd.scp.client.ScpClient.Option; 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 d560f6f..d713b43 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 @@ -44,7 +44,7 @@ import org.apache.sshd.common.session.Session; import org.apache.sshd.common.session.SessionHolder; import org.apache.sshd.common.util.GenericUtils; import org.apache.sshd.common.util.io.IoUtils; -import org.apache.sshd.common.util.io.LimitInputStream; +import org.apache.sshd.common.util.io.input.LimitInputStream; import org.apache.sshd.common.util.logging.AbstractLoggingBean; import org.apache.sshd.scp.ScpModuleProperties; import org.apache.sshd.scp.common.ScpTransferEventListener.FileOperation; diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/SftpClientFactory.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/SftpClientFactory.java index 84514d2..986085b 100644 --- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/SftpClientFactory.java +++ b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/SftpClientFactory.java @@ -65,7 +65,34 @@ public interface SftpClientFactory { * @return The created {@link SftpClient} instance * @throws IOException If failed to create the client */ - SftpClient createSftpClient(ClientSession session, SftpVersionSelector selector) throws IOException; + default SftpClient createSftpClient(ClientSession session, SftpVersionSelector selector) throws IOException { + return createSftpClient(session, selector, SftpErrorDataHandler.EMPTY); + } + + /** + * Create an SFTP client from this session. + * + * @param session The {@link ClientSession} to be used for creating the SFTP client + * @param errorDataHandler The {@link SftpErrorDataHandler} to handle incoming data through the error stream - if + * {@code null} the data is silently ignored + * @return The created {@link SftpClient} + * @throws IOException if failed to create the client + */ + default SftpClient createSftpClient(ClientSession session, SftpErrorDataHandler errorDataHandler) throws IOException { + return createSftpClient(session, SftpVersionSelector.CURRENT, errorDataHandler); + } + + /** + * @param session The {@link ClientSession} to which the SFTP client should be attached + * @param selector The {@link SftpVersionSelector} to use in order to negotiate the SFTP version + * @param errorDataHandler The {@link SftpErrorDataHandler} to handle incoming data through the error stream - if + * {@code null} the data is silently ignored + * @return The created {@link SftpClient} instance + * @throws IOException If failed to create the client + */ + SftpClient createSftpClient( + ClientSession session, SftpVersionSelector selector, SftpErrorDataHandler errorDataHandler) + throws IOException; default SftpFileSystem createSftpFileSystem(ClientSession session) throws IOException { return createSftpFileSystem(session, SftpVersionSelector.CURRENT); @@ -90,16 +117,25 @@ public interface SftpClientFactory { return createSftpFileSystem(session, SftpVersionSelector.CURRENT, readBufferSize, writeBufferSize); } + default SftpFileSystem createSftpFileSystem( + ClientSession session, SftpVersionSelector selector, int readBufferSize, int writeBufferSize) + throws IOException { + return createSftpFileSystem(session, selector, SftpErrorDataHandler.EMPTY, readBufferSize, writeBufferSize); + } + /** - * @param session The {@link ClientSession} to which the SFTP client backing the file system should be - * attached - * @param selector The {@link SftpVersionSelector} to use in order to negotiate the SFTP version - * @param readBufferSize Default I/O read buffer size - * @param writeBufferSize Default I/O write buffer size - * @return The created {@link SftpFileSystem} instance - * @throws IOException If failed to create the instance + * @param session The {@link ClientSession} to which the SFTP client backing the file system should be + * attached + * @param selector The {@link SftpVersionSelector} to use in order to negotiate the SFTP version + * @param errorDataHandler The {@link SftpErrorDataHandler} to handle incoming data through the error stream - if + * {@code null} the data is silently ignored + * @param readBufferSize Default I/O read buffer size + * @param writeBufferSize Default I/O write buffer size + * @return The created {@link SftpFileSystem} instance + * @throws IOException If failed to create the instance */ SftpFileSystem createSftpFileSystem( - ClientSession session, SftpVersionSelector selector, int readBufferSize, int writeBufferSize) + ClientSession session, SftpVersionSelector selector, SftpErrorDataHandler errorDataHandler, + int readBufferSize, int writeBufferSize) throws IOException; } diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseOutputStream.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/SftpErrorDataHandler.java similarity index 60% rename from sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseOutputStream.java rename to sshd-sftp/src/main/java/org/apache/sshd/sftp/client/SftpErrorDataHandler.java index 4ba16d3..76edf2a 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseOutputStream.java +++ b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/SftpErrorDataHandler.java @@ -16,32 +16,28 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.sshd.common.util.io; -import java.io.FilterOutputStream; +package org.apache.sshd.sftp.client; + import java.io.IOException; -import java.io.OutputStream; /** - * TODO Add javadoc + * Callback for any error stream data sent by the server * * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> */ -public class NoCloseOutputStream extends FilterOutputStream { - public NoCloseOutputStream(OutputStream out) { - super(out); - } - - @Override - public void close() throws IOException { - // ignored - } +@FunctionalInterface +public interface SftpErrorDataHandler { + SftpErrorDataHandler EMPTY = (buf, start, len) -> { + /* ignored */ }; - public static OutputStream resolveOutputStream(OutputStream output, boolean okToClose) { - if ((output == null) || okToClose) { - return output; - } else { - return new NoCloseOutputStream(output); - } - } + /** + * Receive binary data from server error stream + * + * @param buf The buffer of the incoming data + * @param start Offset in buffer to read the data + * @param len Available data in buffer + * @throws IOException If failed to receive incoming data + */ + void errorData(byte[] buf, int start, int len) throws IOException; } diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/fs/SftpFileSystem.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/fs/SftpFileSystem.java index f962796..c29c333 100644 --- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/fs/SftpFileSystem.java +++ b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/fs/SftpFileSystem.java @@ -52,6 +52,7 @@ import org.apache.sshd.sftp.SftpModuleProperties; import org.apache.sshd.sftp.client.RawSftpClient; import org.apache.sshd.sftp.client.SftpClient; import org.apache.sshd.sftp.client.SftpClientFactory; +import org.apache.sshd.sftp.client.SftpErrorDataHandler; import org.apache.sshd.sftp.client.SftpVersionSelector; import org.apache.sshd.sftp.client.impl.AbstractSftpClient; import org.apache.sshd.sftp.common.SftpConstants; @@ -67,6 +68,7 @@ public class SftpFileSystem private final ClientSession clientSession; private final SftpClientFactory factory; private final SftpVersionSelector selector; + private final SftpErrorDataHandler errorDataHandler; private final Queue<SftpClient> pool; private final ThreadLocal<Wrapper> wrappers = new ThreadLocal<>(); private final int version; @@ -77,12 +79,14 @@ public class SftpFileSystem private final List<FileStore> stores; public SftpFileSystem(SftpFileSystemProvider provider, String id, ClientSession session, - SftpClientFactory factory, SftpVersionSelector selector) throws IOException { + SftpClientFactory factory, SftpVersionSelector selector, SftpErrorDataHandler errorDataHandler) + throws IOException { super(provider); this.id = id; this.clientSession = Objects.requireNonNull(session, "No client session"); this.factory = factory != null ? factory : SftpClientFactory.instance(); this.selector = selector; + this.errorDataHandler = errorDataHandler; this.stores = Collections.unmodifiableList(Collections.<FileStore> singletonList(new SftpFileStore(id, this))); this.pool = new LinkedBlockingQueue<>(SftpModuleProperties.POOL_SIZE.getRequired(session)); try (SftpClient client = getClient()) { @@ -104,6 +108,10 @@ public class SftpFileSystem return selector; } + public SftpErrorDataHandler getSftpErrorDataHandler() { + return errorDataHandler; + } + public final String getId() { return id; } @@ -171,10 +179,13 @@ public class SftpFileSystem SftpClient client = pool.poll(); if (client == null) { ClientSession session = getClientSession(); - client = factory.createSftpClient(session, getSftpVersionSelector()); + client = factory.createSftpClient( + session, getSftpVersionSelector(), getSftpErrorDataHandler()); } if (!client.isClosing()) { - wrapper = new Wrapper(client, getReadBufferSize(), getWriteBufferSize()); + wrapper = new Wrapper( + client, + getSftpErrorDataHandler(), getReadBufferSize(), getWriteBufferSize()); } } wrappers.set(wrapper); @@ -231,7 +242,9 @@ public class SftpFileSystem private final int readSize; private final int writeSize; - private Wrapper(SftpClient delegate, int readSize, int writeSize) { + private Wrapper(SftpClient delegate, SftpErrorDataHandler errorHandler, int readSize, int writeSize) { + super(errorHandler); + this.delegate = delegate; this.readSize = readSize; this.writeSize = writeSize; diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/fs/SftpFileSystemClientSessionInitializer.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/fs/SftpFileSystemClientSessionInitializer.java index fa27dd4..890864e 100644 --- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/fs/SftpFileSystemClientSessionInitializer.java +++ b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/fs/SftpFileSystemClientSessionInitializer.java @@ -26,6 +26,7 @@ import org.apache.sshd.client.session.ClientSession; import org.apache.sshd.client.session.ClientSessionCreator; import org.apache.sshd.common.auth.PasswordHolder; import org.apache.sshd.common.auth.UsernameHolder; +import org.apache.sshd.sftp.client.SftpErrorDataHandler; import org.apache.sshd.sftp.client.SftpVersionSelector; /** @@ -86,17 +87,20 @@ public interface SftpFileSystemClientSessionInitializer { * Invoked by the {@link SftpFileSystemProvider#newFileSystem(java.net.URI, Map)} method in order to create the * {@link SftpFileSystem} once session has been authenticated. * - * @param provider The {@link SftpFileSystemProvider} instance requesting the session - * @param context The initialization {@link SftpFileSystemInitializationContext} - * @param session The authenticated {@link ClientSession} - * @param selector The <U>resolved</U> {@link SftpVersionSelector} to use - * @return The created {@link SftpFileSystem} - * @throws IOException If failed to create the file-system + * @param provider The {@link SftpFileSystemProvider} instance requesting the session + * @param context The initialization {@link SftpFileSystemInitializationContext} + * @param session The authenticated {@link ClientSession} + * @param selector The <U>resolved</U> {@link SftpVersionSelector} to use + * @param errorDataHandler The {@link SftpErrorDataHandler} to handle incoming data through the error stream - if + * {@code null} the data is silently ignored + * @return The created {@link SftpFileSystem} + * @throws IOException If failed to create the file-system */ default SftpFileSystem createSftpFileSystem( SftpFileSystemProvider provider, SftpFileSystemInitializationContext context, ClientSession session, - SftpVersionSelector selector) + SftpVersionSelector selector, SftpErrorDataHandler errorDataHandler) throws IOException { - return new SftpFileSystem(provider, context.getId(), session, provider.getSftpClientFactory(), selector); + return new SftpFileSystem( + provider, context.getId(), session, provider.getSftpClientFactory(), selector, errorDataHandler); } } diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/fs/SftpFileSystemProvider.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/fs/SftpFileSystemProvider.java index c5e0f0d..e671298 100644 --- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/fs/SftpFileSystemProvider.java +++ b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/fs/SftpFileSystemProvider.java @@ -91,6 +91,7 @@ import org.apache.sshd.sftp.client.SftpClient; import org.apache.sshd.sftp.client.SftpClient.Attributes; import org.apache.sshd.sftp.client.SftpClient.OpenMode; import org.apache.sshd.sftp.client.SftpClientFactory; +import org.apache.sshd.sftp.client.SftpErrorDataHandler; import org.apache.sshd.sftp.client.SftpVersionSelector; import org.apache.sshd.sftp.client.extensions.CopyFileExtension; import org.apache.sshd.sftp.client.impl.SftpRemotePathChannel; @@ -132,6 +133,7 @@ public class SftpFileSystemProvider extends FileSystemProvider { private final SshClient clientInstance; private final SftpClientFactory factory; private final SftpVersionSelector versionSelector; + private final SftpErrorDataHandler errorDataHandler; private final NavigableMap<String, SftpFileSystem> fileSystems = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); private SftpFileSystemClientSessionInitializer fsSessionInitializer = SftpFileSystemClientSessionInitializer.DEFAULT; @@ -143,23 +145,38 @@ public class SftpFileSystemProvider extends FileSystemProvider { this(null, selector); } - /** - * @param client The {@link SshClient} to use - if {@code null} then a default one will be setup and started. - * Otherwise, it is assumed that the client has already been started - * @see SshClient#setUpDefaultClient() - */ public SftpFileSystemProvider(SshClient client) { this(client, SftpVersionSelector.CURRENT); } public SftpFileSystemProvider(SshClient client, SftpVersionSelector selector) { - this(client, null, selector); + this(client, selector, SftpErrorDataHandler.EMPTY); + } + + public SftpFileSystemProvider(SshClient client, SftpVersionSelector selector, SftpErrorDataHandler errorDataHandler) { + this(client, null, selector, errorDataHandler); + } public SftpFileSystemProvider(SshClient client, SftpClientFactory factory, SftpVersionSelector selector) { + this(client, factory, selector, SftpErrorDataHandler.EMPTY); + } + + /** + * @param client The {@link SshClient} to use - if {@code null} then a default one will be setup and + * started. Otherwise, it is assumed that the client has already been started + * @param factory The {@link SftpClientFactory} to use to generate SFTP client instances + * @param selector The {@link SftpVersionSelector} to use in order to negotiate the SFTP version + * @param errorDataHandler The {@link SftpErrorDataHandler} to handle incoming data through the error stream - if + * {@code null} the data is silently ignored + * @see SshClient#setUpDefaultClient() + */ + public SftpFileSystemProvider(SshClient client, SftpClientFactory factory, + SftpVersionSelector selector, SftpErrorDataHandler errorDataHandler) { this.log = LoggerFactory.getLogger(getClass()); this.factory = factory; this.versionSelector = selector; + this.errorDataHandler = errorDataHandler; if (client == null) { // TODO: make this configurable using system properties client = SshClient.setUpDefaultClient(); @@ -177,6 +194,10 @@ public class SftpFileSystemProvider extends FileSystemProvider { return versionSelector; } + public SftpErrorDataHandler getSftpErrorDataHandler() { + return errorDataHandler; + } + public final SshClient getClientInstance() { return clientInstance; } @@ -218,6 +239,7 @@ public class SftpFileSystemProvider extends FileSystemProvider { context.setMaxAuthTime(SftpModuleProperties.AUTH_TIME.getRequired(resolver)); SftpVersionSelector selector = resolveSftpVersionSelector(uri, getSftpVersionSelector(), resolver); + SftpErrorDataHandler errorHandler = resolveSftpErrorDataHandler(uri, getSftpErrorDataHandler(), resolver); Charset decodingCharset = SftpModuleProperties.NAME_DECODER_CHARSET.getRequired(resolver); SftpFileSystemClientSessionInitializer initializer = getSftpFileSystemClientSessionInitializer(); @@ -250,7 +272,8 @@ public class SftpFileSystemProvider extends FileSystemProvider { initializer.authenticateClientSession(this, context, session); - fileSystem = initializer.createSftpFileSystem(this, context, session, selector); + fileSystem = initializer.createSftpFileSystem( + this, context, session, selector, errorHandler); fileSystems.put(id, fileSystem); } catch (Exception e) { if (session != null) { @@ -311,6 +334,11 @@ public class SftpFileSystemProvider extends FileSystemProvider { } } + protected SftpErrorDataHandler resolveSftpErrorDataHandler( + URI uri, SftpErrorDataHandler errorHandler, PropertyResolver resolver) { + return errorHandler; + } + // NOTE: URI parameters override environment ones public static Map<String, Object> resolveFileSystemParameters(Map<String, ?> env, Map<String, Object> uriParams) { if (MapEntryUtils.isEmpty(env)) { @@ -395,7 +423,7 @@ public class SftpFileSystemProvider extends FileSystemProvider { if (fileSystems.containsKey(id)) { throw new FileSystemAlreadyExistsException(id); } - fileSystem = new SftpFileSystem(this, id, session, factory, getSftpVersionSelector()); + fileSystem = new SftpFileSystem(this, id, session, factory, getSftpVersionSelector(), getSftpErrorDataHandler()); fileSystems.put(id, fileSystem); } diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/AbstractSftpClient.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/AbstractSftpClient.java index 0d29b51..7a3ce00 100644 --- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/AbstractSftpClient.java +++ b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/AbstractSftpClient.java @@ -45,6 +45,7 @@ import org.apache.sshd.common.util.buffer.Buffer; import org.apache.sshd.common.util.buffer.ByteArrayBuffer; import org.apache.sshd.sftp.SftpModuleProperties; import org.apache.sshd.sftp.client.FullAccessSftpClient; +import org.apache.sshd.sftp.client.SftpErrorDataHandler; import org.apache.sshd.sftp.client.extensions.BuiltinSftpClientExtensions; import org.apache.sshd.sftp.client.extensions.SftpClientExtension; import org.apache.sshd.sftp.client.extensions.SftpClientExtensionFactory; @@ -56,13 +57,18 @@ import org.apache.sshd.sftp.common.extensions.ParserUtils; /** * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> */ -public abstract class AbstractSftpClient extends AbstractSubsystemClient implements FullAccessSftpClient { +public abstract class AbstractSftpClient + extends AbstractSubsystemClient + implements FullAccessSftpClient, SftpErrorDataHandler { public static final int INIT_COMMAND_SIZE = Byte.BYTES /* command */ + Integer.BYTES /* version */; + protected final SftpErrorDataHandler errorDataHandler; + private final Attributes fileOpenAttributes = new Attributes(); private final AtomicReference<Map<String, Object>> parsedExtensionsHolder = new AtomicReference<>(null); - protected AbstractSftpClient() { + protected AbstractSftpClient(SftpErrorDataHandler delegateHandler) { + errorDataHandler = (delegateHandler == null) ? SftpErrorDataHandler.EMPTY : delegateHandler; fileOpenAttributes.setType(SftpConstants.SSH_FILEXFER_TYPE_REGULAR); } @@ -846,6 +852,17 @@ public abstract class AbstractSftpClient extends AbstractSubsystemClient impleme } @Override + public void errorData(byte[] buf, int start, int len) throws IOException { + /* + * The protocol does not specify how to handle such data but we are lenient and ignore it - similar to + * /dev/null + */ + if (errorDataHandler != null) { + errorDataHandler.errorData(buf, start, len); + } + } + + @Override public void write(Handle handle, long fileOffset, byte[] src, int srcOffset, int len) throws IOException { // do some bounds checking first if ((fileOffset < 0L) || (srcOffset < 0) || (len < 0)) { diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/DefaultSftpClient.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/DefaultSftpClient.java index ae3f1c7..21f21ac 100644 --- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/DefaultSftpClient.java +++ b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/DefaultSftpClient.java @@ -56,9 +56,9 @@ import org.apache.sshd.common.util.MapEntryUtils; import org.apache.sshd.common.util.ValidateUtils; import org.apache.sshd.common.util.buffer.Buffer; import org.apache.sshd.common.util.buffer.ByteArrayBuffer; -import org.apache.sshd.common.util.io.NullOutputStream; import org.apache.sshd.core.CoreModuleProperties; import org.apache.sshd.sftp.SftpModuleProperties; +import org.apache.sshd.sftp.client.SftpErrorDataHandler; import org.apache.sshd.sftp.client.SftpVersionSelector; import org.apache.sshd.sftp.common.SftpConstants; import org.apache.sshd.sftp.common.extensions.ParserUtils; @@ -84,9 +84,16 @@ public class DefaultSftpClient extends AbstractSftpClient { * @param clientSession The {@link ClientSession} * @param initialVersionSelector The initial {@link SftpVersionSelector} - if {@code null} then version 6 is * assumed. + * @param errorDataHandler The {@link SftpErrorDataHandler} to handle incoming data through the error stream + * - if {@code null} the data is silently ignored * @throws IOException If failed to initialize */ - public DefaultSftpClient(ClientSession clientSession, SftpVersionSelector initialVersionSelector) throws IOException { + public DefaultSftpClient( + ClientSession clientSession, SftpVersionSelector initialVersionSelector, + SftpErrorDataHandler errorDataHandler) + throws IOException { + super(errorDataHandler); + this.nameDecodingCharset = SftpModuleProperties.NAME_DECODING_CHARSET.getRequired(clientSession); this.clientSession = Objects.requireNonNull(clientSession, "No client session"); this.channel = createSftpChannelSubsystem(clientSession); @@ -161,11 +168,11 @@ public class DefaultSftpClient extends AbstractSftpClient { } /** - * Receive binary data + * Receive binary data from server main stream * - * @param buf The buffer for the incoming data - * @param start Offset in buffer to place the data - * @param len Available space in buffer for the data + * @param buf The buffer containing the incoming data + * @param start Offset in buffer to read the data + * @param len Available data in buffer * @return Actual size of received data * @throws IOException If failed to receive incoming data */ @@ -288,7 +295,8 @@ public class DefaultSftpClient extends AbstractSftpClient { buf.putBuffer(buffer); } - IoOutputStream asyncIn = channel.getAsyncIn(); + ClientChannel clientChannel = getClientChannel(); + IoOutputStream asyncIn = clientChannel.getAsyncIn(); IoWriteFuture writeFuture = asyncIn.writeBuffer(buf); writeFuture.verify(); return id; @@ -364,8 +372,8 @@ public class DefaultSftpClient extends AbstractSftpClient { buf.putInt(initialVersion); boolean traceEnabled = log.isTraceEnabled(); - IoOutputStream asyncIn = channel.getAsyncIn(); ClientChannel clientChannel = getClientChannel(); + IoOutputStream asyncIn = clientChannel.getAsyncIn(); if (traceEnabled) { log.trace("init({}) send SSH_FXP_INIT - initial version={}", clientChannel, initialVersion); } @@ -592,11 +600,22 @@ public class DefaultSftpClient extends AbstractSftpClient { } protected OutputStream createErrOutputStream(Session session) { - /* - * The protocol does not specify how to handle such data but we are lenient and ignore it - similar to - * /dev/null - */ - return new NullOutputStream(); + return new OutputStream() { + private final byte[] singleByte = new byte[1]; + + @Override + public void write(int b) throws IOException { + synchronized (singleByte) { + singleByte[0] = (byte) b; + write(singleByte); + } + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + errorData(b, off, len); + } + }; } } } diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/DefaultSftpClientFactory.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/DefaultSftpClientFactory.java index d62f28b..11ed039 100644 --- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/DefaultSftpClientFactory.java +++ b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/DefaultSftpClientFactory.java @@ -26,6 +26,7 @@ import org.apache.sshd.client.session.ClientSession; import org.apache.sshd.common.util.logging.AbstractLoggingBean; import org.apache.sshd.sftp.client.SftpClient; import org.apache.sshd.sftp.client.SftpClientFactory; +import org.apache.sshd.sftp.client.SftpErrorDataHandler; import org.apache.sshd.sftp.client.SftpVersionSelector; import org.apache.sshd.sftp.client.fs.SftpFileSystem; import org.apache.sshd.sftp.client.fs.SftpFileSystemProvider; @@ -43,8 +44,10 @@ public class DefaultSftpClientFactory extends AbstractLoggingBean implements Sft } @Override - public SftpClient createSftpClient(ClientSession session, SftpVersionSelector selector) throws IOException { - DefaultSftpClient client = createDefaultSftpClient(session, selector); + public SftpClient createSftpClient( + ClientSession session, SftpVersionSelector selector, SftpErrorDataHandler errorDataHandler) + throws IOException { + DefaultSftpClient client = createDefaultSftpClient(session, selector, errorDataHandler); try { client.negotiateVersion(selector); } catch (IOException | RuntimeException | Error e) { @@ -57,17 +60,19 @@ public class DefaultSftpClientFactory extends AbstractLoggingBean implements Sft return client; } - protected DefaultSftpClient createDefaultSftpClient(ClientSession session, SftpVersionSelector selector) + protected DefaultSftpClient createDefaultSftpClient( + ClientSession session, SftpVersionSelector selector, SftpErrorDataHandler errorDataHandler) throws IOException { - return new DefaultSftpClient(session, selector); + return new DefaultSftpClient(session, selector, errorDataHandler); } @Override public SftpFileSystem createSftpFileSystem( - ClientSession session, SftpVersionSelector selector, int readBufferSize, int writeBufferSize) + ClientSession session, SftpVersionSelector selector, SftpErrorDataHandler errorDataHandler, + int readBufferSize, int writeBufferSize) throws IOException { ClientFactoryManager manager = session.getFactoryManager(); - SftpFileSystemProvider provider = new SftpFileSystemProvider((SshClient) manager, selector); + SftpFileSystemProvider provider = new SftpFileSystemProvider((SshClient) manager, selector, errorDataHandler); SftpFileSystem fs = provider.newFileSystem(session); if (readBufferSize > 0) { fs.setReadBufferSize(readBufferSize); diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/SftpInputStreamAsync.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/SftpInputStreamAsync.java index 71d8251..3c822e8 100644 --- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/SftpInputStreamAsync.java +++ b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/SftpInputStreamAsync.java @@ -35,7 +35,7 @@ import org.apache.sshd.common.channel.Window; import org.apache.sshd.common.session.Session; import org.apache.sshd.common.util.buffer.Buffer; import org.apache.sshd.common.util.buffer.ByteArrayBuffer; -import org.apache.sshd.common.util.io.InputStreamWithChannel; +import org.apache.sshd.common.util.io.input.InputStreamWithChannel; import org.apache.sshd.sftp.client.SftpClient; import org.apache.sshd.sftp.client.SftpClient.Attributes; import org.apache.sshd.sftp.client.SftpClient.CloseableHandle; diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/SftpOutputStreamAsync.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/SftpOutputStreamAsync.java index 627d3f4..db6147e 100644 --- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/SftpOutputStreamAsync.java +++ b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/SftpOutputStreamAsync.java @@ -28,7 +28,7 @@ import org.apache.sshd.common.SshConstants; import org.apache.sshd.common.session.Session; import org.apache.sshd.common.util.buffer.Buffer; import org.apache.sshd.common.util.buffer.ByteArrayBuffer; -import org.apache.sshd.common.util.io.OutputStreamWithChannel; +import org.apache.sshd.common.util.io.output.OutputStreamWithChannel; import org.apache.sshd.sftp.client.SftpClient; import org.apache.sshd.sftp.client.SftpClient.CloseableHandle; import org.apache.sshd.sftp.client.SftpClient.OpenMode; diff --git a/sshd-sftp/src/test/java/org/apache/sshd/sftp/client/SftpInputStreamWithChannel.java b/sshd-sftp/src/test/java/org/apache/sshd/sftp/client/SftpInputStreamWithChannel.java index e0ff3ab..27aa811 100644 --- a/sshd-sftp/src/test/java/org/apache/sshd/sftp/client/SftpInputStreamWithChannel.java +++ b/sshd-sftp/src/test/java/org/apache/sshd/sftp/client/SftpInputStreamWithChannel.java @@ -22,7 +22,7 @@ import java.io.IOException; import java.util.Collection; import java.util.Objects; -import org.apache.sshd.common.util.io.InputStreamWithChannel; +import org.apache.sshd.common.util.io.input.InputStreamWithChannel; import org.apache.sshd.sftp.client.SftpClient.CloseableHandle; import org.apache.sshd.sftp.client.SftpClient.OpenMode; diff --git a/sshd-sftp/src/test/java/org/apache/sshd/sftp/client/SftpOutputStreamWithChannel.java b/sshd-sftp/src/test/java/org/apache/sshd/sftp/client/SftpOutputStreamWithChannel.java index ee4b40a..e58d25b 100644 --- a/sshd-sftp/src/test/java/org/apache/sshd/sftp/client/SftpOutputStreamWithChannel.java +++ b/sshd-sftp/src/test/java/org/apache/sshd/sftp/client/SftpOutputStreamWithChannel.java @@ -22,7 +22,7 @@ import java.io.IOException; import java.util.Collection; import java.util.Objects; -import org.apache.sshd.common.util.io.OutputStreamWithChannel; +import org.apache.sshd.common.util.io.output.OutputStreamWithChannel; import org.apache.sshd.sftp.client.SftpClient.CloseableHandle; import org.apache.sshd.sftp.client.SftpClient.OpenMode;