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 925af4b [SSHD-1233] Added support for '[email protected]' SFTP
extension
925af4b is described below
commit 925af4b3f6b0496499c2ca6b9302fe21987db227
Author: Lyor Goldstein <[email protected]>
AuthorDate: Thu Dec 23 12:56:53 2021 +0200
[SSHD-1233] Added support for '[email protected]' SFTP extension
---
CHANGES.md | 16 +++
docs/sftp.md | 4 +-
.../apache/sshd/cli/client/SftpCommandMain.java | 64 +++++++--
.../apache/sshd/util/test/JUnitTestSupport.java | 17 +++
.../common/session/helpers/AbstractSession.java | 2 +-
.../org/apache/sshd/sftp/SftpModuleProperties.java | 18 ++-
.../extensions/BuiltinSftpClientExtensions.java | 10 ++
.../extensions/openssh/OpenSSHFsyncExtension.java | 2 +-
...cExtension.java => OpenSSHLimitsExtension.java} | 9 +-
.../openssh/OpenSSHLimitsExtensionInfo.java | 150 +++++++++++++++++++++
.../openssh/OpenSSHStatExtensionInfo.java | 18 +--
.../helpers/OpenSSHLimitsExtensionImpl.java | 52 +++++++
.../org/apache/sshd/sftp/client/fs/SftpPath.java | 2 +-
.../apache/sshd/sftp/client/impl/SftpPathImpl.java | 4 +-
.../sshd/sftp/common/extensions/ParserUtils.java | 4 +-
.../openssh/FstatVfsExtensionParser.java | 1 +
.../extensions/openssh/FsyncExtensionParser.java | 2 +-
.../openssh/HardLinkExtensionParser.java | 2 +-
.../openssh/LSetStatExtensionParser.java | 2 +-
...nsionParser.java => LimitsExtensionParser.java} | 10 +-
.../openssh/PosixRenameExtensionParser.java | 2 +-
.../extensions/openssh/StatVfsExtensionParser.java | 2 +-
.../server/AbstractSftpEventListenerAdapter.java | 44 +++++-
.../sftp/server/AbstractSftpSubsystemHelper.java | 91 ++++++++-----
.../org/apache/sshd/sftp/server/SftpSubsystem.java | 9 +-
.../java/org/apache/sshd/sftp/client/SftpTest.java | 4 +-
.../sftp/client/extensions/SftpExtensionsTest.java | 2 +-
.../openssh/helpers/OpenSSHExtensionsTest.java | 33 ++---
28 files changed, 471 insertions(+), 105 deletions(-)
diff --git a/CHANGES.md b/CHANGES.md
index 691aed2..c884827 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -20,9 +20,25 @@
## Potential compatibility issues
+* A **new** SFTP configuration property has been introduced that limits the
maximum amount of data that can be sent in a single *SSH_FXP_WRITE* packet -
default=256KB
+
+```java
+ /**
+ * Force the use of a max. packet length for {@link
AbstractSftpSubsystemHelper#doWrite(Buffer, int)} protection
+ * against malicious packets
+ */
+ public static final Property<Integer> MAX_WRITE_DATA_PACKET_LENGTH
+ = Property.integer("sftp-max-writedata-packet-length", 256 * 1024);
+```
+
+This might cause SFTP write failures for clients that might have sent larger
buffers and they have been accepted so far. If this happens, simply increase
+this value (though the choice of 256KB should be compatible with the vast
majority of clients).
+
## Minor code helpers
## Behavioral changes and enhancements
* [SSHD-1231](https://issues.apache.org/jira/browse/SSHD-1231) Public key
authentication: wrong signature algorithm used (ed25519 key with ssh-rsa
signature)
+* [SSHD-1233](https://issues.apache.org/jira/browse/SSHD-1233) Added support
for "[email protected]" SFTP extension
+
diff --git a/docs/sftp.md b/docs/sftp.md
index e76ddf3..224f7bf 100644
--- a/docs/sftp.md
+++ b/docs/sftp.md
@@ -629,6 +629,7 @@ Furthermore several [OpenSSH SFTP
extensions](https://github.com/openssh/openssh
* `[email protected]` - only client side
* `[email protected]`
* `[email protected]`
+* `[email protected]`
On the server side, the reported standard extensions are configured via the
`SftpModuleProperties.CLIENT_EXTENSIONS` configuration
key, and the _OpenSSH_ ones via the `SftpModuleProperties.OPENSSH_EXTENSIONS`.
@@ -693,7 +694,7 @@ try (ClientSession session = client.connect(username, host,
port).verify(timeout
* Add the code to handle the new extension in
`AbstractSftpSubsystemHelper#executeExtendedCommand`
-* Declare the extension name in `DEFAULT_SUPPORTED_CLIENT_EXTENSIONS` (same
class)
+* Declare the extension name in `DEFAULT_SUPPORTED_CLIENT_EXTENSIONS` or
`DEFAULT_OPEN_SSH_EXTENSIONS` (same class) - according to the extension type
(generic or *OpenSSH* one).
* In the `org.apache.sshd.sftp.client.extensions.helpers` package implement an
extension of `AbstractSftpClientExtension`
for sending and receiving the newly added extension.
@@ -744,7 +745,6 @@
sshd.setSubsystemFactories(Collections.singletonList(factory));
```
-
**Note:**
* The code assumes that the extension name is a **string** - the draft
specification actually allows an array of bytes as well, but we chose
simplicity.
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 feb5d98..2e98012 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
@@ -80,12 +80,15 @@ import org.apache.sshd.sftp.client.SftpClientFactory;
import org.apache.sshd.sftp.client.SftpClientHolder;
import org.apache.sshd.sftp.client.SftpVersionSelector;
import org.apache.sshd.sftp.client.SftpVersionSelector.NamedVersionSelector;
+import org.apache.sshd.sftp.client.extensions.openssh.OpenSSHLimitsExtension;
+import
org.apache.sshd.sftp.client.extensions.openssh.OpenSSHLimitsExtensionInfo;
import org.apache.sshd.sftp.client.extensions.openssh.OpenSSHStatExtensionInfo;
import org.apache.sshd.sftp.client.extensions.openssh.OpenSSHStatPathExtension;
import org.apache.sshd.sftp.client.fs.SftpFileSystemProvider;
import org.apache.sshd.sftp.common.SftpConstants;
import org.apache.sshd.sftp.common.SftpException;
import org.apache.sshd.sftp.common.extensions.ParserUtils;
+import org.apache.sshd.sftp.common.extensions.openssh.LimitsExtensionParser;
import org.apache.sshd.sftp.common.extensions.openssh.StatVfsExtensionParser;
import org.slf4j.Logger;
@@ -132,6 +135,7 @@ public class SftpCommandMain extends SshClientCliSupport
implements SftpClientHo
new GetCommandExecutor(),
new PutCommandExecutor(),
new ProgressCommandExecutor(),
+ new LimitsCommandExecutor(),
new HelpCommandExecutor())) {
String name = e.getName();
ValidateUtils.checkTrue(map.put(name, e) == null, "Multiple
commands named '%s'", name);
@@ -781,7 +785,7 @@ public class SftpCommandMain extends SshClientCliSupport
implements SftpClientHo
} else if (Files.isRegularFile(local)) {
displayLocalPathInfo(local, stdout);
} else {
- stderr.println("Unsupported special file");
+ stderr.println("[" + flags + "] Unsupported special file");
}
return false;
@@ -941,6 +945,35 @@ public class SftpCommandMain extends SshClientCliSupport
implements SftpClientHo
/* -------------------------------------------------------------------- */
+ private class LimitsCommandExecutor implements SftpCommandExecutor {
+ LimitsCommandExecutor() {
+ super();
+ }
+
+ @Override
+ public String getName() {
+ return LimitsExtensionParser.NAME;
+ }
+
+ @Override
+ public boolean executeCommand(
+ String args, BufferedReader stdin, PrintStream stdout,
PrintStream stderr)
+ throws Exception {
+ String[] comps = GenericUtils.split(args, ' ');
+ int numArgs = GenericUtils.length(comps);
+ ValidateUtils.checkTrue(numArgs <= 1, "Invalid number of
arguments: %s", args);
+
+ SftpClient sftp = getClient();
+ OpenSSHLimitsExtension ext =
sftp.getExtension(OpenSSHLimitsExtension.class);
+ ValidateUtils.checkTrue(ext.isSupported(), "Extension not
supported by server: %s", ext.getName());
+ OpenSSHLimitsExtensionInfo info = ext.limits();
+ printFieldsValues(info, stdout);
+ return false;
+ }
+ }
+
+ /* -------------------------------------------------------------------- */
+
private class StatVfsCommandExecutor implements SftpCommandExecutor {
StatVfsCommandExecutor() {
super();
@@ -966,18 +999,7 @@ public class SftpCommandMain extends SshClientCliSupport
implements SftpClientHo
String remPath = resolveRemotePath(
(numArgs >= 1) ? GenericUtils.trimToEmpty(comps[0]) :
GenericUtils.trimToEmpty(args));
OpenSSHStatExtensionInfo info = ext.stat(remPath);
- Field[] fields = info.getClass().getFields();
- for (Field f : fields) {
- String name = f.getName();
- int mod = f.getModifiers();
- if (Modifier.isStatic(mod)) {
- continue;
- }
-
- Object value = f.get(info);
- stdout.append(" ").append(name).append(": ").println(value);
- }
-
+ printFieldsValues(info, stdout);
return false;
}
}
@@ -1323,4 +1345,20 @@ public class SftpCommandMain extends SshClientCliSupport
implements SftpClientHo
return false;
}
}
+
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public static void printFieldsValues(Object info, PrintStream stdout)
throws Exception {
+ Field[] fields = info.getClass().getFields();
+ for (Field f : fields) {
+ String name = f.getName();
+ int mod = f.getModifiers();
+ if (Modifier.isStatic(mod)) {
+ continue;
+ }
+
+ Object value = f.get(info);
+ stdout.append(" ").append(name).append(": ").println(value);
+ }
+ }
}
diff --git
a/sshd-common/src/test/java/org/apache/sshd/util/test/JUnitTestSupport.java
b/sshd-common/src/test/java/org/apache/sshd/util/test/JUnitTestSupport.java
index 3a0bf0e..e3eca3d 100644
--- a/sshd-common/src/test/java/org/apache/sshd/util/test/JUnitTestSupport.java
+++ b/sshd-common/src/test/java/org/apache/sshd/util/test/JUnitTestSupport.java
@@ -22,6 +22,8 @@ package org.apache.sshd.util.test;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
@@ -348,6 +350,21 @@ public abstract class JUnitTestSupport extends Assert {
assertFalse(message + "[non-empty-actual]", actual.hasNext());
}
+ public static <T> void assertFieldsEqual(String extension, T expected, T
actual) throws Exception {
+ Field[] fields = expected.getClass().getFields();
+ for (Field f : fields) {
+ String name = f.getName();
+ int mod = f.getModifiers();
+ if (Modifier.isStatic(mod)) {
+ continue;
+ }
+
+ Object expValue = f.get(expected);
+ Object actValue = f.get(actual);
+ assertEquals(extension + "[" + name + "]", expValue, actValue);
+ }
+ }
+
public static Path assertHierarchyTargetFolderExists(Path folder,
LinkOption... options) throws IOException {
if (Files.exists(folder, options)) {
assertTrue("Target is an existing file instead of a folder: " +
folder, Files.isDirectory(folder, options));
diff --git
a/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java
b/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java
index 6222804..08e5d7e 100644
---
a/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java
+++
b/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java
@@ -1828,7 +1828,7 @@ public abstract class AbstractSession extends
SessionHelper {
log.debug("setOutputEncoding({}): cipher {}; mac {}; compression
{}; blocks limit {}", this, outCipher, outMac,
outCompression, maxRekeyBlocks);
}
- };
+ }
/**
* Installs the current prepared {@link #inSettings} so that they are
effective and will be applied to any future
diff --git
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/SftpModuleProperties.java
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/SftpModuleProperties.java
index 3303913..977735d 100644
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/SftpModuleProperties.java
+++ b/sshd-sftp/src/main/java/org/apache/sshd/sftp/SftpModuleProperties.java
@@ -156,18 +156,30 @@ public final class SftpModuleProperties {
public static final Property<String> NEWLINE_VALUE
= Property.string("sftp-newline", IoUtils.EOL);
+ public static final int MIN_READDATA_PACKET_LENGTH = 32768;
/**
* Force the use of a max. packet length for {@link
AbstractSftpSubsystemHelper#doRead(Buffer, int)} protection
* against malicious packets
*/
public static final Property<Integer> MAX_READDATA_PACKET_LENGTH
- = Property.integer("sftp-max-readdata-packet-length", 63 * 1024);
+ =
Property.validating(Property.integer("sftp-max-readdata-packet-length", 63 *
1024),
+ l -> ValidateUtils.checkTrue(l >=
MIN_READDATA_PACKET_LENGTH, "Length is below min.: %d", l));
+ public static final int MIN_WRITEDATA_PACKET_LENGTH = 32768;
/**
- * Properties key for the maximum of available open handles per session.
+ * Force the use of a max. packet length for {@link
AbstractSftpSubsystemHelper#doWrite(Buffer, int)} protection
+ * against malicious packets
+ */
+ public static final Property<Integer> MAX_WRITEDATA_PACKET_LENGTH
+ =
Property.validating(Property.integer("sftp-max-writedata-packet-length", 256 *
1024),
+ l -> ValidateUtils.checkTrue(l >=
MIN_WRITEDATA_PACKET_LENGTH, "Length is below min.: %d", l));
+
+ /**
+ * Properties key for the maximum of available open handles per session.
By default we impose virtually no limit and
+ * relay on the O/S.
*/
public static final Property<Integer> MAX_OPEN_HANDLES_PER_SESSION
- = Property.integer("max-open-handles-per-session",
Integer.MAX_VALUE);
+ = Property.integer("max-open-handles-per-session",
Integer.MAX_VALUE - 1);
public static final int MIN_FILE_HANDLE_SIZE = 4; // ~uint32
public static final int DEFAULT_FILE_HANDLE_SIZE = 16;
diff --git
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/extensions/BuiltinSftpClientExtensions.java
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/extensions/BuiltinSftpClientExtensions.java
index b531240..b5c919a 100644
---
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/extensions/BuiltinSftpClientExtensions.java
+++
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/extensions/BuiltinSftpClientExtensions.java
@@ -35,10 +35,12 @@ import
org.apache.sshd.sftp.client.extensions.helpers.MD5FileExtensionImpl;
import org.apache.sshd.sftp.client.extensions.helpers.MD5HandleExtensionImpl;
import
org.apache.sshd.sftp.client.extensions.helpers.SpaceAvailableExtensionImpl;
import org.apache.sshd.sftp.client.extensions.openssh.OpenSSHFsyncExtension;
+import org.apache.sshd.sftp.client.extensions.openssh.OpenSSHLimitsExtension;
import
org.apache.sshd.sftp.client.extensions.openssh.OpenSSHPosixRenameExtension;
import
org.apache.sshd.sftp.client.extensions.openssh.OpenSSHStatHandleExtension;
import org.apache.sshd.sftp.client.extensions.openssh.OpenSSHStatPathExtension;
import
org.apache.sshd.sftp.client.extensions.openssh.helpers.OpenSSHFsyncExtensionImpl;
+import
org.apache.sshd.sftp.client.extensions.openssh.helpers.OpenSSHLimitsExtensionImpl;
import
org.apache.sshd.sftp.client.extensions.openssh.helpers.OpenSSHPosixRenameExtensionImpl;
import
org.apache.sshd.sftp.client.extensions.openssh.helpers.OpenSSHStatHandleExtensionImpl;
import
org.apache.sshd.sftp.client.extensions.openssh.helpers.OpenSSHStatPathExtensionImpl;
@@ -46,6 +48,7 @@ import org.apache.sshd.sftp.common.SftpConstants;
import org.apache.sshd.sftp.common.extensions.ParserUtils;
import org.apache.sshd.sftp.common.extensions.openssh.FstatVfsExtensionParser;
import org.apache.sshd.sftp.common.extensions.openssh.FsyncExtensionParser;
+import org.apache.sshd.sftp.common.extensions.openssh.LimitsExtensionParser;
import
org.apache.sshd.sftp.common.extensions.openssh.PosixRenameExtensionParser;
import org.apache.sshd.sftp.common.extensions.openssh.StatVfsExtensionParser;
@@ -130,6 +133,13 @@ public enum BuiltinSftpClientExtensions implements
SftpClientExtensionFactory {
return new OpenSSHPosixRenameExtensionImpl(client, raw,
extensions);
}
},
+ OPENSSH_LIMITS(LimitsExtensionParser.NAME, OpenSSHLimitsExtension.class) {
+ @Override // co-variant return
+ public OpenSSHLimitsExtension create(
+ SftpClient client, RawSftpClient raw, Map<String, byte[]>
extensions, Map<String, ?> parsed) {
+ return new OpenSSHLimitsExtensionImpl(client, raw, extensions);
+ }
+ },
;
public static final Set<BuiltinSftpClientExtensions> VALUES
diff --git
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/extensions/openssh/OpenSSHFsyncExtension.java
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/extensions/openssh/OpenSSHFsyncExtension.java
index cbd5666..e0885ac 100644
---
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/extensions/openssh/OpenSSHFsyncExtension.java
+++
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/extensions/openssh/OpenSSHFsyncExtension.java
@@ -28,7 +28,7 @@ import
org.apache.sshd.sftp.client.extensions.SftpClientExtension;
* Implements the "[email protected]" extension
*
* @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a>
- * @see <A
HREF="https://github.com/openssh/openssh-portable/blob/master/PROTOCOL">OpenSSH
- section 10</A>
+ * @see <A
HREF="https://github.com/openssh/openssh-portable/blob/master/PROTOCOL">OpenSSH
- section 4.6</A>
*/
public interface OpenSSHFsyncExtension extends SftpClientExtension {
void fsync(Handle fileHandle) throws IOException;
diff --git
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/extensions/openssh/OpenSSHFsyncExtension.java
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/extensions/openssh/OpenSSHLimitsExtension.java
similarity index 79%
copy from
sshd-sftp/src/main/java/org/apache/sshd/sftp/client/extensions/openssh/OpenSSHFsyncExtension.java
copy to
sshd-sftp/src/main/java/org/apache/sshd/sftp/client/extensions/openssh/OpenSSHLimitsExtension.java
index cbd5666..91716a6 100644
---
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/extensions/openssh/OpenSSHFsyncExtension.java
+++
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/extensions/openssh/OpenSSHLimitsExtension.java
@@ -21,15 +21,12 @@ package org.apache.sshd.sftp.client.extensions.openssh;
import java.io.IOException;
-import org.apache.sshd.sftp.client.SftpClient.Handle;
import org.apache.sshd.sftp.client.extensions.SftpClientExtension;
/**
- * Implements the "[email protected]" extension
- *
* @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a>
- * @see <A
HREF="https://github.com/openssh/openssh-portable/blob/master/PROTOCOL">OpenSSH
- section 10</A>
+ * @see <A
HREF="https://github.com/openssh/openssh-portable/blob/master/PROTOCOL">OpenSSH
- section 4.8</A>
*/
-public interface OpenSSHFsyncExtension extends SftpClientExtension {
- void fsync(Handle fileHandle) throws IOException;
+public interface OpenSSHLimitsExtension extends SftpClientExtension {
+ OpenSSHLimitsExtensionInfo limits() throws IOException;
}
diff --git
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/extensions/openssh/OpenSSHLimitsExtensionInfo.java
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/extensions/openssh/OpenSSHLimitsExtensionInfo.java
new file mode 100644
index 0000000..f365902
--- /dev/null
+++
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/extensions/openssh/OpenSSHLimitsExtensionInfo.java
@@ -0,0 +1,150 @@
+/*
+ * 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.sftp.client.extensions.openssh;
+
+import org.apache.sshd.common.PropertyResolver;
+import org.apache.sshd.common.SshConstants;
+import org.apache.sshd.common.util.NumberUtils;
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.sftp.SftpModuleProperties;
+
+/**
+ * Response for the "[email protected]" request
+ *
+ * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a>
+ * @see <A
HREF="https://github.com/openssh/openssh-portable/blob/master/PROTOCOL">OpenSSH
- section 4.8</A>
+ */
+public class OpenSSHLimitsExtensionInfo implements Cloneable {
+ // CHECKSTYLE:OFF
+ /** The total number of bytes in a single SFTP packet. */
+ public long maxPacketLength;
+
+ /**
+ * The largest length in a SSH_FXP_READ packet. Even if the client
requests a larger size,\
+ * servers will usually respond with a shorter SSH_FXP_DATA packet
+ */
+ public long maxReadLength;
+
+ /** The largest length in a SSH_FXP_WRITE packet the server will accept */
+ public long maxWriteLength;
+
+ /**
+ * The maximum number of active handles that the server allows (e.g.
handles created
+ * by SSH_FXP_OPEN and SSH_FXP_OPENDIR packets). If the server doesn't
enforce a
+ * specific limit, then the field may be set to 0. This implies the server
relies on
+ * the OS to enforce limits (e.g. available memory or file handles), and
such limits
+ * might be dynamic. The client SHOULD take care to not try to exceed
reasonable limits.
+ */
+ public long maxOpenHandles;
+ // CHECKSTYLE:ON
+
+ public OpenSSHLimitsExtensionInfo() {
+ super();
+ }
+
+ public OpenSSHLimitsExtensionInfo(Buffer buffer) {
+ decode(buffer, this);
+ }
+
+ public OpenSSHLimitsExtensionInfo(PropertyResolver resolver) {
+ fill(resolver, this);
+ }
+
+ public <B extends Buffer> B encode(B buffer) {
+ return encode(buffer, this);
+ }
+
+ @Override
+ public int hashCode() {
+ return NumberUtils.hashCode(maxPacketLength, maxReadLength,
maxWriteLength, maxOpenHandles);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+
+ OpenSSHLimitsExtensionInfo other = (OpenSSHLimitsExtensionInfo) obj;
+ return (this.maxPacketLength == other.maxPacketLength)
+ && (this.maxReadLength == other.maxReadLength)
+ && (this.maxWriteLength == other.maxWriteLength)
+ && (this.maxOpenHandles == other.maxOpenHandles);
+ }
+
+ @Override
+ public OpenSSHLimitsExtensionInfo clone() {
+ try {
+ return getClass().cast(super.clone());
+ } catch (CloneNotSupportedException e) {
+ throw new RuntimeException("Failed to close " + toString() + ": "
+ e.getMessage());
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "maxPacketLength=" + maxPacketLength
+ + ", maxReadLength=" + maxReadLength
+ + ", maxWriteLength=" + maxWriteLength
+ + ", maxOpenHandles=" + maxOpenHandles;
+ }
+
+ public static <B extends Buffer> B encode(B buffer,
OpenSSHLimitsExtensionInfo info) {
+ buffer.putLong(info.maxPacketLength);
+ buffer.putLong(info.maxReadLength);
+ buffer.putLong(info.maxWriteLength);
+ buffer.putLong(info.maxOpenHandles);
+ return buffer;
+ }
+
+ public static <I extends OpenSSHLimitsExtensionInfo> I decode(Buffer
buffer, I info) {
+ info.maxPacketLength = buffer.getLong();
+ info.maxReadLength = buffer.getLong();
+ info.maxWriteLength = buffer.getLong();
+ info.maxOpenHandles = buffer.getLong();
+ return info;
+ }
+
+ public static <I extends OpenSSHLimitsExtensionInfo> I
fill(PropertyResolver resolver, I info) {
+ info.maxReadLength =
SftpModuleProperties.MAX_READDATA_PACKET_LENGTH.getRequired(resolver);
+ info.maxWriteLength =
SftpModuleProperties.MAX_WRITEDATA_PACKET_LENGTH.getRequired(resolver);
+ info.maxPacketLength = Math.max(info.maxReadLength,
info.maxWriteLength)
+ + SshConstants.SSH_PACKET_HEADER_LEN
+ + (3 * Integer.BYTES) // id, type, len
+ ;
+ info.maxOpenHandles =
SftpModuleProperties.MAX_OPEN_HANDLES_PER_SESSION.getRequired(resolver);
+ /*
+ * Quote:
+ *
+ * If the server doesn't enforce a specific limit, then the field
may be set to 0.
+ * This implies the server relies on the OS to enforce limits.
+ */
+ if (info.maxOpenHandles >= (Integer.MAX_VALUE - 1)) {
+ info.maxOpenHandles = 0;
+ }
+ return info;
+ }
+}
diff --git
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/extensions/openssh/OpenSSHStatExtensionInfo.java
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/extensions/openssh/OpenSSHStatExtensionInfo.java
index 4f0ae07..cc610b0 100644
---
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/extensions/openssh/OpenSSHStatExtensionInfo.java
+++
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/extensions/openssh/OpenSSHStatExtensionInfo.java
@@ -26,9 +26,7 @@ import org.apache.sshd.common.util.buffer.Buffer;
* Response for the "[email protected]" and
"[email protected]" extension commands.
*
* @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a>
- * @see <A HREF=
- *
"http://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/usr.bin/ssh/PROTOCOL?rev=1.28&content-type=text/plain">OpenSSH
- * section 3.4</A>
+ * @see <A
HREF="https://github.com/openssh/openssh-portable/blob/master/PROTOCOL">OpenSSH
- section 4.4</A>
*/
public class OpenSSHStatExtensionInfo implements Cloneable {
// The values of the f_flag bitmask
@@ -57,6 +55,10 @@ public class OpenSSHStatExtensionInfo implements Cloneable {
decode(buffer, this);
}
+ public <B extends Buffer> B encode(B buffer) {
+ return encode(buffer, this);
+ }
+
@Override
public int hashCode() {
return NumberUtils.hashCode(this.f_bsize, this.f_frsize, this.f_blocks,
@@ -115,7 +117,7 @@ public class OpenSSHStatExtensionInfo implements Cloneable {
+ ",f_namemax=" + f_namemax;
}
- public static void encode(Buffer buffer, OpenSSHStatExtensionInfo info) {
+ public static <B extends Buffer> B encode(B buffer,
OpenSSHStatExtensionInfo info) {
buffer.putLong(info.f_bsize);
buffer.putLong(info.f_frsize);
buffer.putLong(info.f_blocks);
@@ -127,15 +129,14 @@ public class OpenSSHStatExtensionInfo implements
Cloneable {
buffer.putLong(info.f_fsid);
buffer.putLong(info.f_flag);
buffer.putLong(info.f_namemax);
+ return buffer;
}
public static OpenSSHStatExtensionInfo decode(Buffer buffer) {
- OpenSSHStatExtensionInfo info = new OpenSSHStatExtensionInfo();
- decode(buffer, info);
- return info;
+ return decode(buffer, new OpenSSHStatExtensionInfo());
}
- public static void decode(Buffer buffer, OpenSSHStatExtensionInfo info) {
+ public static <I extends OpenSSHStatExtensionInfo> I decode(Buffer buffer,
I info) {
info.f_bsize = buffer.getLong();
info.f_frsize = buffer.getLong();
info.f_blocks = buffer.getLong();
@@ -147,5 +148,6 @@ public class OpenSSHStatExtensionInfo implements Cloneable {
info.f_fsid = buffer.getLong();
info.f_flag = buffer.getLong();
info.f_namemax = buffer.getLong();
+ return info;
}
}
diff --git
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/extensions/openssh/helpers/OpenSSHLimitsExtensionImpl.java
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/extensions/openssh/helpers/OpenSSHLimitsExtensionImpl.java
new file mode 100644
index 0000000..e07f005
--- /dev/null
+++
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/extensions/openssh/helpers/OpenSSHLimitsExtensionImpl.java
@@ -0,0 +1,52 @@
+/*
+ * 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.sftp.client.extensions.openssh.helpers;
+
+import java.io.IOException;
+import java.io.StreamCorruptedException;
+import java.util.Map;
+
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.sftp.client.RawSftpClient;
+import org.apache.sshd.sftp.client.SftpClient;
+import
org.apache.sshd.sftp.client.extensions.helpers.AbstractSftpClientExtension;
+import org.apache.sshd.sftp.client.extensions.openssh.OpenSSHLimitsExtension;
+import
org.apache.sshd.sftp.client.extensions.openssh.OpenSSHLimitsExtensionInfo;
+import org.apache.sshd.sftp.common.extensions.openssh.LimitsExtensionParser;
+
+/**
+ * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a>
+ */
+public class OpenSSHLimitsExtensionImpl extends AbstractSftpClientExtension
implements OpenSSHLimitsExtension {
+ public OpenSSHLimitsExtensionImpl(SftpClient client, RawSftpClient raw,
Map<String, byte[]> extensions) {
+ super(LimitsExtensionParser.NAME, client, raw, extensions);
+ }
+
+ @Override
+ public OpenSSHLimitsExtensionInfo limits() throws IOException {
+ Buffer buffer = getCommandBuffer(Integer.BYTES);
+ buffer =
checkExtendedReplyBuffer(receive(sendExtendedCommand(buffer)));
+ if (buffer == null) {
+ throw new StreamCorruptedException("Missing extended reply data");
+ }
+
+ return new OpenSSHLimitsExtensionInfo(buffer);
+ }
+}
diff --git
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/fs/SftpPath.java
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/fs/SftpPath.java
index 11221a5..a8c6468 100644
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/fs/SftpPath.java
+++ b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/fs/SftpPath.java
@@ -28,7 +28,7 @@ import org.apache.sshd.common.file.util.BasePath;
import org.apache.sshd.sftp.client.SftpClient;
/**
- * A {@link Path} on an {@link SftpFileSystem}.
+ * A {@link java.nio.file.Path} on an {@link SftpFileSystem}.
*/
public class SftpPath extends BasePath<SftpPath, SftpFileSystem> {
diff --git
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/SftpPathImpl.java
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/SftpPathImpl.java
index 35e9f59..d9d76cc 100644
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/SftpPathImpl.java
+++ b/sshd-sftp/src/main/java/org/apache/sshd/sftp/client/impl/SftpPathImpl.java
@@ -57,7 +57,7 @@ public class SftpPathImpl extends SftpPath {
*
* @param doCache whether to start caching (increasing the cache level) or
to stop caching (decreasing the cache
* level)
- * @see {@link #withAttributeCache(Path, IOFunction)}
+ * @see #withAttributeCache(Path, IOFunction)
*/
protected void cacheAttributes(boolean doCache) {
if (doCache) {
@@ -133,7 +133,7 @@ public class SftpPathImpl extends SftpPath {
* @return the result of the {@code operation}
* @throws IOException if thrown by the {@code operation}
*
- * @see {@link #withAttributeCache(IOFunction)}
+ * @see #withAttributeCache(IOFunction)
*/
public static <T> T withAttributeCache(Path path, IOFunction<Path, T>
operation) throws IOException {
if (path instanceof SftpPathImpl) {
diff --git
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/common/extensions/ParserUtils.java
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/common/extensions/ParserUtils.java
index 5e28cee..600d1c2 100644
---
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/common/extensions/ParserUtils.java
+++
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/common/extensions/ParserUtils.java
@@ -42,6 +42,7 @@ import
org.apache.sshd.sftp.common.extensions.openssh.FstatVfsExtensionParser;
import org.apache.sshd.sftp.common.extensions.openssh.FsyncExtensionParser;
import org.apache.sshd.sftp.common.extensions.openssh.HardLinkExtensionParser;
import org.apache.sshd.sftp.common.extensions.openssh.LSetStatExtensionParser;
+import org.apache.sshd.sftp.common.extensions.openssh.LimitsExtensionParser;
import
org.apache.sshd.sftp.common.extensions.openssh.PosixRenameExtensionParser;
import org.apache.sshd.sftp.common.extensions.openssh.StatVfsExtensionParser;
@@ -65,7 +66,8 @@ public final class ParserUtils {
FstatVfsExtensionParser.INSTANCE,
HardLinkExtensionParser.INSTANCE,
FsyncExtensionParser.INSTANCE,
- LSetStatExtensionParser.INSTANCE));
+ LSetStatExtensionParser.INSTANCE,
+ LimitsExtensionParser.INSTANCE));
private static final NavigableMap<String, ExtensionParser<?>> PARSERS_MAP
= Collections.unmodifiableNavigableMap(
BUILT_IN_PARSERS.stream()
diff --git
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/common/extensions/openssh/FstatVfsExtensionParser.java
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/common/extensions/openssh/FstatVfsExtensionParser.java
index 612a487..ff7d824 100644
---
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/common/extensions/openssh/FstatVfsExtensionParser.java
+++
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/common/extensions/openssh/FstatVfsExtensionParser.java
@@ -21,6 +21,7 @@ package org.apache.sshd.sftp.common.extensions.openssh;
/**
* @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a>
+ * @see <A
HREF="https://github.com/openssh/openssh-portable/blob/master/PROTOCOL">OpenSSH
- section 4.4</A>
*/
public class FstatVfsExtensionParser extends AbstractOpenSSHExtensionParser {
public static final String NAME = "[email protected]";
diff --git
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/common/extensions/openssh/FsyncExtensionParser.java
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/common/extensions/openssh/FsyncExtensionParser.java
index e5ab96c..2ee19b9 100644
---
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/common/extensions/openssh/FsyncExtensionParser.java
+++
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/common/extensions/openssh/FsyncExtensionParser.java
@@ -21,7 +21,7 @@ package org.apache.sshd.sftp.common.extensions.openssh;
/**
* @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a>
- * @see <A
HREF="https://github.com/openssh/openssh-portable/blob/master/PROTOCOL">OpenSSH
- section 10</A>
+ * @see <A
HREF="https://github.com/openssh/openssh-portable/blob/master/PROTOCOL">OpenSSH
- section 4.6</A>
*/
public class FsyncExtensionParser extends AbstractOpenSSHExtensionParser {
public static final String NAME = "[email protected]";
diff --git
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/common/extensions/openssh/HardLinkExtensionParser.java
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/common/extensions/openssh/HardLinkExtensionParser.java
index 90d559c..ea2d4d1 100644
---
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/common/extensions/openssh/HardLinkExtensionParser.java
+++
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/common/extensions/openssh/HardLinkExtensionParser.java
@@ -21,7 +21,7 @@ package org.apache.sshd.sftp.common.extensions.openssh;
/**
* @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a>
- * @see <A
HREF="https://github.com/openssh/openssh-portable/blob/master/PROTOCOL">OpenSSH
- section 10</A>
+ * @see <A
HREF="https://github.com/openssh/openssh-portable/blob/master/PROTOCOL">OpenSSH
- section 4.5</A>
*/
public class HardLinkExtensionParser extends AbstractOpenSSHExtensionParser {
public static final String NAME = "[email protected]";
diff --git
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/common/extensions/openssh/LSetStatExtensionParser.java
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/common/extensions/openssh/LSetStatExtensionParser.java
index 69856a4..b6884ff 100644
---
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/common/extensions/openssh/LSetStatExtensionParser.java
+++
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/common/extensions/openssh/LSetStatExtensionParser.java
@@ -23,7 +23,7 @@ package org.apache.sshd.sftp.common.extensions.openssh;
* Replicates the functionality of the existing {@code SSH_FXP_SETSTAT}
operation but does not follow symbolic links
*
* @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a>
- * @see <A HREF="https://www.openssh.com/txt/release-8.0">OpenSSH v8.0
release notes</A>
+ * @see <A
HREF="https://github.com/openssh/openssh-portable/blob/master/PROTOCOL">OpenSSH
- section 4.7</A>
*/
public class LSetStatExtensionParser extends AbstractOpenSSHExtensionParser {
public static final String NAME = "[email protected]";
diff --git
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/common/extensions/openssh/FsyncExtensionParser.java
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/common/extensions/openssh/LimitsExtensionParser.java
similarity index 77%
copy from
sshd-sftp/src/main/java/org/apache/sshd/sftp/common/extensions/openssh/FsyncExtensionParser.java
copy to
sshd-sftp/src/main/java/org/apache/sshd/sftp/common/extensions/openssh/LimitsExtensionParser.java
index e5ab96c..73b4d06 100644
---
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/common/extensions/openssh/FsyncExtensionParser.java
+++
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/common/extensions/openssh/LimitsExtensionParser.java
@@ -21,13 +21,13 @@ package org.apache.sshd.sftp.common.extensions.openssh;
/**
* @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a>
- * @see <A
HREF="https://github.com/openssh/openssh-portable/blob/master/PROTOCOL">OpenSSH
- section 10</A>
+ * @see <A
HREF="https://github.com/openssh/openssh-portable/blob/master/PROTOCOL">OpenSSH
- section 4.8</A>
*/
-public class FsyncExtensionParser extends AbstractOpenSSHExtensionParser {
- public static final String NAME = "[email protected]";
- public static final FsyncExtensionParser INSTANCE = new
FsyncExtensionParser();
+public class LimitsExtensionParser extends AbstractOpenSSHExtensionParser {
+ public static final String NAME = "[email protected]";
+ public static final LimitsExtensionParser INSTANCE = new
LimitsExtensionParser();
- public FsyncExtensionParser() {
+ public LimitsExtensionParser() {
super(NAME);
}
}
diff --git
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/common/extensions/openssh/PosixRenameExtensionParser.java
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/common/extensions/openssh/PosixRenameExtensionParser.java
index 9f8690c..f71da6b 100644
---
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/common/extensions/openssh/PosixRenameExtensionParser.java
+++
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/common/extensions/openssh/PosixRenameExtensionParser.java
@@ -21,7 +21,7 @@ package org.apache.sshd.sftp.common.extensions.openssh;
/**
* @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a>
- * @see <A
HREF="https://github.com/openssh/openssh-portable/blob/master/PROTOCOL">OpenSSH
- section 3.3</A>
+ * @see <A
HREF="https://github.com/openssh/openssh-portable/blob/master/PROTOCOL">OpenSSH
- section 4.3</A>
*/
public class PosixRenameExtensionParser extends AbstractOpenSSHExtensionParser
{
public static final String NAME = "[email protected]";
diff --git
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/common/extensions/openssh/StatVfsExtensionParser.java
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/common/extensions/openssh/StatVfsExtensionParser.java
index 8f28fe5..8ce333f 100644
---
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/common/extensions/openssh/StatVfsExtensionParser.java
+++
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/common/extensions/openssh/StatVfsExtensionParser.java
@@ -21,7 +21,7 @@ package org.apache.sshd.sftp.common.extensions.openssh;
/**
* @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a>
- * @see <A
HREF="https://github.com/openssh/openssh-portable/blob/master/PROTOCOL">OpenSSH
- section 3.4</A>
+ * @see <A
HREF="https://github.com/openssh/openssh-portable/blob/master/PROTOCOL">OpenSSH
- section 4.4</A>
*/
public class StatVfsExtensionParser extends AbstractOpenSSHExtensionParser {
public static final String NAME = "[email protected]";
diff --git
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/AbstractSftpEventListenerAdapter.java
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/AbstractSftpEventListenerAdapter.java
index 9d4fd4f..43f2e06 100644
---
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/AbstractSftpEventListenerAdapter.java
+++
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/AbstractSftpEventListenerAdapter.java
@@ -42,9 +42,16 @@ public abstract class AbstractSftpEventListenerAdapter
extends AbstractLoggingBe
}
@Override
- public void initialized(ServerSession session, int version) throws
IOException {
+ public void receivedExtension(ServerSession session, String extension, int
id) throws IOException {
if (log.isTraceEnabled()) {
- log.trace("initialized(" + session + ") version: " + version);
+ log.trace("receivedExtension({}) id={}, extension={}", session,
id, extension);
+ }
+ }
+
+ @Override
+ public void initialized(ServerSession session, int version) throws
IOException {
+ if (log.isDebugEnabled()) {
+ log.debug("initialized(" + session + ") version=" + version);
}
}
@@ -65,6 +72,16 @@ public abstract class AbstractSftpEventListenerAdapter
extends AbstractLoggingBe
}
@Override
+ public void openFailed(
+ ServerSession session, String remotePath, Path localPath, boolean
isDirectory, Throwable thrown)
+ throws IOException {
+ if (log.isTraceEnabled()) {
+ log.trace("openFailed({}) remotePath={}, localPath={}, isDir={},
thrown={}",
+ session, remotePath, localPath, isDirectory, thrown);
+ }
+ }
+
+ @Override
public void open(ServerSession session, String remoteHandle, Handle
localHandle) throws IOException {
if (log.isTraceEnabled()) {
Path path = localHandle.getFile();
@@ -74,6 +91,14 @@ public abstract class AbstractSftpEventListenerAdapter
extends AbstractLoggingBe
}
@Override
+ public void readingEntries(ServerSession session, String remoteHandle,
DirectoryHandle localHandle)
+ throws IOException {
+ if (log.isTraceEnabled()) {
+ log.trace("readingEntries({}) handle={}[{}]", session,
remoteHandle, localHandle.getFile());
+ }
+ }
+
+ @Override
public void readEntries(ServerSession session, String remoteHandle,
DirectoryHandle localHandle, Map<String, Path> entries)
throws IOException {
int numEntries = MapEntryUtils.size(entries);
@@ -181,6 +206,14 @@ public abstract class AbstractSftpEventListenerAdapter
extends AbstractLoggingBe
}
@Override
+ public void closed(ServerSession session, String remoteHandle, Handle
localHandle, Throwable thrown)
+ throws IOException {
+ if (log.isTraceEnabled()) {
+ log.trace("closed({}) handle={}[{}], thrown={}", session,
remoteHandle, localHandle.getFile(), thrown);
+ }
+ }
+
+ @Override
public void creating(ServerSession session, Path path, Map<String, ?>
attrs)
throws IOException {
if (log.isTraceEnabled()) {
@@ -264,4 +297,11 @@ public abstract class AbstractSftpEventListenerAdapter
extends AbstractLoggingBe
+ ((thrown == null) ? "" : (": " +
thrown.getClass().getSimpleName() + ": " + thrown.getMessage())));
}
}
+
+ @Override
+ public void exiting(ServerSession session, Handle handle) throws
IOException {
+ if (log.isDebugEnabled()) {
+ log.debug("exiting({}) handle={}[{}]", session, handle.getFile(),
handle.getFileHandle());
+ }
+ }
}
diff --git
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/AbstractSftpSubsystemHelper.java
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/AbstractSftpSubsystemHelper.java
index 812f24b..b3e32bf 100644
---
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/AbstractSftpSubsystemHelper.java
+++
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/AbstractSftpSubsystemHelper.java
@@ -93,6 +93,7 @@ import org.apache.sshd.server.channel.ChannelSession;
import org.apache.sshd.server.session.ServerSession;
import org.apache.sshd.sftp.SftpModuleProperties;
import org.apache.sshd.sftp.client.SftpClient;
+import
org.apache.sshd.sftp.client.extensions.openssh.OpenSSHLimitsExtensionInfo;
import org.apache.sshd.sftp.client.fs.SftpPath;
import org.apache.sshd.sftp.client.impl.SftpPathImpl;
import org.apache.sshd.sftp.common.SftpConstants;
@@ -104,6 +105,7 @@ import
org.apache.sshd.sftp.common.extensions.openssh.AbstractOpenSSHExtensionPa
import org.apache.sshd.sftp.common.extensions.openssh.FsyncExtensionParser;
import org.apache.sshd.sftp.common.extensions.openssh.HardLinkExtensionParser;
import org.apache.sshd.sftp.common.extensions.openssh.LSetStatExtensionParser;
+import org.apache.sshd.sftp.common.extensions.openssh.LimitsExtensionParser;
import
org.apache.sshd.sftp.common.extensions.openssh.PosixRenameExtensionParser;
/**
@@ -137,7 +139,8 @@ public abstract class AbstractSftpSubsystemHelper
new OpenSSHExtension(FsyncExtensionParser.NAME, "1"),
new OpenSSHExtension(HardLinkExtensionParser.NAME, "1"),
new OpenSSHExtension(LSetStatExtensionParser.NAME, "1"),
- new OpenSSHExtension(PosixRenameExtensionParser.NAME,
"1")));
+ new OpenSSHExtension(PosixRenameExtensionParser.NAME, "1"),
+ new OpenSSHExtension(LimitsExtensionParser.NAME, "1")));
public static final List<String> DEFAULT_OPEN_SSH_EXTENSIONS_NAMES =
Collections.unmodifiableList(
NamedResource.getNameList(DEFAULT_OPEN_SSH_EXTENSIONS));
@@ -790,7 +793,7 @@ public abstract class AbstractSftpSubsystemHelper
int id, String existingPath, String linkPath, boolean symLink)
throws IOException;
- // see https://github.com/openssh/openssh-portable/blob/master/PROTOCOL
section 10
+ // see https://github.com/openssh/openssh-portable/blob/master/PROTOCOL
section 4.5
protected void doOpenSSHHardLink(Buffer buffer, int id) throws IOException
{
String srcFile = buffer.getString();
String dstFile = buffer.getString();
@@ -806,6 +809,21 @@ public abstract class AbstractSftpSubsystemHelper
sendStatus(prepareReply(buffer), id, SftpConstants.SSH_FX_OK, "");
}
+ // see https://github.com/openssh/openssh-portable/blob/master/PROTOCOL
section 4.8
+ protected void doOpenSSHLimits(Buffer buffer, int id) throws IOException {
+ OpenSSHLimitsExtensionInfo info = getOpenSSHLimitsExtensionInfo(id,
getServerChannelSession());
+ buffer = prepareReply(buffer);
+ buffer.putByte((byte) SftpConstants.SSH_FXP_EXTENDED_REPLY);
+ buffer.putInt(id);
+ info.encode(buffer);
+ send(buffer);
+
+ }
+
+ protected OpenSSHLimitsExtensionInfo getOpenSSHLimitsExtensionInfo(int id,
ChannelSession channel) throws IOException {
+ return new OpenSSHLimitsExtensionInfo(channel);
+ }
+
protected void doOpenSSHHardLink(int id, String srcFile, String dstFile)
throws IOException {
if (log.isDebugEnabled()) {
log.debug("doOpenSSHHardLink({})[id={}] SSH_FXP_EXTENDED[{}]
(src={}, dst={})",
@@ -1766,6 +1784,9 @@ public abstract class AbstractSftpSubsystemHelper
case PosixRenameExtensionParser.NAME:
doPosixRename(buffer, id);
break;
+ case LimitsExtensionParser.NAME:
+ doOpenSSHLimits(buffer, id);
+ break;
default:
doUnsupportedExtension(buffer, id, extension);
}
@@ -1866,6 +1887,10 @@ public abstract class AbstractSftpSubsystemHelper
return extList;
}
+ if (log.isDebugEnabled()) {
+ log.debug("appendOpenSSHExtensions({}): {}", session, extList);
+ }
+
for (OpenSSHExtension ext : extList) {
buffer.putString(ext.getName());
buffer.putString(ext.getVersion());
@@ -1943,15 +1968,16 @@ public abstract class AbstractSftpSubsystemHelper
* Appends the "versions" extension to the buffer. <B>Note:</B>
if overriding this method make sure you
* either do not append anything or use the correct extension name
*
- * @param buffer The {@link Buffer} to append to
- * @param value The recommended value - ignored if {@code null}/empty
- * @param session The {@link ServerSession} for which this extension is
added
- * @see SftpConstants#EXT_VERSIONS
+ * @param buffer The {@link Buffer} to append to
+ * @param value The recommended value - ignored if {@code null}/empty
+ * @param session The {@link ServerSession} for which this extension is
added
+ * @return The apended value
+ * @see SftpConstants#EXT_VERSIONS
*/
- protected void appendVersionsExtension(
+ protected String appendVersionsExtension(
Buffer buffer, String value, ServerSession session) {
if (GenericUtils.isEmpty(value)) {
- return;
+ return value;
}
if (log.isDebugEnabled()) {
@@ -1960,30 +1986,33 @@ public abstract class AbstractSftpSubsystemHelper
buffer.putString(SftpConstants.EXT_VERSIONS);
buffer.putString(value);
+ return value;
}
/**
* Appends the "newline" extension to the buffer. <B>Note:</B>
if overriding this method make sure you
* either do not append anything or use the correct extension name
*
- * @param buffer The {@link Buffer} to append to
- * @param session The {@link ServerSession} for which this extension is
added
- * @see SftpConstants#EXT_NEWLINE
- * @see #resolveNewlineValue(ServerSession)
+ * @param buffer The {@link Buffer} to append to
+ * @param session The {@link ServerSession} for which this extension is
added
+ * @return The appended value
+ * @see SftpConstants#EXT_NEWLINE
+ * @see #resolveNewlineValue(ServerSession)
*/
- protected void appendNewlineExtension(Buffer buffer, ServerSession
session) {
+ protected String appendNewlineExtension(Buffer buffer, ServerSession
session) {
String value = resolveNewlineValue(session);
if (GenericUtils.isEmpty(value)) {
- return;
+ return value;
}
if (log.isDebugEnabled()) {
log.debug("appendNewlineExtension({}) value={}",
- getServerSession(), BufferUtils.toHex(':',
value.getBytes(StandardCharsets.UTF_8)));
+ session, BufferUtils.toHex(':',
value.getBytes(StandardCharsets.UTF_8)));
}
buffer.putString(SftpConstants.EXT_NEWLINE);
buffer.putString(value);
+ return value;
}
protected String resolveNewlineValue(ServerSession session) {
@@ -1994,24 +2023,25 @@ public abstract class AbstractSftpSubsystemHelper
* Appends the "vendor-id" extension to the buffer. <B>Note:</B>
if overriding this method make sure you
* either do not append anything or use the correct extension name
*
- * @param buffer The {@link Buffer} to append to
- * @param versionProperties The currently available version properties -
ignored if {@code null}/empty. The code
- * expects the following values:
- * <UL>
- * <LI>{@code groupId} - as the vendor name</LI>
- * <LI>{@code artifactId} - as the product
name</LI>
- * <LI>{@code version} - as the product
version</LI>
- * </UL>
- * @param session The {@link ServerSession} for which these
properties are added
- * @see SftpConstants#EXT_VENDOR_ID
- * @see <A HREF=
- *
"http://tools.ietf.org/wg/secsh/draft-ietf-secsh-filexfer/draft-ietf-secsh-filexfer-09.txt">DRAFT
- * 09 - section 4.4</A>
+ * @param buffer The {@link Buffer} to append to
+ * @param versionProperties The currently available version properties -
ignored if {@code null}/empty. The code
+ * expects the following values:
+ * <UL>
+ * <LI>{@code groupId} - as the vendor name</LI>
+ * <LI>{@code artifactId} - as the product
name</LI>
+ * <LI>{@code version} - as the product
version</LI>
+ * </UL>
+ * @param session The {@link ServerSession} for which these
properties are added
+ * @return The version properties
+ * @see SftpConstants#EXT_VENDOR_ID
+ * @see <A HREF=
+ *
"http://tools.ietf.org/wg/secsh/draft-ietf-secsh-filexfer/draft-ietf-secsh-filexfer-09.txt">DRAFT
+ * 09 - section 4.4</A>
*/
- protected void appendVendorIdExtension(
+ protected Map<String, ?> appendVendorIdExtension(
Buffer buffer, Map<String, ?> versionProperties, ServerSession
session) {
if (MapEntryUtils.isEmpty(versionProperties)) {
- return;
+ return versionProperties;
}
if (log.isDebugEnabled()) {
@@ -2029,6 +2059,7 @@ public abstract class AbstractSftpSubsystemHelper
buffer.putString(resolver.getStringProperty("version",
FactoryManager.DEFAULT_VERSION)); // product-version
buffer.putLong(0L); // product-build-number
BufferUtils.updateLengthPlaceholder(buffer, lenPos);
+ return versionProperties;
}
/**
diff --git
a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystem.java
b/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystem.java
index d985c5d..34421cb 100644
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystem.java
+++ b/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystem.java
@@ -822,9 +822,10 @@ public class SftpSubsystem
throws IOException {
Handle h = handles.get(handle);
ServerSession session = getServerSession();
+ int maxAllowed =
SftpModuleProperties.MAX_WRITEDATA_PACKET_LENGTH.getRequired(session);
if (log.isTraceEnabled()) {
- log.trace("doWrite({})[id={}] SSH_FXP_WRITE (handle={}[{}],
offset={}, data=byte[{}])",
- session, id, handle, h, offset, length);
+ log.trace("doWrite({})[id={}] SSH_FXP_WRITE (handle={}[{}],
offset={}, length={}, maxAllowed={})",
+ session, id, handle, h, offset, length, maxAllowed);
}
FileHandle fh = validateHandle(handle, h, FileHandle.class);
@@ -838,6 +839,10 @@ public class SftpSubsystem
+ ": required=" + length + ",
available=" + remaining);
}
+ if (length > maxAllowed) {
+ throw new IOException("Reuested write size (" + length + ")
exceeds max. allowed (" + maxAllowed + ")");
+ }
+
SftpEventListener listener = getSftpEventListenerProxy();
listener.writing(session, handle, fh, offset, data, doff, length);
try {
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/sftp/client/SftpTest.java
b/sshd-sftp/src/test/java/org/apache/sshd/sftp/client/SftpTest.java
index 35422f8..ce4904d 100644
--- a/sshd-sftp/src/test/java/org/apache/sshd/sftp/client/SftpTest.java
+++ b/sshd-sftp/src/test/java/org/apache/sshd/sftp/client/SftpTest.java
@@ -212,8 +212,8 @@ public class SftpTest extends AbstractSftpClientTestSupport
{
Path parentPath = targetPath.getParent();
Path lclSftp = CommonTestSupportUtils.resolve(
targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME,
getClass().getSimpleName(), getCurrentTestName());
- Path testFile =
assertHierarchyTargetFolderExists(lclSftp).resolve("file.txt");
- byte[] expected = new byte[1024];
+ Path testFile =
assertHierarchyTargetFolderExists(lclSftp).resolve("file.bin");
+ byte[] expected = new
byte[(SftpModuleProperties.MIN_READDATA_PACKET_LENGTH + 16) * 4];
Factory<? extends Random> factory = sshd.getRandomFactory();
Random rnd = factory.create();
diff --git
a/sshd-sftp/src/test/java/org/apache/sshd/sftp/client/extensions/SftpExtensionsTest.java
b/sshd-sftp/src/test/java/org/apache/sshd/sftp/client/extensions/SftpExtensionsTest.java
index 22ac2b5..1dbeb45 100644
---
a/sshd-sftp/src/test/java/org/apache/sshd/sftp/client/extensions/SftpExtensionsTest.java
+++
b/sshd-sftp/src/test/java/org/apache/sshd/sftp/client/extensions/SftpExtensionsTest.java
@@ -80,7 +80,7 @@ public class SftpExtensionsTest extends
AbstractSftpClientTestSupport {
}
}
- @Test // see SSHD-1266
+ @Test // see SSHD-1166
public void testCustomFileExtensionAttributes() throws IOException {
Path targetPath = detectTargetFolder();
Path parentPath = targetPath.getParent();
diff --git
a/sshd-sftp/src/test/java/org/apache/sshd/sftp/client/extensions/openssh/helpers/OpenSSHExtensionsTest.java
b/sshd-sftp/src/test/java/org/apache/sshd/sftp/client/extensions/openssh/helpers/OpenSSHExtensionsTest.java
index bdcd9c9..8427844 100644
---
a/sshd-sftp/src/test/java/org/apache/sshd/sftp/client/extensions/openssh/helpers/OpenSSHExtensionsTest.java
+++
b/sshd-sftp/src/test/java/org/apache/sshd/sftp/client/extensions/openssh/helpers/OpenSSHExtensionsTest.java
@@ -22,8 +22,6 @@ package
org.apache.sshd.sftp.client.extensions.openssh.helpers;
import java.io.IOException;
import java.io.OutputStream;
import java.io.StreamCorruptedException;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -42,6 +40,8 @@ import
org.apache.sshd.sftp.client.AbstractSftpClientTestSupport;
import org.apache.sshd.sftp.client.SftpClient;
import org.apache.sshd.sftp.client.SftpClient.CloseableHandle;
import org.apache.sshd.sftp.client.extensions.openssh.OpenSSHFsyncExtension;
+import org.apache.sshd.sftp.client.extensions.openssh.OpenSSHLimitsExtension;
+import
org.apache.sshd.sftp.client.extensions.openssh.OpenSSHLimitsExtensionInfo;
import
org.apache.sshd.sftp.client.extensions.openssh.OpenSSHPosixRenameExtension;
import org.apache.sshd.sftp.client.extensions.openssh.OpenSSHStatExtensionInfo;
import
org.apache.sshd.sftp.client.extensions.openssh.OpenSSHStatHandleExtension;
@@ -144,8 +144,7 @@ public class OpenSSHExtensionsTest extends
AbstractSftpClientTestSupport {
Path parentPath = targetPath.getParent();
String srcPath =
CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, srcFile);
- final AtomicReference<String> extensionHolder = new
AtomicReference<>(null);
- final OpenSSHStatExtensionInfo expected = new
OpenSSHStatExtensionInfo();
+ OpenSSHStatExtensionInfo expected = new OpenSSHStatExtensionInfo();
expected.f_bavail = Short.MAX_VALUE;
expected.f_bfree = Integer.MAX_VALUE;
expected.f_blocks = Short.MAX_VALUE;
@@ -158,6 +157,7 @@ public class OpenSSHExtensionsTest extends
AbstractSftpClientTestSupport {
expected.f_fsid = 1L;
expected.f_namemax = 256;
+ AtomicReference<String> extensionHolder = new AtomicReference<>(null);
sshd.setSubsystemFactories(Collections.singletonList(new
SftpSubsystemFactory() {
@Override
public Command createSubsystem(ChannelSession channel) throws
IOException {
@@ -206,32 +206,25 @@ public class OpenSSHExtensionsTest extends
AbstractSftpClientTestSupport {
OpenSSHStatExtensionInfo actual = pathStat.stat(srcPath);
String invokedExtension = extensionHolder.getAndSet(null);
assertEquals("Mismatched invoked extension", pathStat.getName(),
invokedExtension);
- assertOpenSSHStatExtensionInfoEquals(invokedExtension, expected,
actual);
+ assertFieldsEqual(invokedExtension, expected, actual);
try (CloseableHandle handle = sftp.open(srcPath)) {
OpenSSHStatHandleExtension handleStat =
assertExtensionCreated(sftp, OpenSSHStatHandleExtension.class);
actual = handleStat.stat(handle);
invokedExtension = extensionHolder.getAndSet(null);
assertEquals("Mismatched invoked extension",
handleStat.getName(), invokedExtension);
- assertOpenSSHStatExtensionInfoEquals(invokedExtension,
expected, actual);
+ assertFieldsEqual(invokedExtension, expected, actual);
}
}
}
- private static void assertOpenSSHStatExtensionInfoEquals(
- String extension, OpenSSHStatExtensionInfo expected,
OpenSSHStatExtensionInfo actual)
- throws Exception {
- Field[] fields = expected.getClass().getFields();
- for (Field f : fields) {
- String name = f.getName();
- int mod = f.getModifiers();
- if (Modifier.isStatic(mod)) {
- continue;
- }
-
- Object expValue = f.get(expected);
- Object actValue = f.get(actual);
- assertEquals(extension + "[" + name + "]", expValue, actValue);
+ @Test // see SSHD-1233
+ public void testLimits() throws Exception {
+ try (SftpClient sftp = createSingleSessionClient()) {
+ OpenSSHLimitsExtension ext = assertExtensionCreated(sftp,
OpenSSHLimitsExtension.class);
+ OpenSSHLimitsExtensionInfo expected = new
OpenSSHLimitsExtensionInfo(sftp.getClientChannel());
+ OpenSSHLimitsExtensionInfo actual = ext.limits();
+ assertFieldsEqual(ext.getName(), expected, actual);
}
}
}