You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by lg...@apache.org on 2021/03/25 19:47:40 UTC
[mina-sshd] 04/05: [SSHD-1132] Added SFTP servder-side support for
non-UTF8 encoding of returned file names
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
commit 4a1c58d101019df2ece89a932412186a39728245
Author: Lyor Goldstein <lg...@apache.org>
AuthorDate: Tue Mar 23 17:41:33 2021 +0200
[SSHD-1132] Added SFTP servder-side support for non-UTF8 encoding of returned file names
---
CHANGES.md | 1 +
README.md | 1 +
docs/sftp.md | 5 +-
.../sftp/server/AbstractSftpSubsystemHelper.java | 54 ++++++++++++++--------
.../sshd/sftp/server/SftpFileSystemAccessor.java | 20 ++++++++
5 files changed, 62 insertions(+), 19 deletions(-)
diff --git a/CHANGES.md b/CHANGES.md
index f10009f..09cdcc2 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -50,6 +50,7 @@
* [SSHD-1127](https://issues.apache.org/jira/browse/SSHD-1127) Added capability to register a custom receiver for SFTP STDERR channel raw or stream data
* [SSHD-1132](https://issues.apache.org/jira/browse/SSHD-1132) Added SFTP client-side support for 'filename-charset' extension
* [SSHD-1132](https://issues.apache.org/jira/browse/SSHD-1132) Added SFTP client-side support for 'filename-translation-control' extension
+* [SSHD-1132](https://issues.apache.org/jira/browse/SSHD-1132) Added SFTP servder-side support for non-UTF8 encoding of returned file names
* [SSHD-1133](https://issues.apache.org/jira/browse/SSHD-1133) Added capability to specify a custom charset for parsing incoming commands to the `ScpShell`
* [SSHD-1133](https://issues.apache.org/jira/browse/SSHD-1133) Added capability to specify a custom charset for returning environment variables related data from the `ScpShell`
* [SSHD-1133](https://issues.apache.org/jira/browse/SSHD-1133) Added capability to specify a custom charset for handling the SCP protocol textual commands and responses
diff --git a/README.md b/README.md
index 9fab4f7..0d4297c 100644
--- a/README.md
+++ b/README.md
@@ -51,6 +51,7 @@ based applications requiring SSH support.
* `check-file-handle`, `check-file-name` - [DRAFT 09 - section 9.1.2](http://tools.ietf.org/wg/secsh/draft-ietf-secsh-filexfer/draft-ietf-secsh-filexfer-09.txt)
* `copy-file`, `copy-data` - [DRAFT 00 - sections 6, 7](http://tools.ietf.org/id/draft-ietf-secsh-filexfer-extensions-00.txt)
* `space-available` - [DRAFT 09 - section 9.3](http://tools.ietf.org/wg/secsh/draft-ietf-secsh-filexfer/draft-ietf-secsh-filexfer-09.txt)
+ * `filename-charset`, `filename-translation-control` - [DRAFT 13 - section 6](https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-6) - only client side
* Several [OpenSSH SFTP extensions](https://github.com/openssh/openssh-portable/blob/master/PROTOCOL)
* [Endless tarpit](https://nullprogram.com/blog/2019/03/22/) - see [HOWTO(s)](./docs/howto.md) section.
diff --git a/docs/sftp.md b/docs/sftp.md
index 08707d5..3a5c083 100644
--- a/docs/sftp.md
+++ b/docs/sftp.md
@@ -426,6 +426,8 @@ try (ClientSession session = client.connect(...)) {
```
+On the server side, one can use the `SftpFileSystemAccessor#putRemoteFileName` to encode the returned file name/path using non-UTF8 encoding. However, this might break clients that expect UTF-8 - i.e., as long as both the client and server are somehow "aligned" on the encoding being used it will work. In this context, one might also need to consider implementing the `filename-charset` , `filename-translation-control` extensions as described in [DRAFT 13 - section 6](https://tools.ietf.or [...]
+
### SFTP aware directory scanners
The framework provides special SFTP aware directory scanners that look for files/folders matching specific patterns. The
@@ -484,13 +486,14 @@ Both client and server support several of the SFTP extensions specified in vario
* `check-file-handle`, `check-file-name` - [DRAFT 09 - section 9.1.2](http://tools.ietf.org/wg/secsh/draft-ietf-secsh-filexfer/draft-ietf-secsh-filexfer-09.txt)
* `copy-file`, `copy-data` - [DRAFT 00 - sections 6, 7](http://tools.ietf.org/id/draft-ietf-secsh-filexfer-extensions-00.txt)
* `space-available` - [DRAFT 09 - section 9.3](http://tools.ietf.org/wg/secsh/draft-ietf-secsh-filexfer/draft-ietf-secsh-filexfer-09.txt)
+* `filename-charset`, `filename-translation-control` - [DRAFT 13 - section 6](https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-6) - only client side
Furthermore several [OpenSSH SFTP extensions](https://github.com/openssh/openssh-portable/blob/master/PROTOCOL) are also supported:
* `fsync@openssh.com`
* `fstatvfs@openssh.com`
* `hardlink@openssh.com`
-* `posix-rename@openssh.com`
+* `posix-rename@openssh.com` - only client side
* `statvfs@openssh.com`
* `lsetstat@openssh.com`
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 926fb2c..a7f0c75 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
@@ -1134,30 +1134,38 @@ public abstract class AbstractSftpSubsystemHelper
protected void doReadLink(Buffer buffer, int id) throws IOException {
String path = buffer.getString();
- String l;
+ Map.Entry<Path, String> link;
try {
if (log.isDebugEnabled()) {
log.debug("doReadLink({})[id={}] SSH_FXP_READLINK path={}",
getServerSession(), id, path);
}
- l = doReadLink(id, path);
+ link = doReadLink(id, path);
} catch (IOException | RuntimeException e) {
sendStatus(prepareReply(buffer), id, e, SftpConstants.SSH_FXP_READLINK, path);
return;
}
- sendLink(prepareReply(buffer), id, l);
+ sendLink(prepareReply(buffer), id, link.getKey(), link.getValue());
}
- protected String doReadLink(int id, String path) throws IOException {
+ /**
+ *
+ * @param id Request identifier
+ * @param path Referenced path
+ * @return A "pair" containing the local link {@link Path} and its referenced symbolic link
+ * @throws IOException If failed to resolve the requested data
+ */
+ protected SimpleImmutableEntry<Path, String> doReadLink(int id, String path) throws IOException {
Path link = resolveFile(path);
SftpFileSystemAccessor accessor = getFileSystemAccessor();
- String target = accessor.resolveLinkTarget(getServerSession(), this, link);
+ ServerSession session = getServerSession();
+ String target = accessor.resolveLinkTarget(session, this, link);
if (log.isDebugEnabled()) {
log.debug("doReadLink({})[id={}] path={}[{}]: {}",
- getServerSession(), id, path, link, target);
+ session, id, path, link, target);
}
- return target;
+ return new SimpleImmutableEntry<>(link, target);
}
protected void doRename(Buffer buffer, int id) throws IOException {
@@ -2071,14 +2079,16 @@ public abstract class AbstractSftpSubsystemHelper
send(buffer);
}
- protected void sendLink(Buffer buffer, int id, String link) throws IOException {
+ protected void sendLink(Buffer buffer, int id, Path file, String link) throws IOException {
// in case we are running on Windows
String unixPath = link.replace(File.separatorChar, '/');
+ SftpFileSystemAccessor accessor = getFileSystemAccessor();
+ ServerSession session = getServerSession();
buffer.putByte((byte) SftpConstants.SSH_FXP_NAME);
buffer.putInt(id);
buffer.putInt(1); // one response
- buffer.putString(unixPath);
+ accessor.putRemoteFileName(session, this, file, buffer, unixPath, true);
/*
* As per the spec (https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-6.10):
@@ -2088,11 +2098,12 @@ public abstract class AbstractSftpSubsystemHelper
Map<String, Object> attrs = Collections.emptyMap();
int version = getVersion();
if (version == SftpConstants.SFTP_V3) {
- buffer.putString(SftpHelper.getLongName(unixPath, attrs));
+ String longName = SftpHelper.getLongName(unixPath, attrs);
+ accessor.putRemoteFileName(session, this, file, buffer, longName, false);
}
writeAttrs(buffer, attrs);
- SftpHelper.indicateEndOfNamesList(buffer, getVersion(), getServerSession());
+ SftpHelper.indicateEndOfNamesList(buffer, getVersion(), session);
send(buffer);
}
@@ -2106,15 +2117,18 @@ public abstract class AbstractSftpSubsystemHelper
String originalPath = f.toString();
// in case we are running on Windows
String unixPath = originalPath.replace(File.separatorChar, '/');
- buffer.putString(unixPath);
+ SftpFileSystemAccessor accessor = getFileSystemAccessor();
+ ServerSession session = getServerSession();
+ accessor.putRemoteFileName(session, this, f, buffer, unixPath, true);
int version = getVersion();
if (version == SftpConstants.SFTP_V3) {
- buffer.putString(getLongName(f, getShortName(f), attrs));
+ String longName = getLongName(f, getShortName(f), attrs);
+ accessor.putRemoteFileName(session, this, f, buffer, longName, false);
}
writeAttrs(buffer, attrs);
- SftpHelper.indicateEndOfNamesList(buffer, getVersion(), getServerSession());
+ SftpHelper.indicateEndOfNamesList(buffer, getVersion(), session);
send(buffer);
}
@@ -2174,18 +2188,22 @@ public abstract class AbstractSftpSubsystemHelper
f, SftpConstants.SSH_FILEXFER_ATTR_ALL, options);
entries.put(shortName, f);
- buffer.putString(shortName);
+ SftpFileSystemAccessor accessor = getFileSystemAccessor();
+ ServerSession session = getServerSession();
+ accessor.putRemoteFileName(session, this, f, buffer, shortName, true);
+
int version = getVersion();
if (version == SftpConstants.SFTP_V3) {
String longName = getLongName(f, shortName, options);
- buffer.putString(longName);
+ accessor.putRemoteFileName(session, this, f, buffer, longName, false);
+
if (log.isTraceEnabled()) {
- log.trace("writeDirEntry(" + getServerSession() + ") id=" + id + ")[" + index + "] - "
+ log.trace("writeDirEntry(" + session + ") id=" + id + ")[" + index + "] - "
+ shortName + " [" + longName + "]: " + attrs);
}
} else {
if (log.isTraceEnabled()) {
- log.trace("writeDirEntry(" + getServerSession() + "(id=" + id + ")[" + index + "] - "
+ log.trace("writeDirEntry(" + session + "(id=" + id + ")[" + index + "] - "
+ shortName + ": " + attrs);
}
}
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpFileSystemAccessor.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpFileSystemAccessor.java
index 39f5137..0d2d3ce 100644
--- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpFileSystemAccessor.java
+++ b/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpFileSystemAccessor.java
@@ -56,6 +56,7 @@ import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.MapEntryUtils.NavigableMapBuilder;
import org.apache.sshd.common.util.OsUtils;
import org.apache.sshd.common.util.SelectorUtils;
+import org.apache.sshd.common.util.buffer.Buffer;
import org.apache.sshd.common.util.io.FileInfoExtractor;
import org.apache.sshd.common.util.io.IoUtils;
import org.apache.sshd.server.session.ServerSession;
@@ -117,6 +118,25 @@ public interface SftpFileSystemAccessor {
}
/**
+ * Invoked in order to encode the outgoing referenced file name/path
+ *
+ * @param session The {@link ServerSession} through which the request was received
+ * @param subsystem The SFTP subsystem instance that manages the session
+ * @param path The associated file {@link Path} - <B>Note:</B> might be a symbolic link container
+ * @param buf The target {@link Buffer} for the encoded string
+ * @param name The string to send
+ * @param shortName If {@code true} then this is the "pure" file name/path, otherwise it also contains
+ * user/group/size/last-modified-time/etc.
+ * @throws IOException If failed to resolve the remote name
+ * @see <A HREF="https://issues.apache.org/jira/browse/SSHD-1132">SSHD-1132</A>
+ */
+ default void putRemoteFileName(
+ ServerSession session, SftpSubsystemProxy subsystem, Path path, Buffer buf, String name, boolean shortName)
+ throws IOException {
+ buf.putString(name);
+ }
+
+ /**
* Called whenever a new file is opened
*
* @param session The {@link ServerSession} through which the request was received