From 7ebf842bae97fbbd2614de9c1a74e87aa030644e Mon Sep 17 00:00:00 2001 From: Lee Painter Date: Wed, 3 Apr 2024 10:56:58 +0100 Subject: [PATCH 01/51] Bumped version for development --- maverick-base-tests/pom.xml | 2 +- maverick-base/pom.xml | 2 +- maverick-bc-fips-tests/pom.xml | 2 +- maverick-bc-fips/pom.xml | 2 +- maverick-bc-tests/pom.xml | 2 +- maverick-bc/pom.xml | 2 +- maverick-logging/pom.xml | 2 +- maverick-putty/pom.xml | 2 +- maverick-sshagent-jdk16-sockets/pom.xml | 2 +- maverick-sshagent-jni-sockets/pom.xml | 2 +- maverick-sshagent-named-pipes/pom.xml | 2 +- maverick-sshagent/pom.xml | 2 +- maverick-synergy-assembly/pom.xml | 2 +- maverick-synergy-callback-client/pom.xml | 2 +- maverick-synergy-callback-server/pom.xml | 2 +- maverick-synergy-client-tests/pom.xml | 2 +- maverick-synergy-client/pom.xml | 2 +- maverick-synergy-common-tests/pom.xml | 2 +- maverick-synergy-common/pom.xml | 2 +- maverick-synergy-jdk16-client/pom.xml | 2 +- maverick-synergy-jdk16-common/pom.xml | 2 +- maverick-synergy-jdk16-server/pom.xml | 2 +- maverick-synergy-s3/pom.xml | 4 ++-- maverick-synergy-server/pom.xml | 2 +- maverick-synergy/pom.xml | 2 +- maverick-utils/pom.xml | 2 +- maverick-virtual-filesystem-tests/pom.xml | 2 +- maverick-virtual-filesystem/pom.xml | 2 +- maverick-virtual-session-tests/pom.xml | 2 +- maverick-virtual-session/pom.xml | 2 +- maverick-x509/pom.xml | 2 +- pom.xml | 2 +- 32 files changed, 33 insertions(+), 33 deletions(-) diff --git a/maverick-base-tests/pom.xml b/maverick-base-tests/pom.xml index 5f472ec3..3d245b3a 100644 --- a/maverick-base-tests/pom.xml +++ b/maverick-base-tests/pom.xml @@ -3,7 +3,7 @@ com.sshtools maverick-synergy-group - 3.1.1 + 3.1.2-SNAPSHOT jar maverick-base-tests diff --git a/maverick-base/pom.xml b/maverick-base/pom.xml index 0eb3a479..3ffd1553 100644 --- a/maverick-base/pom.xml +++ b/maverick-base/pom.xml @@ -5,7 +5,7 @@ com.sshtools maverick-synergy-group - 3.1.1 + 3.1.2-SNAPSHOT maverick-base Base API diff --git a/maverick-bc-fips-tests/pom.xml b/maverick-bc-fips-tests/pom.xml index 06e7767b..c59f8d5d 100644 --- a/maverick-bc-fips-tests/pom.xml +++ b/maverick-bc-fips-tests/pom.xml @@ -3,7 +3,7 @@ com.sshtools maverick-synergy-group - 3.1.1 + 3.1.2-SNAPSHOT maverick-bc-fips-tests BouncyCastle FIPS Tests diff --git a/maverick-bc-fips/pom.xml b/maverick-bc-fips/pom.xml index ccc9a60f..87d9ed46 100644 --- a/maverick-bc-fips/pom.xml +++ b/maverick-bc-fips/pom.xml @@ -6,7 +6,7 @@ com.sshtools maverick-synergy-group - 3.1.1 + 3.1.2-SNAPSHOT maverick-bc-fips BouncyCastle FIPS diff --git a/maverick-bc-tests/pom.xml b/maverick-bc-tests/pom.xml index 95cdd7e3..bba147f0 100644 --- a/maverick-bc-tests/pom.xml +++ b/maverick-bc-tests/pom.xml @@ -3,7 +3,7 @@ com.sshtools maverick-synergy-group - 3.1.1 + 3.1.2-SNAPSHOT maverick-bc-tests BouncyCastle JCE Support Tests diff --git a/maverick-bc/pom.xml b/maverick-bc/pom.xml index f932df7d..358e350e 100644 --- a/maverick-bc/pom.xml +++ b/maverick-bc/pom.xml @@ -6,7 +6,7 @@ com.sshtools maverick-synergy-group - 3.1.1 + 3.1.2-SNAPSHOT maverick-bc BouncyCastle JCE Support diff --git a/maverick-logging/pom.xml b/maverick-logging/pom.xml index 05f23a2b..9f00a090 100644 --- a/maverick-logging/pom.xml +++ b/maverick-logging/pom.xml @@ -5,7 +5,7 @@ com.sshtools maverick-synergy-group - 3.1.1 + 3.1.2-SNAPSHOT maverick-logging Logging API diff --git a/maverick-putty/pom.xml b/maverick-putty/pom.xml index 1b311c27..e0f6ef04 100644 --- a/maverick-putty/pom.xml +++ b/maverick-putty/pom.xml @@ -6,7 +6,7 @@ com.sshtools maverick-synergy-group - 3.1.1 + 3.1.2-SNAPSHOT maverick-putty PuTTY Key Support diff --git a/maverick-sshagent-jdk16-sockets/pom.xml b/maverick-sshagent-jdk16-sockets/pom.xml index e037ca8d..eaceab1b 100644 --- a/maverick-sshagent-jdk16-sockets/pom.xml +++ b/maverick-sshagent-jdk16-sockets/pom.xml @@ -3,7 +3,7 @@ com.sshtools maverick-synergy-group - 3.1.1 + 3.1.2-SNAPSHOT maverick-sshagent-jdk16-sockets JDK16+ Unix Domain Socket Agent Provider diff --git a/maverick-sshagent-jni-sockets/pom.xml b/maverick-sshagent-jni-sockets/pom.xml index f78fcd82..ce7e6d8c 100644 --- a/maverick-sshagent-jni-sockets/pom.xml +++ b/maverick-sshagent-jni-sockets/pom.xml @@ -4,7 +4,7 @@ com.sshtools maverick-synergy-group - 3.1.1 + 3.1.2-SNAPSHOT maverick-sshagent-jni-sockets JNI Unix Domain Sockets Key Agent Provider diff --git a/maverick-sshagent-named-pipes/pom.xml b/maverick-sshagent-named-pipes/pom.xml index 3499d4cc..398441c5 100644 --- a/maverick-sshagent-named-pipes/pom.xml +++ b/maverick-sshagent-named-pipes/pom.xml @@ -3,7 +3,7 @@ com.sshtools maverick-synergy-group - 3.1.1 + 3.1.2-SNAPSHOT maverick-sshagent-named-pipes Named Pipes Key Agent Provider diff --git a/maverick-sshagent/pom.xml b/maverick-sshagent/pom.xml index e36be81e..e50617bc 100644 --- a/maverick-sshagent/pom.xml +++ b/maverick-sshagent/pom.xml @@ -4,7 +4,7 @@ com.sshtools maverick-synergy-group - 3.1.1 + 3.1.2-SNAPSHOT maverick-sshagent Key Agent diff --git a/maverick-synergy-assembly/pom.xml b/maverick-synergy-assembly/pom.xml index 52a104b2..e92a9e55 100644 --- a/maverick-synergy-assembly/pom.xml +++ b/maverick-synergy-assembly/pom.xml @@ -4,7 +4,7 @@ com.sshtools maverick-synergy-group - 3.1.1 + 3.1.2-SNAPSHOT maverick-synergy-assembly diff --git a/maverick-synergy-callback-client/pom.xml b/maverick-synergy-callback-client/pom.xml index e6dc7248..7cb9edda 100644 --- a/maverick-synergy-callback-client/pom.xml +++ b/maverick-synergy-callback-client/pom.xml @@ -4,7 +4,7 @@ com.sshtools maverick-synergy-group - 3.1.1 + 3.1.2-SNAPSHOT maverick-synergy-callback-client Callback Client API diff --git a/maverick-synergy-callback-server/pom.xml b/maverick-synergy-callback-server/pom.xml index 34109641..4c6383af 100644 --- a/maverick-synergy-callback-server/pom.xml +++ b/maverick-synergy-callback-server/pom.xml @@ -4,7 +4,7 @@ com.sshtools maverick-synergy-group - 3.1.1 + 3.1.2-SNAPSHOT maverick-synergy-callback-server Callback Server API diff --git a/maverick-synergy-client-tests/pom.xml b/maverick-synergy-client-tests/pom.xml index f62110f3..d3f69cbf 100644 --- a/maverick-synergy-client-tests/pom.xml +++ b/maverick-synergy-client-tests/pom.xml @@ -3,7 +3,7 @@ com.sshtools maverick-synergy-group - 3.1.1 + 3.1.2-SNAPSHOT maverick-synergy-client-tests Client API Tests diff --git a/maverick-synergy-client/pom.xml b/maverick-synergy-client/pom.xml index a71cc628..46f00ce6 100644 --- a/maverick-synergy-client/pom.xml +++ b/maverick-synergy-client/pom.xml @@ -6,7 +6,7 @@ com.sshtools maverick-synergy-group - 3.1.1 + 3.1.2-SNAPSHOT maverick-synergy-client Client API diff --git a/maverick-synergy-common-tests/pom.xml b/maverick-synergy-common-tests/pom.xml index e064a98e..b12cef04 100644 --- a/maverick-synergy-common-tests/pom.xml +++ b/maverick-synergy-common-tests/pom.xml @@ -3,7 +3,7 @@ com.sshtools maverick-synergy-group - 3.1.1 + 3.1.2-SNAPSHOT maverick-synergy-common-tests Common API Tests diff --git a/maverick-synergy-common/pom.xml b/maverick-synergy-common/pom.xml index faeaad2f..8dc69257 100644 --- a/maverick-synergy-common/pom.xml +++ b/maverick-synergy-common/pom.xml @@ -5,7 +5,7 @@ com.sshtools maverick-synergy-group - 3.1.1 + 3.1.2-SNAPSHOT maverick-synergy-common Common API diff --git a/maverick-synergy-jdk16-client/pom.xml b/maverick-synergy-jdk16-client/pom.xml index c46815a3..4c9af744 100644 --- a/maverick-synergy-jdk16-client/pom.xml +++ b/maverick-synergy-jdk16-client/pom.xml @@ -5,7 +5,7 @@ com.sshtools maverick-synergy-group - 3.1.1 + 3.1.2-SNAPSHOT maverick-synergy-jdk16-client Client implementation Unix Domain Socket forwarding diff --git a/maverick-synergy-jdk16-common/pom.xml b/maverick-synergy-jdk16-common/pom.xml index 059f153e..5df38d2c 100644 --- a/maverick-synergy-jdk16-common/pom.xml +++ b/maverick-synergy-jdk16-common/pom.xml @@ -5,7 +5,7 @@ com.sshtools maverick-synergy-group - 3.1.1 + 3.1.2-SNAPSHOT maverick-synergy-jdk16-common Common code for Unix Domain Socket support diff --git a/maverick-synergy-jdk16-server/pom.xml b/maverick-synergy-jdk16-server/pom.xml index cf240210..3a77389e 100644 --- a/maverick-synergy-jdk16-server/pom.xml +++ b/maverick-synergy-jdk16-server/pom.xml @@ -5,7 +5,7 @@ com.sshtools maverick-synergy-group - 3.1.1 + 3.1.2-SNAPSHOT maverick-synergy-jdk16-server Server implementation Unix Domain Socket forwarding diff --git a/maverick-synergy-s3/pom.xml b/maverick-synergy-s3/pom.xml index 394c950e..fe458f9d 100644 --- a/maverick-synergy-s3/pom.xml +++ b/maverick-synergy-s3/pom.xml @@ -3,7 +3,7 @@ com.sshtools maverick-synergy-group - 3.1.1 + 3.1.2-SNAPSHOT maverick-synergy-s3 S3 File System @@ -12,7 +12,7 @@ com.sshtools maverick-base - 3.1.1 + 3.1.2-SNAPSHOT diff --git a/maverick-synergy-server/pom.xml b/maverick-synergy-server/pom.xml index 552d7615..b7ad43f5 100644 --- a/maverick-synergy-server/pom.xml +++ b/maverick-synergy-server/pom.xml @@ -4,7 +4,7 @@ com.sshtools maverick-synergy-group - 3.1.1 + 3.1.2-SNAPSHOT maverick-synergy-server Server API diff --git a/maverick-synergy/pom.xml b/maverick-synergy/pom.xml index 797f877a..4161b6be 100644 --- a/maverick-synergy/pom.xml +++ b/maverick-synergy/pom.xml @@ -6,7 +6,7 @@ com.sshtools maverick-synergy-group - 3.1.1 + 3.1.2-SNAPSHOT maverick-synergy Unified API diff --git a/maverick-utils/pom.xml b/maverick-utils/pom.xml index 7657da5f..d14ddc5c 100644 --- a/maverick-utils/pom.xml +++ b/maverick-utils/pom.xml @@ -5,7 +5,7 @@ com.sshtools maverick-synergy-group - 3.1.1 + 3.1.2-SNAPSHOT maverick-utils Utils diff --git a/maverick-virtual-filesystem-tests/pom.xml b/maverick-virtual-filesystem-tests/pom.xml index 0d21deba..6330a269 100644 --- a/maverick-virtual-filesystem-tests/pom.xml +++ b/maverick-virtual-filesystem-tests/pom.xml @@ -3,7 +3,7 @@ com.sshtools maverick-synergy-group - 3.1.1 + 3.1.2-SNAPSHOT maverick-virtual-filesystem-tests Virtual File System Tests diff --git a/maverick-virtual-filesystem/pom.xml b/maverick-virtual-filesystem/pom.xml index 85a8fe53..69f1a32f 100644 --- a/maverick-virtual-filesystem/pom.xml +++ b/maverick-virtual-filesystem/pom.xml @@ -4,7 +4,7 @@ com.sshtools maverick-synergy-group - 3.1.1 + 3.1.2-SNAPSHOT maverick-virtual-filesystem Virtual File System diff --git a/maverick-virtual-session-tests/pom.xml b/maverick-virtual-session-tests/pom.xml index c1f0a8d3..32d75c11 100644 --- a/maverick-virtual-session-tests/pom.xml +++ b/maverick-virtual-session-tests/pom.xml @@ -4,7 +4,7 @@ com.sshtools maverick-synergy-group - 3.1.1 + 3.1.2-SNAPSHOT maverick-virtual-session-tests Virtual Sessions Tests diff --git a/maverick-virtual-session/pom.xml b/maverick-virtual-session/pom.xml index ce782c45..aca84fef 100644 --- a/maverick-virtual-session/pom.xml +++ b/maverick-virtual-session/pom.xml @@ -7,7 +7,7 @@ com.sshtools maverick-synergy-group - 3.1.1 + 3.1.2-SNAPSHOT maverick-virtual-session Virtual Sessions diff --git a/maverick-x509/pom.xml b/maverick-x509/pom.xml index 3964b1f5..720b1728 100644 --- a/maverick-x509/pom.xml +++ b/maverick-x509/pom.xml @@ -5,7 +5,7 @@ com.sshtools maverick-synergy-group - 3.1.1 + 3.1.2-SNAPSHOT maverick-x509 X509 Certificate Support diff --git a/pom.xml b/pom.xml index e2657c0a..767a6fbd 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 com.sshtools maverick-synergy-group - 3.1.1 + 3.1.2-SNAPSHOT Maverick Synergy Open source Java SSH API http://www.jadaptive.com From 0b4fb017f37c2f16be9403eb02349bde597e7e08 Mon Sep 17 00:00:00 2001 From: Lee Painter Date: Wed, 3 Apr 2024 11:27:38 +0100 Subject: [PATCH 02/51] SSH Client Put File Throws No connection or client supplied --- .../java/com/sshtools/client/tasks/AbstractConnectionTask.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maverick-synergy-client/src/main/java/com/sshtools/client/tasks/AbstractConnectionTask.java b/maverick-synergy-client/src/main/java/com/sshtools/client/tasks/AbstractConnectionTask.java index ac2ae567..b1e4231a 100644 --- a/maverick-synergy-client/src/main/java/com/sshtools/client/tasks/AbstractConnectionTask.java +++ b/maverick-synergy-client/src/main/java/com/sshtools/client/tasks/AbstractConnectionTask.java @@ -81,7 +81,7 @@ public B withClients(Function clientSupplier) { protected final Optional> clientSupplier; protected AbstractConnectionTask(AbstractConnectionTaskBuilder builder) { - super(builder.connection.orElse(builder.clientSupplier.orElseThrow(() -> new IllegalArgumentException("No connection or client supplied.")).apply(0).getConnection())); + super(builder.connection.orElseGet(() -> builder.clientSupplier.orElseThrow(() -> new IllegalArgumentException("No connection or client supplied.")).apply(0).getConnection())); this.clientSupplier = builder.clientSupplier; } From f2d164deac186a2f0ae639a2441e2479991188a9 Mon Sep 17 00:00:00 2001 From: Lee Painter Date: Wed, 3 Apr 2024 11:28:11 +0100 Subject: [PATCH 03/51] Updated change log --- maverick-synergy-assembly/notes/CHANGES | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/maverick-synergy-assembly/notes/CHANGES b/maverick-synergy-assembly/notes/CHANGES index ef7ce2a0..5d96e20c 100644 --- a/maverick-synergy-assembly/notes/CHANGES +++ b/maverick-synergy-assembly/notes/CHANGES @@ -1,12 +1,19 @@ -Maverick Synergy 3.1.1 - 3 Apr, 2024 +Maverick Synergy 3.1.2 - TBC Bug Fixes - o Fixed issue with Terrapin strict kex mode and messages after SSH_MSG_NEWKEYS. + o SshClient put file throws No connection or client supplied. ================= = HISTORY = ================= +Maverick Synergy 3.1.1 - 3 Apr, 2024 + +Bug Fixes + o Fixed issue with Terrapin strict kex mode and messages after SSH_MSG_NEWKEYS. + +----------------- + Maverick Synergy 3.1.0 - Feb 20, 2024 Features From 190429de03c218d8dca4ae4082d6b6d8e028b067 Mon Sep 17 00:00:00 2001 From: Lee Painter Date: Wed, 3 Apr 2024 11:44:56 +0100 Subject: [PATCH 04/51] Added maverick.disableSlashRemoval system switch to disable processing of paths by removing trailing slash. --- .../src/main/java/com/sshtools/client/sftp/SftpClient.java | 2 +- .../src/main/java/com/sshtools/client/sftp/SftpFile.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpClient.java b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpClient.java index 6b5361c6..c1f760bd 100644 --- a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpClient.java +++ b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpClient.java @@ -770,7 +770,7 @@ private String resolveRemotePath(String path) throws SftpStatusException, SshExc actual = path; } - if (!actual.equals("/") && actual.endsWith("/")) { + if (!Boolean.getBoolean("maverick.disableSlashRemoval") && !actual.equals("/") && actual.endsWith("/")) { return actual.substring(0, actual.length() - 1); } else { return actual; diff --git a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpFile.java b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpFile.java index 16a87773..3867f0f3 100644 --- a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpFile.java +++ b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpFile.java @@ -58,7 +58,7 @@ public final class SftpFile { } else { //Remove trailing forward slash - if (absolutePath.endsWith("/")) { + if (!Boolean.getBoolean("maverick.disableSlashRemoval") && absolutePath.endsWith("/")) { absolutePath = absolutePath.substring(0, absolutePath.length() - 1); } From cfcbed2d2234b12a5034db7ad9b3931bd2bef931 Mon Sep 17 00:00:00 2001 From: Brett Smith Date: Fri, 5 Apr 2024 16:08:25 +0100 Subject: [PATCH 05/51] Fixes regression found in callback service where `osshell` would fail to send keyboard input. * ByteBuffer passed to channel event listener is now a read only wrapper with independent positions. * Raw mode deprecated and replaced with setAutoConsume which will be turned on for osshell * When auto consuming, dont cache the buffer. --- .../java/com/sshtools/common/ssh/Channel.java | 2 ++ .../com/sshtools/synergy/ssh/ChannelNG.java | 22 ++++++++++--- .../com/sshtools/server/SessionChannelNG.java | 4 ++- .../server/vsession/VirtualShellNG.java | 32 ++----------------- .../commands/os/AbstractOSCommand.java | 14 +++++--- 5 files changed, 35 insertions(+), 39 deletions(-) diff --git a/maverick-base/src/main/java/com/sshtools/common/ssh/Channel.java b/maverick-base/src/main/java/com/sshtools/common/ssh/Channel.java index 259fed27..df3a9522 100644 --- a/maverick-base/src/main/java/com/sshtools/common/ssh/Channel.java +++ b/maverick-base/src/main/java/com/sshtools/common/ssh/Channel.java @@ -44,6 +44,8 @@ public interface Channel { void addEventListener(ChannelEventListener listener); + void removeEventListener(ChannelEventListener listener); + void sendChannelRequest(String requestName, boolean wantReply, byte[] data); void sendChannelRequest(String type, boolean wantreply, diff --git a/maverick-synergy-common/src/main/java/com/sshtools/synergy/ssh/ChannelNG.java b/maverick-synergy-common/src/main/java/com/sshtools/synergy/ssh/ChannelNG.java index 8732a05b..8a97d7b4 100644 --- a/maverick-synergy-common/src/main/java/com/sshtools/synergy/ssh/ChannelNG.java +++ b/maverick-synergy-common/src/main/java/com/sshtools/synergy/ssh/ChannelNG.java @@ -98,9 +98,8 @@ public abstract class ChannelNG implements Channel { protected SshConnection con; private ChannelInputStream channelIn; private ChannelOutputStream channelOut = new ChannelOutputStream(this); - private final boolean autoConsume; + private boolean autoConsume; - @Deprecated(forRemoval = true, since = "3.1.0") public ChannelNG(String channelType, int maximumPacketSize, int initialWindowSize, int maximumWindowSpace, int minimumWindowSpace, ChannelRequestFuture closeFuture, boolean autoConsume) { this(channelType, maximumPacketSize, new UnsignedInteger32(initialWindowSize), new UnsignedInteger32(maximumWindowSpace), new UnsignedInteger32(minimumWindowSpace), closeFuture, autoConsume); @@ -142,6 +141,10 @@ public final boolean isAutoConsume() { return autoConsume; } + public void setAutoconsume(boolean autoConsume) { + this.autoConsume = autoConsume; + } + public InputStream getInputStream() { if(Objects.nonNull(channelIn)) { return channelIn; @@ -242,6 +245,17 @@ public void addEventListener(ChannelEventListener listener) { } } + /** + * Stop listening for channel events + * + * @param listener + */ + public void removeEventListener(ChannelEventListener listener) { + if (listener != null) { + eventListeners.remove(listener); + } + } + /** * The name of this channel. * @@ -445,9 +459,9 @@ void consumeWindowSpace(int length) throws IOException { protected void onChannelData(ByteBuffer data) { for (ChannelEventListener listener : eventListeners) { - listener.onChannelDataIn(this, data); + listener.onChannelDataIn(this, data.asReadOnlyBuffer()); } - if(Objects.nonNull(cache)) { + if(!autoConsume && Objects.nonNull(cache)) { try { cache.put(data); } catch (EOFException e) { diff --git a/maverick-synergy-server/src/main/java/com/sshtools/server/SessionChannelNG.java b/maverick-synergy-server/src/main/java/com/sshtools/server/SessionChannelNG.java index 01e8d889..f8960fd4 100644 --- a/maverick-synergy-server/src/main/java/com/sshtools/server/SessionChannelNG.java +++ b/maverick-synergy-server/src/main/java/com/sshtools/server/SessionChannelNG.java @@ -141,10 +141,12 @@ protected void onChannelClosed() { } } + @Deprecated public void enableRawMode() { rawMode = true; } - + + @Deprecated public void disableRawMode() { rawMode = false; } diff --git a/maverick-virtual-session/src/main/java/com/sshtools/server/vsession/VirtualShellNG.java b/maverick-virtual-session/src/main/java/com/sshtools/server/vsession/VirtualShellNG.java index 1a06a346..541b59d1 100644 --- a/maverick-virtual-session/src/main/java/com/sshtools/server/vsession/VirtualShellNG.java +++ b/maverick-virtual-session/src/main/java/com/sshtools/server/vsession/VirtualShellNG.java @@ -24,7 +24,6 @@ import java.io.IOException; import java.lang.reflect.InvocationTargetException; -import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.nio.file.FileSystem; import java.nio.file.FileSystems; @@ -47,15 +46,11 @@ import org.jline.terminal.Size; import org.jline.terminal.Terminal; import org.jline.terminal.TerminalBuilder; -import org.jline.terminal.impl.AbstractPosixTerminal; -import org.jline.terminal.impl.ExternalTerminal; import com.sshtools.common.files.nio.AbstractFileURI; import com.sshtools.common.logger.Log; import com.sshtools.common.permissions.PermissionDeniedException; import com.sshtools.common.policy.ClassLoaderPolicy; -import com.sshtools.common.ssh.Channel; -import com.sshtools.common.ssh.ChannelEventListener; import com.sshtools.common.ssh.SshConnection; import com.sshtools.common.util.Utils; import com.sshtools.server.AgentForwardingChannel; @@ -82,8 +77,6 @@ public interface WindowSizeChangeListener { protected VirtualConsole console; protected ShellCommandFactory commandFactory; - boolean rawMode = false; - List listeners = new ArrayList(); private Terminal terminal; @@ -91,28 +84,6 @@ public VirtualShellNG(SshConnection con, ShellCommandFactory commandFactory) { super(con); this.commandFactory = commandFactory; - addEventListener(new ChannelEventListener() { - @Override - public void onChannelDataIn(Channel channel, ByteBuffer data) { - - byte[] tmp = new byte[data.remaining()]; - data.get(tmp); - - try { - if(terminal instanceof AbstractPosixTerminal) { - ((AbstractPosixTerminal)terminal).getPty().getMasterOutput().write(tmp); - ((AbstractPosixTerminal)terminal).getPty().getMasterOutput().flush(); - } - else { - ((ExternalTerminal)terminal).processInputBytes(tmp, 0, tmp.length); - } - evaluateWindowSpace(); - } catch (Exception e) { - Log.error("Failed to send input to terminal.", e); - close(); - } - } - }); } public void addWindowSizeChangeListener(WindowSizeChangeListener listener) { @@ -252,13 +223,14 @@ protected void onLocalEOF() { @Override public void enableRawMode() { console.getTerminal().pause(); + setAutoconsume(true); super.enableRawMode(); - } @Override public void disableRawMode() { console.getTerminal().resume(); + setAutoconsume(false); super.disableRawMode(); } diff --git a/maverick-virtual-session/src/main/java/com/sshtools/server/vsession/commands/os/AbstractOSCommand.java b/maverick-virtual-session/src/main/java/com/sshtools/server/vsession/commands/os/AbstractOSCommand.java index ab582c6b..e97c42ed 100644 --- a/maverick-virtual-session/src/main/java/com/sshtools/server/vsession/commands/os/AbstractOSCommand.java +++ b/maverick-virtual-session/src/main/java/com/sshtools/server/vsession/commands/os/AbstractOSCommand.java @@ -121,11 +121,11 @@ public void newSize(int rows, int cols) { console.getSessionChannel().enableRawMode(); - console.getSessionChannel().addEventListener(new ChannelEventListener() { + ChannelEventListener l = new ChannelEventListener() { @Override public void onChannelDataIn(Channel channel, ByteBuffer buffer) { - + byte[] tmp = new byte[buffer.remaining()]; buffer.get(tmp); @@ -138,7 +138,8 @@ public void onChannelDataIn(Channel channel, ByteBuffer buffer) { IOUtils.closeStream(in); } } - }); + }; + console.getSessionChannel().addEventListener(l); try { IOUtils.copy(in, console.getSessionChannel().getOutputStream()); @@ -150,7 +151,12 @@ public void onChannelDataIn(Channel channel, ByteBuffer buffer) { } } catch (Exception e) { } finally { - console.getSessionChannel().disableRawMode(); + try { + console.getSessionChannel().disableRawMode(); + } + finally { + console.getSessionChannel().removeEventListener(l); + } } } From 6795bd683f9c299ae15a74778d49faf46eca99fe Mon Sep 17 00:00:00 2001 From: Brett Smith Date: Tue, 30 Apr 2024 14:34:13 +0100 Subject: [PATCH 06/51] Still getting NPE on root paths. --- .../src/main/java/com/sshtools/common/files/direct/NioFile.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maverick-base/src/main/java/com/sshtools/common/files/direct/NioFile.java b/maverick-base/src/main/java/com/sshtools/common/files/direct/NioFile.java index 8ce7ea92..b401c313 100644 --- a/maverick-base/src/main/java/com/sshtools/common/files/direct/NioFile.java +++ b/maverick-base/src/main/java/com/sshtools/common/files/direct/NioFile.java @@ -452,7 +452,7 @@ protected SftpFileAttributes doGetAttributes() throws FileNotFoundException, IOE permsBldr.withAllWrite(); } var filename = path.getFileName(); - if (filename != null && filename.toString().endsWith(".exe") || filename.toString().endsWith(".com") || filename.toString().endsWith(".cmd")) { + if (filename != null && ( filename.toString().endsWith(".exe") || filename.toString().endsWith(".com") || filename.toString().endsWith(".cmd"))) { permsBldr.withAllExecute(); } bldr.withPermissions(permsBldr.build()); From 977256327afb65530be4f22d61d21f0e05cecd98 Mon Sep 17 00:00:00 2001 From: Lee Painter Date: Wed, 1 May 2024 18:16:40 +0100 Subject: [PATCH 07/51] Changed default public key preference to ed25519 from ecdsa --- .../src/main/java/com/sshtools/synergy/ssh/SshContext.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/maverick-synergy-common/src/main/java/com/sshtools/synergy/ssh/SshContext.java b/maverick-synergy-common/src/main/java/com/sshtools/synergy/ssh/SshContext.java index 5c57b033..f3798052 100644 --- a/maverick-synergy-common/src/main/java/com/sshtools/synergy/ssh/SshContext.java +++ b/maverick-synergy-common/src/main/java/com/sshtools/synergy/ssh/SshContext.java @@ -165,6 +165,7 @@ public abstract class SshContext extends ProtocolContext implements public static final String KEX_DIFFIE_HELLMAN_ECDH_NISTP_521 = "ecdh-sha2-nistp521"; /** SSH2 DSA Public Key **/ + @Deprecated public static final String PUBLIC_KEY_SSHDSS = "ssh-dss"; /** ED25519 Public key */ @@ -226,7 +227,8 @@ public abstract class SshContext extends ProtocolContext implements protected String prefCompressionSC = COMPRESSION_NONE; protected String prefKeyExchange = KEX_DIFFIE_HELLMAN_GROUP_EXCHANGE_SHA256; - protected String prefPublicKey = PUBLIC_KEY_ECDSA_SHA2_NISPTP_256; + + protected String prefPublicKey = PUBLIC_KEY_ED25519; protected int maxChannels = 100; From 59b5e734f7a4a9b12a84defa61d8d9901fc6be63 Mon Sep 17 00:00:00 2001 From: Lee Painter Date: Wed, 1 May 2024 18:17:48 +0100 Subject: [PATCH 08/51] Allow user information on a mount to be provided by VirtualMountTemplate. --- .../common/files/vfs/AbstractMount.java | 22 ++++++++++++++++++- .../common/files/vfs/VirtualMount.java | 5 +++++ .../common/files/vfs/VirtualMountFile.java | 8 +++---- .../files/vfs/VirtualMountTemplate.java | 20 +++++++++++++++++ 4 files changed, 50 insertions(+), 5 deletions(-) diff --git a/maverick-virtual-filesystem/src/main/java/com/sshtools/common/files/vfs/AbstractMount.java b/maverick-virtual-filesystem/src/main/java/com/sshtools/common/files/vfs/AbstractMount.java index b8a0c556..274c728d 100644 --- a/maverick-virtual-filesystem/src/main/java/com/sshtools/common/files/vfs/AbstractMount.java +++ b/maverick-virtual-filesystem/src/main/java/com/sshtools/common/files/vfs/AbstractMount.java @@ -51,7 +51,11 @@ public int hashCode() { private boolean filesystemRoot; private boolean isDefault; protected boolean isImaginary; - + protected String username = "unknown"; + protected String group = "unknown"; + protected int uid = 0; + protected int gid = 0; + protected AbstractMount(String mount, String path) { this(mount, path, false, false); } @@ -121,4 +125,20 @@ public void setAttribute(String key, Object value) { public String toString() { return getMount() + " on " + getRoot(); } + + public String getUsername() { + return username; + } + + public String getGroup() { + return group; + } + + public int getUid() { + return uid; + } + + public int getGid() { + return gid; + } } diff --git a/maverick-virtual-filesystem/src/main/java/com/sshtools/common/files/vfs/VirtualMount.java b/maverick-virtual-filesystem/src/main/java/com/sshtools/common/files/vfs/VirtualMount.java index a77699f3..350e118d 100644 --- a/maverick-virtual-filesystem/src/main/java/com/sshtools/common/files/vfs/VirtualMount.java +++ b/maverick-virtual-filesystem/src/main/java/com/sshtools/common/files/vfs/VirtualMount.java @@ -60,6 +60,11 @@ public class VirtualMount extends AbstractMount { AbstractFile f = actualFileFactory.getFile(path); this.path = f.getAbsolutePath(); } + + this.uid = mountTemplate.getUid(); + this.gid = mountTemplate.getGid(); + this.username = mountTemplate.getUsername(); + this.group = mountTemplate.getGroup(); } diff --git a/maverick-virtual-filesystem/src/main/java/com/sshtools/common/files/vfs/VirtualMountFile.java b/maverick-virtual-filesystem/src/main/java/com/sshtools/common/files/vfs/VirtualMountFile.java index b60cb574..d9f8abb7 100644 --- a/maverick-virtual-filesystem/src/main/java/com/sshtools/common/files/vfs/VirtualMountFile.java +++ b/maverick-virtual-filesystem/src/main/java/com/sshtools/common/files/vfs/VirtualMountFile.java @@ -123,10 +123,10 @@ public SftpFileAttributes getAttributes() throws FileNotFoundException, bldr.withPermissions(builder.build()); - bldr.withUid(0); - bldr.withGid(0); - bldr.withUsername(System.getProperty("maverick.unknownUsername", "unknown")); - bldr.withGroup(System.getProperty("maverick.unknownUsername", "unknown")); + bldr.withUid(parentMount.getUid()); + bldr.withGid(parentMount.getGid()); + bldr.withUsername(System.getProperty("maverick.unknownUsername", parentMount.getUsername())); + bldr.withGroup(System.getProperty("maverick.unknownUsername", parentMount.getGroup())); bldr.withLastAccessTime(parentMount.lastModified()); bldr.withLastModifiedTime(parentMount.lastModified()); bldr.withCreateTime(parentMount.lastModified()); diff --git a/maverick-virtual-filesystem/src/main/java/com/sshtools/common/files/vfs/VirtualMountTemplate.java b/maverick-virtual-filesystem/src/main/java/com/sshtools/common/files/vfs/VirtualMountTemplate.java index 8e36fca6..ecd128b5 100644 --- a/maverick-virtual-filesystem/src/main/java/com/sshtools/common/files/vfs/VirtualMountTemplate.java +++ b/maverick-virtual-filesystem/src/main/java/com/sshtools/common/files/vfs/VirtualMountTemplate.java @@ -77,4 +77,24 @@ public boolean isChildOf(VirtualMountTemplate o2) { public long lastModified() { return lastModified; } + + public VirtualMountTemplate setUsername(String username) { + this.username = username; + return this; + } + + public VirtualMountTemplate setGroup(String group) { + this.group = group; + return this; + } + + public VirtualMountTemplate setUid(int uid) { + this.uid = uid; + return this; + } + + public VirtualMountTemplate setGid(int gid) { + this.gid = gid; + return this; + } } From 8ac48120b1f9d6acca99755379f8e88b7792dd3f Mon Sep 17 00:00:00 2001 From: Lee Painter Date: Wed, 1 May 2024 18:18:11 +0100 Subject: [PATCH 09/51] Updated change log. --- maverick-synergy-assembly/notes/CHANGES | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/maverick-synergy-assembly/notes/CHANGES b/maverick-synergy-assembly/notes/CHANGES index 5d96e20c..5cf5f288 100644 --- a/maverick-synergy-assembly/notes/CHANGES +++ b/maverick-synergy-assembly/notes/CHANGES @@ -1,7 +1,8 @@ Maverick Synergy 3.1.2 - TBC Bug Fixes - o SshClient put file throws No connection or client supplied. + o User and group information can now be defaulted on an AbstractMount. + o Changed default public key preference from ecdsa to ed25519. ================= = HISTORY = @@ -9,6 +10,13 @@ Bug Fixes Maverick Synergy 3.1.1 - 3 Apr, 2024 +Bug Fixes + o SshClient put file throws No connection or client supplied. + +----------------- + +Maverick Synergy 3.1.1 - 3 Apr, 2024 + Bug Fixes o Fixed issue with Terrapin strict kex mode and messages after SSH_MSG_NEWKEYS. From 0e2c9c1d293fdeb906675ab566dfeaa9d4e03429 Mon Sep 17 00:00:00 2001 From: Lee Painter Date: Thu, 2 May 2024 08:46:21 +0100 Subject: [PATCH 10/51] Incorrect mount passed to VirtualMount when resolving children. --- .../java/com/sshtools/common/files/vfs/VirtualFileFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maverick-virtual-filesystem/src/main/java/com/sshtools/common/files/vfs/VirtualFileFactory.java b/maverick-virtual-filesystem/src/main/java/com/sshtools/common/files/vfs/VirtualFileFactory.java index e87e7538..851c02bc 100644 --- a/maverick-virtual-filesystem/src/main/java/com/sshtools/common/files/vfs/VirtualFileFactory.java +++ b/maverick-virtual-filesystem/src/main/java/com/sshtools/common/files/vfs/VirtualFileFactory.java @@ -249,7 +249,7 @@ public Map resolveChildren(VirtualFile parent) throws Permis if(intermediate = !childPaths.isEmpty()) { childPath = FileUtils.checkEndsWithNoSlash(childPaths.get(0)); } - files.put(childPath, new VirtualMountFile(currentPath + childPath, parent.getMount(), this, intermediate)); + files.put(childPath, new VirtualMountFile(currentPath + childPath, m, this, intermediate)); } } From 22d26d58e2b1b04e0381eea74a414823cabe872b Mon Sep 17 00:00:00 2001 From: Lee Painter Date: Thu, 2 May 2024 08:47:06 +0100 Subject: [PATCH 11/51] Updated change log. --- maverick-synergy-assembly/notes/CHANGES | 1 + 1 file changed, 1 insertion(+) diff --git a/maverick-synergy-assembly/notes/CHANGES b/maverick-synergy-assembly/notes/CHANGES index 5cf5f288..12be0ee9 100644 --- a/maverick-synergy-assembly/notes/CHANGES +++ b/maverick-synergy-assembly/notes/CHANGES @@ -3,6 +3,7 @@ Maverick Synergy 3.1.2 - TBC Bug Fixes o User and group information can now be defaulted on an AbstractMount. o Changed default public key preference from ecdsa to ed25519. + o Incorrect parent mount passed to VirtualMountFile when resolving children. ================= = HISTORY = From 4fe1e092aecd613a3db9152aa9732b21959546b1 Mon Sep 17 00:00:00 2001 From: Brett Smith Date: Mon, 6 May 2024 12:43:39 +0100 Subject: [PATCH 12/51] `CommandTaskBuilder` missing `withConnection`. --- .../com/sshtools/vsession/commands/ssh/SshClientCommand.java | 1 + 1 file changed, 1 insertion(+) diff --git a/maverick-virtual-session/src/main/java/com/sshtools/vsession/commands/ssh/SshClientCommand.java b/maverick-virtual-session/src/main/java/com/sshtools/vsession/commands/ssh/SshClientCommand.java index ca9bf5ca..00bbf249 100644 --- a/maverick-virtual-session/src/main/java/com/sshtools/vsession/commands/ssh/SshClientCommand.java +++ b/maverick-virtual-session/src/main/java/com/sshtools/vsession/commands/ssh/SshClientCommand.java @@ -66,6 +66,7 @@ public void runCommand(SshClient sshClient, SshClientArguments arguments, Virtua if (CommandUtil.isNotEmpty(arguments.getCommand())) { String command = arguments.getCommand(); task = CommandTaskBuilder.create() + .withConnection(connection) .withCommand(command) .withTermType(console.getTerminal().getType()) .withColumns(console.getTerminal().getWidth()) From 59931fd85307740eee834c95b5d10157afba0af3 Mon Sep 17 00:00:00 2001 From: Lee Painter Date: Mon, 6 May 2024 13:45:15 +0100 Subject: [PATCH 13/51] Attempting simple change that does not use "raw" mode. --- .../com/sshtools/vsession/commands/ssh/SshClientCommand.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/maverick-virtual-session/src/main/java/com/sshtools/vsession/commands/ssh/SshClientCommand.java b/maverick-virtual-session/src/main/java/com/sshtools/vsession/commands/ssh/SshClientCommand.java index 00bbf249..13c4cde4 100644 --- a/maverick-virtual-session/src/main/java/com/sshtools/vsession/commands/ssh/SshClientCommand.java +++ b/maverick-virtual-session/src/main/java/com/sshtools/vsession/commands/ssh/SshClientCommand.java @@ -55,7 +55,7 @@ public SshClientCommand() { @Override public void runCommand(SshClient sshClient, SshClientArguments arguments, VirtualConsole console) { - console.getSessionChannel().enableRawMode(); + //console.getSessionChannel().enableRawMode(); try { Connection connection = sshClient.getConnection(); @@ -106,7 +106,7 @@ public void runCommand(SshClient sshClient, SshClientArguments arguments, Virtua task.waitForever(); } finally { - console.getSessionChannel().disableRawMode(); + //console.getSessionChannel().disableRawMode(); console.println(); } From 1291315f81abadf6db335acc454bd6959d509826 Mon Sep 17 00:00:00 2001 From: Lee Painter Date: Mon, 6 May 2024 19:02:48 +0100 Subject: [PATCH 14/51] Various fixes around child session commands. enableRawMode has been renamed to the more appropriate pauseDataCaching which is actually what we are doing, this stops data being cached in the sessions InputStream, so means child commands that utilize this MUST use channel data listeners (or use the console methods to read/write data). --- .../common/ssh/SessionChannelServer.java | 4 +- .../callback/client/CallbackClient.java | 2 + .../callback/CallbackContextFactory.java | 15 +++---- .../callback/CallbackRegistrationService.java | 2 +- .../server/callback/CallbackServer.java | 13 +++++- .../server/callback/DefaultCallback.java | 5 ++- .../InMemoryCallbackRegistrationService.java | 4 +- .../callback/commands/CallbackShell.java | 4 +- .../com/sshtools/synergy/ssh/ChannelNG.java | 27 +++++++----- .../com/sshtools/server/SessionChannelNG.java | 18 +++----- .../server/vsession/VirtualShellNG.java | 11 +++-- .../commands/os/AbstractOSCommand.java | 4 +- .../commands/ssh/SshClientCommand.java | 44 ++++++++++++++++--- 13 files changed, 98 insertions(+), 55 deletions(-) diff --git a/maverick-base/src/main/java/com/sshtools/common/ssh/SessionChannelServer.java b/maverick-base/src/main/java/com/sshtools/common/ssh/SessionChannelServer.java index 616e26b4..abbd1c6d 100644 --- a/maverick-base/src/main/java/com/sshtools/common/ssh/SessionChannelServer.java +++ b/maverick-base/src/main/java/com/sshtools/common/ssh/SessionChannelServer.java @@ -28,9 +28,9 @@ public interface SessionChannelServer extends SessionChannel { OutputStream getErrorStream(); - void enableRawMode(); + default void pauseDataCaching() { }; - void disableRawMode(); + default void resumeDataCaching() { }; boolean setEnvironmentVariable(String name, String value); } diff --git a/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackClient.java b/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackClient.java index e07593d4..a13974e9 100644 --- a/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackClient.java +++ b/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackClient.java @@ -64,6 +64,7 @@ public class CallbackClient implements ChannelFactoryListener ChannelFactory channelFactory; List defaultPolicies = new ArrayList<>(); FileFactory fileFactory; + String welcomeText = "Callback Client"; public CallbackClient() { executor = getExecutorService(); @@ -229,6 +230,7 @@ public SshServerContext createContext(SshEngineContext daemonContext, CallbackCo sshContext.setIdleConnectionTimeoutSeconds(0); sshContext.setExtendedIdentificationSanitization(false); + for(SshKeyPair key : hostKeys) { sshContext.addHostKey(key); } diff --git a/maverick-synergy-callback-server/src/main/java/com/sshtools/server/callback/CallbackContextFactory.java b/maverick-synergy-callback-server/src/main/java/com/sshtools/server/callback/CallbackContextFactory.java index 18396ab9..de7f8beb 100644 --- a/maverick-synergy-callback-server/src/main/java/com/sshtools/server/callback/CallbackContextFactory.java +++ b/maverick-synergy-callback-server/src/main/java/com/sshtools/server/callback/CallbackContextFactory.java @@ -86,15 +86,13 @@ public SshServerContext createContext(SshEngineContext daemonContext, SocketChan clientContext.addAuthenticator(new MutualCallbackAuthenticator(store)); clientContext.addStateListener(new ClientStateListener() { - @Override - public void connected(SshConnection con) { - Log.info("Callback client {} connected", con.getUsername()); - callbacks.registerCallbackClient(con); - } - @Override public void disconnected(SshConnection con) { - callbacks.unregisterCallbackClient(con.getUUID()); + if(con.getRemoteIdentification().substring(8).startsWith(callbackIdentifier)) { + Log.info("Callback client {} disconnected", con.getUsername()); + callbacks.unregisterCallbackClient(con.getUUID()); + } + } }); @@ -112,8 +110,9 @@ public boolean processGlobalRequest(GlobalRequest request, ConnectionProtocol getDefaultContextFactory() { + addAuthenticator(users); return new CallbackContextFactory(store, callbacks, this); } @@ -98,8 +101,14 @@ public ChannelFactory getChannelFactory() { new SshClientsCommandFactory()); } - public void addAgentKey(String username, SshKeyPair privateKey, SshPublicKey publicKey) { + public CallbackServer addAgentKey(String username, SshKeyPair privateKey, SshPublicKey publicKey) { store.addKey(username, privateKey, publicKey); + return this; + } + + public CallbackServer addUser(String username, String password) { + users.addUser(username, password.toCharArray()); + return this; } } diff --git a/maverick-synergy-callback-server/src/main/java/com/sshtools/server/callback/DefaultCallback.java b/maverick-synergy-callback-server/src/main/java/com/sshtools/server/callback/DefaultCallback.java index dee439cd..5dda55ca 100644 --- a/maverick-synergy-callback-server/src/main/java/com/sshtools/server/callback/DefaultCallback.java +++ b/maverick-synergy-callback-server/src/main/java/com/sshtools/server/callback/DefaultCallback.java @@ -27,10 +27,11 @@ public class DefaultCallback implements Callback { SshConnection con; - String memo = "Undefined"; + String memo; - DefaultCallback(SshConnection con) { + DefaultCallback(SshConnection con, String memo) { this.con = con; + this.memo = memo; } @Override diff --git a/maverick-synergy-callback-server/src/main/java/com/sshtools/server/callback/InMemoryCallbackRegistrationService.java b/maverick-synergy-callback-server/src/main/java/com/sshtools/server/callback/InMemoryCallbackRegistrationService.java index c0223e8b..feb5e1d0 100644 --- a/maverick-synergy-callback-server/src/main/java/com/sshtools/server/callback/InMemoryCallbackRegistrationService.java +++ b/maverick-synergy-callback-server/src/main/java/com/sshtools/server/callback/InMemoryCallbackRegistrationService.java @@ -120,8 +120,8 @@ public Callback getCallbackByUUID(String uuid) { } @Override - public void registerCallbackClient(SshConnection con) { - callbacks.put(con.getUUID(), new DefaultCallback(con)); + public void registerCallbackClient(SshConnection con, String memo) { + callbacks.put(con.getUUID(), new DefaultCallback(con, memo)); } @Override diff --git a/maverick-synergy-callback-server/src/main/java/com/sshtools/server/callback/commands/CallbackShell.java b/maverick-synergy-callback-server/src/main/java/com/sshtools/server/callback/commands/CallbackShell.java index 9757fc44..fd61d05c 100644 --- a/maverick-synergy-callback-server/src/main/java/com/sshtools/server/callback/commands/CallbackShell.java +++ b/maverick-synergy-callback-server/src/main/java/com/sshtools/server/callback/commands/CallbackShell.java @@ -68,7 +68,7 @@ public void run(String[] args, VirtualConsole console) withColumns(console.getTerminal().getWidth()). withRows(console.getTerminal().getHeight()). onBeforeTask((task, session) -> { - console.getSessionChannel().enableRawMode(); + console.getSessionChannel().pauseDataCaching(); listener.session = session; ((VirtualShellNG)console.getSessionChannel()).addWindowSizeChangeListener(listener); con.addTask(Task.ofRunnable(con.getConnection(), (c) -> IOUtils.copy(console.getSessionChannel().getInputStream(), session.getOutputStream()))); @@ -77,7 +77,7 @@ public void run(String[] args, VirtualConsole console) onClose((task, session) -> ((VirtualShellNG)console.getSessionChannel()).removeWindowSizeChangeListener(listener)). build()).waitForever(); - console.getSessionChannel().disableRawMode(); + console.getSessionChannel().resumeDataCaching(); console.println(); console.println(String.format("---- Exited shell on %s", clientName)); } diff --git a/maverick-synergy-common/src/main/java/com/sshtools/synergy/ssh/ChannelNG.java b/maverick-synergy-common/src/main/java/com/sshtools/synergy/ssh/ChannelNG.java index 8a97d7b4..821a9e45 100644 --- a/maverick-synergy-common/src/main/java/com/sshtools/synergy/ssh/ChannelNG.java +++ b/maverick-synergy-common/src/main/java/com/sshtools/synergy/ssh/ChannelNG.java @@ -29,6 +29,7 @@ import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; +import java.util.ArrayList; import java.util.Objects; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicBoolean; @@ -99,6 +100,7 @@ public abstract class ChannelNG implements Channel { private ChannelInputStream channelIn; private ChannelOutputStream channelOut = new ChannelOutputStream(this); private boolean autoConsume; + protected boolean paused; @Deprecated(forRemoval = true, since = "3.1.0") public ChannelNG(String channelType, int maximumPacketSize, int initialWindowSize, int maximumWindowSpace, int minimumWindowSpace, ChannelRequestFuture closeFuture, boolean autoConsume) { @@ -368,7 +370,7 @@ void confirmOpen() { openFuture.done(true); onChannelOpenConfirmation(); - for (ChannelEventListener listener : eventListeners) { + for (ChannelEventListener listener : new ArrayList<>(eventListeners)) { listener.onChannelOpen(this); } @@ -399,7 +401,7 @@ void adjustWindow(UnsignedInteger32 count) { onWindowAdjust(count); - for (ChannelEventListener listener : eventListeners) { + for (ChannelEventListener listener : new ArrayList<>(eventListeners)) { listener.onWindowAdjust(this, remoteWindow.getWindowSpace().longValue()); } @@ -458,14 +460,14 @@ void consumeWindowSpace(int length) throws IOException { } protected void onChannelData(ByteBuffer data) { - for (ChannelEventListener listener : eventListeners) { + for (ChannelEventListener listener : new ArrayList<>(eventListeners)) { listener.onChannelDataIn(this, data.asReadOnlyBuffer()); } - if(!autoConsume && Objects.nonNull(cache)) { + if(!paused && !autoConsume && Objects.nonNull(cache)) { try { cache.put(data); } catch (EOFException e) { - Log.error("Attempt to write data to channel cache failed because the cache is closed"); + Log.error("Attempt to write data to channel cache failed because the cache is closed", e); close(); } } else { @@ -591,7 +593,7 @@ public void sendChannelDataAndBlock(ByteBuffer buf, int type, Runnable r) throws processedBuffer.remaining(), processedBuffer.position(), processedBuffer.limit(), processedBuffer.capacity()); } - for (ChannelEventListener listener : eventListeners) { + for (ChannelEventListener listener : new ArrayList<>(eventListeners)) { listener.onChannelDataOut(this, processedBuffer); } connection.sendMessage(new ChannelData(processedBuffer, type, window)); @@ -601,7 +603,7 @@ public void sendChannelDataAndBlock(ByteBuffer buf, int type, Runnable r) throws Log.trace("Final Buffer rem={} pos={} limit={}, capacity={}", buf.remaining(), buf.position(), buf.limit(), buf.capacity()); } - for (ChannelEventListener listener : eventListeners) { + for (ChannelEventListener listener : new ArrayList<>(eventListeners)) { listener.onChannelDataOut(this, buf); } connection.sendMessage(lastMessage = new ChannelData(buf, type, window)); @@ -692,7 +694,7 @@ void processExtendedData(int type, ByteBuffer data) throws IOException { * @param data */ protected void onExtendedData(ByteBuffer data, int type) { - for (ChannelEventListener listener : eventListeners) { + for (ChannelEventListener listener : new ArrayList<>(eventListeners)) { listener.onChannelExtendedData(this, data, type); } if(Objects.isNull(cache)) { @@ -702,7 +704,7 @@ protected void onExtendedData(ByteBuffer data, int type) { void processChannelEOF() { - for (ChannelEventListener listener : eventListeners) { + for (ChannelEventListener listener : new ArrayList<>(eventListeners)) { listener.onChannelEOF(this); } @@ -826,7 +828,7 @@ protected void close(boolean forceClose) { sentClose.set(true); doSend = true; - for (ChannelEventListener listener : eventListeners) { + for (ChannelEventListener listener : new ArrayList<>(eventListeners)) { listener.onChannelClosing(this); } @@ -857,7 +859,7 @@ protected void close(boolean forceClose) { if(forceClose) { this.forcedClose = true; - for (ChannelEventListener listener : eventListeners) { + for (ChannelEventListener listener : new ArrayList<>(eventListeners)) { listener.onChannelError(this, closingError != null ? closingError : new IOException("Channel has been forced to close")); } @@ -893,7 +895,7 @@ public void run() { if(Log.isTraceEnabled()) { log("Completing", "the close operation"); } - for (ChannelEventListener listener : eventListeners) { + for (ChannelEventListener listener : new ArrayList<>(eventListeners)) { listener.onChannelClose(ChannelNG.this); } eventListeners.clear(); @@ -1457,6 +1459,7 @@ public int read(byte[] b, int off, int len) throws IOException { try { streamCache.waitFor(1000); } catch (InterruptedException e) { + throw new InterruptedIOException("The thread was interrupted"); } } diff --git a/maverick-synergy-server/src/main/java/com/sshtools/server/SessionChannelNG.java b/maverick-synergy-server/src/main/java/com/sshtools/server/SessionChannelNG.java index f8960fd4..2ee349fa 100644 --- a/maverick-synergy-server/src/main/java/com/sshtools/server/SessionChannelNG.java +++ b/maverick-synergy-server/src/main/java/com/sshtools/server/SessionChannelNG.java @@ -109,7 +109,6 @@ public abstract class SessionChannelNG extends ChannelNG imple boolean haltIncomingData = false; long lastActivity = System.currentTimeMillis(); boolean agentForwardingRequested; - boolean rawMode = false; boolean singleSession = false; ChannelOutputStream stderrOutputStream = new ChannelOutputStream(this, SSH_EXTENDED_DATA_STDERR); @@ -141,16 +140,6 @@ protected void onChannelClosed() { } } - @Deprecated - public void enableRawMode() { - rawMode = true; - } - - @Deprecated - public void disableRawMode() { - rawMode = false; - } - public Subsystem getSubsystem() { return subsystem; } @@ -168,6 +157,13 @@ public boolean isAgentForwardingRequested() { return agentForwardingRequested; } + public void pauseDataCaching() { + paused = true; + } + + public void resumeDataCaching() { + paused = false; + } /** * Implement this method to support agent forwarding. * diff --git a/maverick-virtual-session/src/main/java/com/sshtools/server/vsession/VirtualShellNG.java b/maverick-virtual-session/src/main/java/com/sshtools/server/vsession/VirtualShellNG.java index 541b59d1..382bc981 100644 --- a/maverick-virtual-session/src/main/java/com/sshtools/server/vsession/VirtualShellNG.java +++ b/maverick-virtual-session/src/main/java/com/sshtools/server/vsession/VirtualShellNG.java @@ -221,17 +221,16 @@ protected void onLocalEOF() { } @Override - public void enableRawMode() { + public void pauseDataCaching() { console.getTerminal().pause(); - setAutoconsume(true); - super.enableRawMode(); + super.pauseDataCaching(); } @Override - public void disableRawMode() { + public void resumeDataCaching() { + super.resumeDataCaching(); console.getTerminal().resume(); - setAutoconsume(false); - super.disableRawMode(); + } class VirtualShellCompletor implements Completer, MshListener { diff --git a/maverick-virtual-session/src/main/java/com/sshtools/server/vsession/commands/os/AbstractOSCommand.java b/maverick-virtual-session/src/main/java/com/sshtools/server/vsession/commands/os/AbstractOSCommand.java index e97c42ed..98029da9 100644 --- a/maverick-virtual-session/src/main/java/com/sshtools/server/vsession/commands/os/AbstractOSCommand.java +++ b/maverick-virtual-session/src/main/java/com/sshtools/server/vsession/commands/os/AbstractOSCommand.java @@ -119,7 +119,7 @@ public void newSize(int rows, int cols) { shell.addWindowSizeChangeListener(listener); - console.getSessionChannel().enableRawMode(); + console.getSessionChannel().pauseDataCaching(); ChannelEventListener l = new ChannelEventListener() { @@ -152,7 +152,7 @@ public void onChannelDataIn(Channel channel, ByteBuffer buffer) { } catch (Exception e) { } finally { try { - console.getSessionChannel().disableRawMode(); + console.getSessionChannel().resumeDataCaching(); } finally { console.getSessionChannel().removeEventListener(l); diff --git a/maverick-virtual-session/src/main/java/com/sshtools/vsession/commands/ssh/SshClientCommand.java b/maverick-virtual-session/src/main/java/com/sshtools/vsession/commands/ssh/SshClientCommand.java index 13c4cde4..82dd7752 100644 --- a/maverick-virtual-session/src/main/java/com/sshtools/vsession/commands/ssh/SshClientCommand.java +++ b/maverick-virtual-session/src/main/java/com/sshtools/vsession/commands/ssh/SshClientCommand.java @@ -23,6 +23,7 @@ */ import java.io.IOException; +import java.nio.ByteBuffer; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Option; @@ -34,7 +35,10 @@ import com.sshtools.client.tasks.CommandTask.CommandTaskBuilder; import com.sshtools.client.tasks.ShellTask.ShellTaskBuilder; import com.sshtools.client.tasks.Task; +import com.sshtools.common.logger.Log; import com.sshtools.common.permissions.PermissionDeniedException; +import com.sshtools.common.ssh.Channel; +import com.sshtools.common.ssh.ChannelEventListener; import com.sshtools.common.util.IOUtils; import com.sshtools.server.vsession.VirtualConsole; import com.sshtools.server.vsession.VirtualShellNG; @@ -54,10 +58,11 @@ public SshClientCommand() { @Override public void runCommand(SshClient sshClient, SshClientArguments arguments, VirtualConsole console) { - - //console.getSessionChannel().enableRawMode(); - + try { + + console.getSessionChannel().pauseDataCaching(); + Connection connection = sshClient.getConnection(); AbstractSessionTask task; @@ -93,9 +98,37 @@ public void runCommand(SshClient sshClient, SshClientArguments arguments, Virtua onBeforeTask((t, session) -> { listener.session = session; ((VirtualShellNG)console.getSessionChannel()).addWindowSizeChangeListener(listener); + + ChannelEventListener l = new ChannelEventListener() { + + @Override + public void onChannelDataIn(Channel channel, ByteBuffer buffer) { + + byte[] tmp = new byte[buffer.remaining()]; + buffer.get(tmp); + + try { + session.getOutputStream().write(tmp); + session.getOutputStream().flush(); + } catch (IOException e) { + Log.error("Error writing data from console", e); + } + } + }; + console.getSessionChannel().addEventListener(l); + + session.addEventListener(new ChannelEventListener() { + @Override + public void onChannelClose(Channel channel) { + if(Log.isDebugEnabled()) { + Log.debug("Detected close of child command so removing channel data listeners"); + } + console.getSessionChannel().removeEventListener(l); + session.removeEventListener(this); + } + }); }) .onTask((t, session)-> { - connection.addTask(Task.ofRunnable(connection, (c) -> IOUtils.copy(console.getSessionChannel().getInputStream(), session.getOutputStream()))); IOUtils.copy(session.getInputStream(), console.getSessionChannel().getOutputStream()); }). onClose((t, session) -> ((VirtualShellNG)console.getSessionChannel()).removeWindowSizeChangeListener(listener)). @@ -106,7 +139,8 @@ public void runCommand(SshClient sshClient, SshClientArguments arguments, Virtua task.waitForever(); } finally { - //console.getSessionChannel().disableRawMode(); + + console.getSessionChannel().resumeDataCaching(); console.println(); } From ca4e497a1a07b6348871d0099c733f04c45bf205 Mon Sep 17 00:00:00 2001 From: Lee Painter Date: Mon, 6 May 2024 20:10:16 +0100 Subject: [PATCH 15/51] Path could lead to Not enough answers! exception. --- .../common/auth/PasswordKeyboardInteractiveProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maverick-base/src/main/java/com/sshtools/common/auth/PasswordKeyboardInteractiveProvider.java b/maverick-base/src/main/java/com/sshtools/common/auth/PasswordKeyboardInteractiveProvider.java index 60f4e882..fff1c41c 100644 --- a/maverick-base/src/main/java/com/sshtools/common/auth/PasswordKeyboardInteractiveProvider.java +++ b/maverick-base/src/main/java/com/sshtools/common/auth/PasswordKeyboardInteractiveProvider.java @@ -106,10 +106,10 @@ public boolean setResponse(String[] answers, Collection additionalPro else instruction = e.getMessage(); - return true; } catch(IOException ex) { con.disconnect(TransportProtocolSpecification.BY_APPLICATION, ex.getMessage()); } + return true; case CHANGING_PASSWORD: if(answers.length < 2) { throw new RuntimeException("Not enough answers!"); From e282bd967fa77a8d4cbcf04f89edaf6387c7079b Mon Sep 17 00:00:00 2001 From: Brett Smith Date: Tue, 7 May 2024 23:25:00 +0100 Subject: [PATCH 16/51] Another constructor so that channel type may be passed in (e.g. for domain socket forwarding). --- .../server/callback/CallbackForwardingChannel.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/maverick-synergy-callback-server/src/main/java/com/sshtools/server/callback/CallbackForwardingChannel.java b/maverick-synergy-callback-server/src/main/java/com/sshtools/server/callback/CallbackForwardingChannel.java index ba7de337..45fd956e 100644 --- a/maverick-synergy-callback-server/src/main/java/com/sshtools/server/callback/CallbackForwardingChannel.java +++ b/maverick-synergy-callback-server/src/main/java/com/sshtools/server/callback/CallbackForwardingChannel.java @@ -48,7 +48,12 @@ public class CallbackForwardingChannel extends ForwardingC final static Integer CHANNEL_QUEUE = ExecutorOperationQueues.generateUniqueQueue("callbackDataQueue"); public CallbackForwardingChannel(Context ctx, SshConnection callbackClient) { - super(LocalForwardingChannel.LOCAL_FORWARDING_CHANNEL_TYPE, + this(LocalForwardingChannel.LOCAL_FORWARDING_CHANNEL_TYPE, ctx, callbackClient); + this.callbackClient = callbackClient; + } + + public CallbackForwardingChannel(String channel, Context ctx, SshConnection callbackClient) { + super(channel, ctx.getPolicy(ForwardingPolicy.class).getForwardingMaxPacketSize(), ctx.getPolicy(ForwardingPolicy.class).getForwardingMaxWindowSize(), ctx.getPolicy(ForwardingPolicy.class).getForwardingMaxWindowSize(), From b2f59a1d827ae156e8c5564dd398d86749288afd Mon Sep 17 00:00:00 2001 From: Brett Smith Date: Wed, 8 May 2024 09:46:54 +0100 Subject: [PATCH 17/51] More changes required for unix domain socket forwarding over callback. Package not being exported. --- .../callback/CallbackForwardingChannel.java | 57 +++++++++++++++---- .../src/main/java/module-info.java | 1 + 2 files changed, 46 insertions(+), 12 deletions(-) diff --git a/maverick-synergy-callback-server/src/main/java/com/sshtools/server/callback/CallbackForwardingChannel.java b/maverick-synergy-callback-server/src/main/java/com/sshtools/server/callback/CallbackForwardingChannel.java index 45fd956e..5f7bf6c3 100644 --- a/maverick-synergy-callback-server/src/main/java/com/sshtools/server/callback/CallbackForwardingChannel.java +++ b/maverick-synergy-callback-server/src/main/java/com/sshtools/server/callback/CallbackForwardingChannel.java @@ -43,6 +43,7 @@ public class CallbackForwardingChannel extends ForwardingChannel { + static final String DIRECT_STREAM_LOCAL_CHANNEL = "direct-streamlocal@openssh.com"; CallbackForwardingChannel channel; SshConnection callbackClient; final static Integer CHANNEL_QUEUE = ExecutorOperationQueues.generateUniqueQueue("callbackDataQueue"); @@ -97,17 +98,44 @@ public CallbackForwardingChannel(String channelType, SshConnection con, String h this.hostToConnect = hostToConnect; this.portToConnect = portToConnect; } + + /** + * Constructs a forwarding channel of the type "forwarded-tcpip" + * + * @param addressToBind + * String + * @param portToBind + * int + * @param socketChannel + * SocketChannel + */ + public CallbackForwardingChannel(String channelType, Context ctx, SshConnection con, String hostToConnect, int portToConnect) { + super(channelType, con.getContext().getPolicy(ForwardingPolicy.class).getForwardingMaxPacketSize(), + ctx.getPolicy(ForwardingPolicy.class).getForwardingMaxWindowSize(), + ctx.getPolicy(ForwardingPolicy.class).getForwardingMaxWindowSize(), + ctx.getPolicy(ForwardingPolicy.class).getForwardingMinWindowSize(), + true); + this.hostToConnect = hostToConnect; + this.portToConnect = portToConnect; + } + /** * Create the forwarding channel. * * @return byte[] */ protected byte[] createChannel() throws IOException { - - - ByteArrayWriter baw = new ByteArrayWriter(); + if(getChannelType().equals(DIRECT_STREAM_LOCAL_CHANNEL)) { + try(var baw = new ByteArrayWriter()) { + baw.writeString(hostToConnect); + baw.writeString(""); // Reserved + baw.writeInt(0); // Reserved + return baw.toByteArray(); + + } + } - try { + try(var baw = new ByteArrayWriter()) { baw.writeString(hostToConnect); baw.writeInt(portToConnect); baw.writeString(originatingHost = con.getRemoteIPAddress()); @@ -115,9 +143,7 @@ protected byte[] createChannel() throws IOException { return baw.toByteArray(); - } finally { - baw.close(); - } + } } /** @@ -135,10 +161,17 @@ protected byte[] openChannel(byte[] requestdata) ByteArrayReader bar = new ByteArrayReader(requestdata); try { - hostToConnect = bar.readString(); - portToConnect = (int) bar.readInt(); - originatingHost = bar.readString(); - originatingPort = (int) bar.readInt(); + if(getChannelType().equals(DIRECT_STREAM_LOCAL_CHANNEL)) { + hostToConnect = bar.readString(); // socket path + bar.readString(); // reserved + bar.readInt(); // reserved + } + else { + hostToConnect = bar.readString(); + portToConnect = (int) bar.readInt(); + originatingHost = bar.readString(); + originatingPort = (int) bar.readInt(); + } boolean success = checkPermissions(); @@ -167,7 +200,7 @@ protected byte[] openChannel(byte[] requestdata) @Override protected void doTask() throws Throwable { - channel = new CallbackForwardingChannel( + channel = new CallbackForwardingChannel(getChannelType(), connection.getContext(), callbackClient, hostToConnect, portToConnect); channel.setBoundChannel(CallbackForwardingChannel.this); diff --git a/maverick-synergy-jdk16-server/src/main/java/module-info.java b/maverick-synergy-jdk16-server/src/main/java/module-info.java index 92ad1370..364c1f68 100644 --- a/maverick-synergy-jdk16-server/src/main/java/module-info.java +++ b/maverick-synergy-jdk16-server/src/main/java/module-info.java @@ -23,4 +23,5 @@ module com.sshtools.synergy.jdk16.server { requires transitive com.sshtools.synergy.jdk16.common; requires transitive com.sshtools.synergy.server; + exports com.sshtools.server.jdk16; } From 35ee6ae933a941c7cf31ce81b8c4a7d2f99c8b3b Mon Sep 17 00:00:00 2001 From: Brett Smith Date: Thu, 9 May 2024 12:05:41 +0100 Subject: [PATCH 18/51] There was no way to set non-null shell environment variables. --- .../com/sshtools/server/vsession/VirtualSessionPolicy.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/maverick-virtual-session/src/main/java/com/sshtools/server/vsession/VirtualSessionPolicy.java b/maverick-virtual-session/src/main/java/com/sshtools/server/vsession/VirtualSessionPolicy.java index 6d435962..8fb1c43f 100644 --- a/maverick-virtual-session/src/main/java/com/sshtools/server/vsession/VirtualSessionPolicy.java +++ b/maverick-virtual-session/src/main/java/com/sshtools/server/vsession/VirtualSessionPolicy.java @@ -68,6 +68,10 @@ public Map getShellEnvironment() { return shellEnvironment; } + public void setShellEnvironment(Map shellEnvironment) { + this.shellEnvironment = shellEnvironment; + } + public File getShellDirectory() { return shellDirectory; } From f7c17b6c074c378e98d22f4592e1b8231211e35b Mon Sep 17 00:00:00 2001 From: Brett Smith Date: Thu, 9 May 2024 12:26:42 +0100 Subject: [PATCH 19/51] Environment not being set at all. --- .../sshtools/server/vsession/commands/os/AbstractOSCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maverick-virtual-session/src/main/java/com/sshtools/server/vsession/commands/os/AbstractOSCommand.java b/maverick-virtual-session/src/main/java/com/sshtools/server/vsession/commands/os/AbstractOSCommand.java index 98029da9..5ae1f441 100644 --- a/maverick-virtual-session/src/main/java/com/sshtools/server/vsession/commands/os/AbstractOSCommand.java +++ b/maverick-virtual-session/src/main/java/com/sshtools/server/vsession/commands/os/AbstractOSCommand.java @@ -100,7 +100,7 @@ private void runCommand(String cmd, List cmdArgs, if(directory != null) builder.setDirectory(directory.getAbsolutePath()); builder.setConsole(false); - builder.setEnvironment(env); + builder.setEnvironment(penv); pty = builder.start(); final InputStream in = pty.getInputStream(); From b6f8f8231ba94ad908955f711dcee18bdb13ff6b Mon Sep 17 00:00:00 2001 From: Brett Smith Date: Sun, 12 May 2024 11:34:20 +0100 Subject: [PATCH 20/51] When no permissions, use `---------` rather than an empty string (this method is intended for `ls` type output). --- .../main/java/com/sshtools/common/sftp/SftpFileAttributes.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maverick-base/src/main/java/com/sshtools/common/sftp/SftpFileAttributes.java b/maverick-base/src/main/java/com/sshtools/common/sftp/SftpFileAttributes.java index 5d6fdf1f..f9367e5c 100644 --- a/maverick-base/src/main/java/com/sshtools/common/sftp/SftpFileAttributes.java +++ b/maverick-base/src/main/java/com/sshtools/common/sftp/SftpFileAttributes.java @@ -1976,7 +1976,7 @@ public String toPermissionsString() { str.append('-'); break; } - str.append(permissions.map(PosixPermissions::asFileModesString).orElse("")); + str.append(permissions.map(PosixPermissions::asFileModesString).orElse("---------")); return str.toString(); } From 55c4ab7bf7a06d995acb4dfdcf4ca671e259f677 Mon Sep 17 00:00:00 2001 From: Brett Smith Date: Tue, 14 May 2024 01:49:05 +0100 Subject: [PATCH 21/51] Pass on pseudo terminal modes to child SSH clients. https://github.com/jline/jline3/issues/980 --- .../server/vsession/VirtualConsole.java | 8 ++++++- .../server/vsession/VirtualShellNG.java | 5 +++- .../commands/ssh/SshClientCommand.java | 24 +++++++++++++++---- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/maverick-virtual-session/src/main/java/com/sshtools/server/vsession/VirtualConsole.java b/maverick-virtual-session/src/main/java/com/sshtools/server/vsession/VirtualConsole.java index d274dff5..d0911c34 100644 --- a/maverick-virtual-session/src/main/java/com/sshtools/server/vsession/VirtualConsole.java +++ b/maverick-virtual-session/src/main/java/com/sshtools/server/vsession/VirtualConsole.java @@ -49,10 +49,11 @@ public class VirtualConsole { Msh shell; AbstractFile cwd; AbstractFileFactory fileFactory; + byte[] modes; static ThreadLocal threadConsoles = new ThreadLocal<>(); - public VirtualConsole(SessionChannelServer channel, Environment env, Terminal terminal, LineReader reader, Msh shell) throws IOException, PermissionDeniedException { + public VirtualConsole(SessionChannelServer channel, Environment env, Terminal terminal, LineReader reader, Msh shell, byte[] modes) throws IOException, PermissionDeniedException { this.channel = channel; this.con = channel.getConnection(); this.env = env; @@ -61,6 +62,11 @@ public VirtualConsole(SessionChannelServer channel, Environment env, Terminal te this.shell = shell; this.fileFactory = getContext().getPolicy(FileSystemPolicy.class) .getFileFactory().getFileFactory(con); + this.modes = modes; + } + + public byte[] getPseudoTerminalModes() { + return modes; } public SshConnection getConnection() { diff --git a/maverick-virtual-session/src/main/java/com/sshtools/server/vsession/VirtualShellNG.java b/maverick-virtual-session/src/main/java/com/sshtools/server/vsession/VirtualShellNG.java index 382bc981..3d740709 100644 --- a/maverick-virtual-session/src/main/java/com/sshtools/server/vsession/VirtualShellNG.java +++ b/maverick-virtual-session/src/main/java/com/sshtools/server/vsession/VirtualShellNG.java @@ -78,7 +78,9 @@ public interface WindowSizeChangeListener { protected ShellCommandFactory commandFactory; List listeners = new ArrayList(); + private Terminal terminal; + private byte[] modes; public VirtualShellNG(SshConnection con, ShellCommandFactory commandFactory) { @@ -170,7 +172,7 @@ private VirtualConsole createConsole() throws IOException, PermissionDeniedExcep .variable(LineReader.HISTORY_SIZE, 1000) .variable(LineReader.HISTORY_FILE, fs.getPath(".history")); - return new VirtualConsole(this, this.env, terminal, lineReaderBuilder.build(), shell); + return new VirtualConsole(this, this.env, terminal, lineReaderBuilder.build(), shell, modes); } @Override @@ -193,6 +195,7 @@ protected boolean allocatePseudoTerminal(String term, int cols, int rows, int wi env.put("COLS", cols); env.put("ROWS", rows); env.put("PTYMODES", modes); + this.modes = modes; return true; } diff --git a/maverick-virtual-session/src/main/java/com/sshtools/vsession/commands/ssh/SshClientCommand.java b/maverick-virtual-session/src/main/java/com/sshtools/vsession/commands/ssh/SshClientCommand.java index 82dd7752..c2c50c39 100644 --- a/maverick-virtual-session/src/main/java/com/sshtools/vsession/commands/ssh/SshClientCommand.java +++ b/maverick-virtual-session/src/main/java/com/sshtools/vsession/commands/ssh/SshClientCommand.java @@ -28,6 +28,7 @@ import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Option; +import com.sshtools.client.PseudoTerminalModes.PseudoTerminalModesBuilder; import com.sshtools.client.SessionChannelNG; import com.sshtools.client.SshClient; import com.sshtools.client.SshClientContext; @@ -70,7 +71,7 @@ public void runCommand(SshClient sshClient, SshClientArguments arguments, Virtua if (CommandUtil.isNotEmpty(arguments.getCommand())) { String command = arguments.getCommand(); - task = CommandTaskBuilder.create() + var builder = CommandTaskBuilder.create() .withConnection(connection) .withCommand(command) .withTermType(console.getTerminal().getType()) @@ -85,12 +86,18 @@ public void runCommand(SshClient sshClient, SshClientArguments arguments, Virtua IOUtils.copy(session.getInputStream(), console.getSessionChannel().getOutputStream()); }) - .onClose((t, session) -> ((VirtualShellNG)console.getSessionChannel()).removeWindowSizeChangeListener(listener)) - .build(); + .onClose((t, session) -> ((VirtualShellNG)console.getSessionChannel()).removeWindowSizeChangeListener(listener)); + if(console.getPseudoTerminalModes() != null) { + try { + builder.withModes(PseudoTerminalModesBuilder.create().build()); + } catch (IOException e) { + } + } + task = builder.build(); } else { - task = ShellTaskBuilder.create(). + var builder = ShellTaskBuilder.create(). withConnection(connection). withTermType(console.getTerminal().getType()). withColumns(console.getTerminal().getWidth()). @@ -131,7 +138,14 @@ public void onChannelClose(Channel channel) { .onTask((t, session)-> { IOUtils.copy(session.getInputStream(), console.getSessionChannel().getOutputStream()); }). - onClose((t, session) -> ((VirtualShellNG)console.getSessionChannel()).removeWindowSizeChangeListener(listener)). + onClose((t, session) -> ((VirtualShellNG)console.getSessionChannel()).removeWindowSizeChangeListener(listener)); + if(console.getPseudoTerminalModes() != null) { + try { + builder.withModes(PseudoTerminalModesBuilder.create().fromBinaryModes(console.getPseudoTerminalModes()).build()); + } catch (IOException e) { + } + } + task = builder. build(); } From 96bedd34687300a004faa28fd271b355524fbea6 Mon Sep 17 00:00:00 2001 From: Brett Smith Date: Tue, 14 May 2024 10:56:01 +0100 Subject: [PATCH 22/51] Deprecated `PseudoTerminalModes` entirely to be replaced by `TerminalModes` in the common library. * Lets us use same object in both server and client. * Now entirely immutable. * Integer constants replaced by `enum`. * Can now easily both set and query modes by their code or enum name. Virtual shell now makes use of this rather than the byte array. --- .../client/AbstractSessionChannel.java | 9 +- .../sshtools/client/PseudoTerminalModes.java | 4 + .../com/sshtools/client/tasks/ShellTask.java | 17 +- .../sshtools/synergy/ssh/TerminalModes.java | 840 ++++++++++++++++++ .../com/sshtools/server/SessionChannelNG.java | 32 +- .../sshtools/server/UnsupportedSession.java | 32 +- .../server/vsession/VirtualConsole.java | 7 +- .../server/vsession/VirtualShellNG.java | 86 +- .../commands/ssh/SshClientCommand.java | 5 +- 9 files changed, 965 insertions(+), 67 deletions(-) create mode 100644 maverick-synergy-common/src/main/java/com/sshtools/synergy/ssh/TerminalModes.java diff --git a/maverick-synergy-client/src/main/java/com/sshtools/client/AbstractSessionChannel.java b/maverick-synergy-client/src/main/java/com/sshtools/client/AbstractSessionChannel.java index e2fec2aa..93ba2c77 100644 --- a/maverick-synergy-client/src/main/java/com/sshtools/client/AbstractSessionChannel.java +++ b/maverick-synergy-client/src/main/java/com/sshtools/client/AbstractSessionChannel.java @@ -32,6 +32,7 @@ import com.sshtools.common.util.ByteArrayWriter; import com.sshtools.common.util.UnsignedInteger32; import com.sshtools.synergy.ssh.ChannelNG; +import com.sshtools.synergy.ssh.TerminalModes; /** * Implements the client side of the SSH Connection protocol session channel @@ -108,7 +109,7 @@ public RequestFuture allocatePseudoTerminal(String type) { } public RequestFuture allocatePseudoTerminal(String type, int cols, int rows) { - return allocatePseudoTerminal(type, cols, rows, 0, 0, null); + return allocatePseudoTerminal(type, cols, rows, 0, 0, (TerminalModes)null); } public RequestFuture allocatePseudoTerminal(String type, int cols, int rows, PseudoTerminalModes modes) { @@ -175,8 +176,14 @@ public RequestFuture signal(String signal) { } } + @Deprecated(since = "3.1.2", forRemoval = true) public RequestFuture allocatePseudoTerminal(String type, int cols, int rows, int width, int height, PseudoTerminalModes modes) { + return allocatePseudoTerminal(type, cols, rows, width, height, TerminalModes.TerminalModesBuilder.create().fromBytes(modes.toByteArray()).build()); + } + + public RequestFuture allocatePseudoTerminal(String type, int cols, int rows, int width, int height, + TerminalModes modes) { ByteArrayWriter request = new ByteArrayWriter(); diff --git a/maverick-synergy-client/src/main/java/com/sshtools/client/PseudoTerminalModes.java b/maverick-synergy-client/src/main/java/com/sshtools/client/PseudoTerminalModes.java index 318c7934..1b934c4d 100644 --- a/maverick-synergy-client/src/main/java/com/sshtools/client/PseudoTerminalModes.java +++ b/maverick-synergy-client/src/main/java/com/sshtools/client/PseudoTerminalModes.java @@ -29,6 +29,7 @@ import com.sshtools.common.ssh.SshException; import com.sshtools.common.util.ByteArrayWriter; +import com.sshtools.synergy.ssh.TerminalModes; /** * @@ -52,9 +53,12 @@ * build()); * * + *

Deprecated, to be replaced by {@link TerminalModes} that allows the same + * object to be used by the client and the server.

* * @author Lee David Painter */ +@Deprecated(since = "3.1.2", forRemoval = true) public class PseudoTerminalModes { /** diff --git a/maverick-synergy-client/src/main/java/com/sshtools/client/tasks/ShellTask.java b/maverick-synergy-client/src/main/java/com/sshtools/client/tasks/ShellTask.java index df6a338c..a621b1bc 100644 --- a/maverick-synergy-client/src/main/java/com/sshtools/client/tasks/ShellTask.java +++ b/maverick-synergy-client/src/main/java/com/sshtools/client/tasks/ShellTask.java @@ -33,6 +33,7 @@ import com.sshtools.common.shell.ShellPolicy; import com.sshtools.common.ssh.SshConnection; import com.sshtools.common.ssh.SshException; +import com.sshtools.synergy.ssh.TerminalModes; /** * A {@link Task} that starts a remote shell with an allocated PTY. @@ -78,7 +79,7 @@ public final static class ShellTaskBuilder extends AbstractSessionTaskBuilder modes = Optional.empty(); + private Optional modes = Optional.empty(); private boolean autoConsume; private ShellTaskBuilder() { @@ -169,7 +170,19 @@ public final ShellTaskBuilder withRows(int rows) { * @param modes modes * @return builder for chaining */ + @Deprecated(since = "3.1.2", forRemoval = true) public final ShellTaskBuilder withModes(PseudoTerminalModes modes) { + return withModes(TerminalModes.TerminalModesBuilder.create().fromBytes(modes.toByteArray()).build()); + } + + /** + * Set the terminal modes to use when allocating a PTY. Note, this + * will have no effect if {{@link #withPty} is set to false. + * + * @param modes modes + * @return builder for chaining + */ + public final ShellTaskBuilder withModes(TerminalModes modes) { this.modes = Optional.of(modes); return this; } @@ -272,7 +285,7 @@ public ShellTaskBuilder withoutPty() { private final int rows; private final int cols; private final boolean withPty; - private final Optional modes; + private final Optional modes; private final boolean autoConsume; private ShellTask(ShellTaskBuilder builder) { diff --git a/maverick-synergy-common/src/main/java/com/sshtools/synergy/ssh/TerminalModes.java b/maverick-synergy-common/src/main/java/com/sshtools/synergy/ssh/TerminalModes.java new file mode 100644 index 00000000..76579fe7 --- /dev/null +++ b/maverick-synergy-common/src/main/java/com/sshtools/synergy/ssh/TerminalModes.java @@ -0,0 +1,840 @@ +package com.sshtools.synergy.ssh; + +/*- + * #%L + * Client API + * %% + * Copyright (C) 2002 - 2024 JADAPTIVE Limited + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + +import com.sshtools.common.util.ByteArrayReader; +import com.sshtools.common.util.ByteArrayWriter; + +/** + *

When a client requests a pseudo terminal it informs the server of + * any terminal modes that it knows of. This is typically used in + * situations where advance terminal configuration is required but + * it can also be used to perform simple configuration such as turning + * off character echo.

+ * + *

NOTE: the server may ignore some of the modes set if it does not + * support them.

+ * + *
+ * var session = ssh.openSessionChannel();
+ * session.requestPseudoTerminal("vt100", 80, 24, 0, 0, TerminalModesBuilder.create().
+ * 			// Turning off echo
+ * 			withMode(TerminalModes.Mode.ECHO, false).
+ * 			// Setting the Input/Output baud rate
+ * 			withMode(TerminalModes.Mode.TTY_OP_ISPEED, 38400).
+ * 			withMode(TerminalModes.Mode.TTY_OP_OSPEED, 38400).
+ * 			build());
+ * 
+ * + * @author Brett Smith + * @author Lee David Painter + */ +public final class TerminalModes { + + /** + * Enumeration of supported modes. + */ + public enum Mode { + + /** + * Interrupt character; 255 if none. + */ + VINTR, + + /** + * The quit character (sends SIGQUIT signal on POSIX systems). + */ + VQUIT, + + /** + * Erase the character to left of the cursor. + */ + VERASE, + + /** + * Kill the current input line. + */ + VKILL, + + /** + * End-of-file character (sends EOF from the terminal). + */ + VEOF, + + /** + * End-of-line character in addition to carriage return and/or linefeed. + */ + VEOL, + + /** + * Additional end-of-line character. + */ + VEOL2, + + /** + * Continues paused output (normally control-Q). + */ + VSTART, + + /** + * Pauses output (normally control-S).. + */ + VSTOP, + + /** + * Suspends the current program. + */ + VSUSP, + + /** + * Another suspend character. + */ + VDSUSP, + + /** + * Reprints the current input line. + */ + VREPRINT, + + /** + * Erases a word left of cursor. + */ + VWERASE, + + /** + * Enter the next character typed literally, even if it is a special character + */ + VLNEXT, + + /** + * Character to flush output. + */ + VFLUSH, + + /** + * Switch to a different shell layer. + */ + VSWITCH, + + /** + * Prints system status line (load, command, pid, etc). + */ + VSTATUS, + + /** + * Toggles the flushing of terminal output. + */ + VDISCARD, + + /** + * The ignore parity flag. The parameter SHOULD be 0 if this flag is FALSE, + * and 1 if it is TRUE. + */ + IGNPAR, + + /** + * Mark parity and framing errors. + */ + PARMRK, + + /** + * Enable checking of parity errors. + */ + INPCK, + + /** + * Strip 8th bit off characters. + */ + ISTRIP, + + /** + * Map NL into CR on input. + */ + INLCR, + + /** + * Ignore CR on input. + */ + IGNCR, + + /** + * Map CR to NL on input. + */ + ICRNL, + + /** + * Translate uppercase characters to lowercase. + */ + IUCLC, + + /** + * Enable output flow control. + */ + IXON, + + /** + * Any char will restart after stop. + */ + IXANY, + + /** + * Enable input flow control. + */ + IXOFF, + + /** + * Ring bell on input queue full. + */ + IMAXBEL, + + /** + * Output is assumed to be UTF-8 + */ + IUTF8, + + /** + * Enable signals INTR, QUIT, [D]SUSP. + */ + ISIG, + + /** + * Canonicalize input lines. + */ + ICANON, + + /** + * Enable input and output of uppercase characters by preceding their lowercase + * equivalents with "\". + */ + XCASE, + + /** + * Enable echoing. + */ + ECHO, + + /** + * Visually erase chars. + */ + ECHOE, + + /** + * Kill character discards current line. + */ + ECHOK, + + /** + * Echo NL even if ECHO is off. + */ + ECHONL, + + /** + * Don't flush after interrupt. + */ + NOFLSH, + + /** + * Stop background jobs from output.TerminalModes + */ + TOSTOP, + + + /** + * Enable extensions. + */ + IEXTEN, + + + /** + * Echo control characters as ^(Char). + */ + ECHOCTL, + + + /** + * Visual erase for line kill. + */ + ECHOKE, + + + /** + * Retype pending input. + */ + PENDIN, + + + /** + * Enable output processing. + */ + OPOST, + + + /** + * Convert lowercase to uppercase. + */ + OLCUC, + + /** + * Map NL to CR-NL. + */ + ONLCR, + + /** + * Translate carriage return to newline (output). + */ + OCRNL, + + /** + * Translate newline to carriage return-newline (output). + */ + ONOCR, + + /** + * Newline performs a carriage return (output). + */ + ONLRET, + + /** + * 7 bit mode. + */ + CS7, + + /** + * 8 bit mode. + */ + CS8, + + + /** + * Parity enable. + */ + PARENB, + + /** + * Odd parity, else even. + */ + PARODD, + + + /** + * Specifies the input baud rate in bits per second. + */ + TTY_OP_ISPEED, + + /** + * Specifies the output baud rate in bits per second. + */ + TTY_OP_OSPEED; + + public int toMode() { + switch(this) { + case VINTR: + return 1; + case VQUIT: + return 2; + case VERASE: + return 3; + case VKILL: + return 4; + case VEOF: + return 5; + case VEOL: + return 6; + case VEOL2: + return 7; + case VSTART: + return 8; + case VSTOP: + return 9; + case VSUSP: + return 10; + case VDSUSP: + return 11; + case VREPRINT: + return 12; + case VWERASE: + return 13; + case VLNEXT: + return 14; + case VFLUSH: + return 15; + case VSWITCH: + return 16; + case VSTATUS: + return 17; + case VDISCARD: + return 18; + case IGNPAR: + return 30; + case PARMRK: + return 31; + case INPCK: + return 32; + case ISTRIP: + return 33; + case INLCR: + return 34; + case IGNCR: + return 35; + case ICRNL: + return 36; + case IUCLC: + return 37; + case IXON: + return 38; + case IXANY: + return 39; + case IXOFF: + return 40; + case IMAXBEL: + return 41; + case IUTF8: + return 42; + case ISIG: + return 50; + case ICANON: + return 51; + case XCASE: + return 52; + case ECHO: + return 53; + case ECHOE: + return 54; + case ECHOK: + return 55; + case ECHONL: + return 56; + case NOFLSH: + return 57; + case TOSTOP: + return 58; + case IEXTEN: + return 59; + case ECHOCTL: + return 60; + case ECHOKE: + return 61; + case PENDIN: + return 62; + case OPOST: + return 70; + case OLCUC: + return 71; + case ONLCR: + return 72; + case OCRNL: + return 73; + case ONOCR: + return 74; + case ONLRET: + return 75; + case CS7: + return 90; + case CS8: + return 91; + case PARENB: + return 92; + case PARODD: + return 93; + case TTY_OP_ISPEED: + return 128; + case TTY_OP_OSPEED: + return 129; + } + throw new IllegalStateException(); + } + + public static Mode fromMode(int mode) { + switch(mode) { + case 1: + return VINTR; + case 2: + return VQUIT; + case 3: + return VERASE; + case 4: + return VKILL; + case 5: + return VEOF; + case 6: + return VEOL; + case 7: + return VEOL2; + case 8: + return VSTART; + case 9: + return VSTOP; + case 10: + return VSUSP; + case 11: + return VDSUSP; + case 12: + return VREPRINT; + case 13: + return VWERASE; + case 14: + return VLNEXT; + case 15: + return VFLUSH; + case 16: + return VSWITCH; + case 17: + return VSTATUS; + case 18: + return VDISCARD; + case 30: + return IGNPAR; + case 31: + return PARMRK; + case 32: + return INPCK; + case 33: + return ISTRIP; + case 34: + return INLCR; + case 35: + return INLCR; + case 36: + return ICRNL; + case 37: + return IUCLC; + case 38: + return IXON; + case 39: + return IXANY; + case 40: + return IXOFF; + case 41: + return IMAXBEL; + case 42: + return IUTF8; + case 50: + return ISIG; + case 51: + return ICANON; + case 52: + return XCASE; + case 53: + return ECHO; + case 54: + return ECHOE; + case 55: + return ECHOK; + case 56: + return ECHONL; + case 57: + return NOFLSH; + case 58: + return TOSTOP; + case 59: + return IEXTEN; + case 60: + return ECHOCTL; + case 61: + return ECHOKE; + case 62: + return PENDIN; + case 70: + return OPOST; + case 71: + return OLCUC; + case 72: + return ONLCR; + case 73: + return OCRNL; + case 74: + return ONOCR; + case 75: + return ONLRET; + case 90: + return CS7; + case 91: + return CS8; + case 92: + return PARENB; + case 93: + return PARODD; + case 128: + return TTY_OP_ISPEED; + case 129: + return TTY_OP_OSPEED; + } + throw new IllegalStateException("" + mode); + } + } + + /** + * Builds {@link TerminalModes}. + * + *

+ * You can reuse an instance of this class providing that you do not want to + * change any of the modes. If you do want to change modes you can call the + * reset method to clear out old modes. + *

+ */ + public final static class TerminalModesBuilder { + private final Map codes = new LinkedHashMap<>(); + + /** + * Clear all modes set in this builder. + * + * @return this for chaining + */ + public TerminalModesBuilder reset() { + codes.clear(); + return this; + } + + /** + * Set one or more modes to true, i.e. a value of 1. + * + * @param modes modes + * @return this for chaining + */ + public TerminalModesBuilder withModes(Mode... modes) { + Arrays.asList(modes).forEach(m -> withMode(m, true)); + return this; + } + + /** + * Set one or more modes to false, i.e. a value of 0. + * + * @param modes modes + * @return this for chaining + */ + public TerminalModesBuilder withoutModes(Mode... modes) { + Arrays.asList(modes).forEach(m -> withMode(m, false)); + return this; + } + + /** + * Set a boolean mode. + * + * @param mode mode + * @param value value to set + * @return this for chaining + */ + public TerminalModesBuilder withMode(int mode, boolean value) { + return withMode(Mode.fromMode(mode), value); + } + + /** + * Set a boolean mode. + * + * @param mode mode + * @param value value to set + * @return this for chaining + */ + public TerminalModesBuilder withMode(Mode mode, boolean value) { + return withMode(mode, value ? 1 : 0); + } + + /** + * Set an integer mode. + * + * @param mode mode + * @param value value to set + * @return this for chaining + */ + public TerminalModesBuilder withMode(int mode, int value) { + return withMode(Mode.fromMode(mode), value); + } + + /** + * Set an integer mode. + * + * @param mode mode + * @param value value to set + * @return this for chaining + */ + public TerminalModesBuilder withMode(Mode mode, int value) { + codes.put(mode, value); + System.out.println(mode + " : " + value); + return this; + } + + /** + * Set a boolean>code> mode to true. + * + * @param mode mode + * @return this for chaining + */ + public TerminalModesBuilder withMode(int mode) { + return withMode(mode, true); + } + + /** + * Set a boolean>code> mode to false. + * + * @param mode mode + * @return this for chaining + */ + public TerminalModesBuilder withoutMode(int mode) { + return withMode(mode, false); + } + + /** + * Create a new {@link TerminalModesBuilder} + * + * @return builder + */ + public static TerminalModesBuilder create() { + return new TerminalModesBuilder(); + } + + /** + * Build a new {@link TerminalModes}. + * + * @return modes + * @throws IOException + */ + public TerminalModes build() { + return new TerminalModes(this); + } + + public TerminalModesBuilder fromBytes(byte[] modes) { + return read(new ByteArrayReader(modes)); + } + + public TerminalModesBuilder read(ByteArrayReader reader) { + while(true) { + /* Hrm why does ByteArrayWriter throw IOExceptions? */ + try { + var mode = reader.read(); + if(mode < 1) + break; + withMode(Mode.fromMode(mode), (int)reader.readInt()); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + return this; + } + } + + private final Map modes; + + private TerminalModes(TerminalModesBuilder builder) { + this.modes = Collections.unmodifiableMap(new LinkedHashMap<>(builder.codes)); + } + + /** + * Get all of the modes and their values as a map. + * + * @return modes + */ + public Map modes() { + return modes; + } + + /** + * Returns the encoded modes for use by the {@link SshSession}. + * @return byte[] + */ + public byte[] toByteArray() { + var baw = new ByteArrayWriter(); + write(baw); + return baw.toByteArray(); + } + + /** + * Get the value of a numeric node, or zero if it does not exist + * + * @param mode + * @param default + * @return value + */ + public int get(Mode mode) { + return get(mode, 0); + } + + /** + * Get the value of a number node, or a default if it does not exist. + * + * @param mode + * @param default + * @return value + */ + public int get(Mode mode, int defaultValue) { + return modes.getOrDefault(mode, defaultValue); + } + + /** + * Get if a mode is set (i.e. is non-zero). If the mode is not in the set, + * false will be returned. + * + * @param mode mode + * @return mode is preent and non-zero + */ + public boolean is(Mode mode) { + return is(mode, false); + } + + /** + * Get if a mode is set (i.e. is non-zero) or a default if it does not exist. + * + * @param mode mode + * @param defaultValue default value + * @return mode is non-zero + */ + public boolean is(Mode mode, boolean defaultValue) { + var m = modes.get(mode); + return m == null ? defaultValue : m > 0; + } + + /** + * Get if a mode is present in the set. + * + * @param mode mode + * @return mode is present and non-zero + */ + public boolean present(Mode mode) { + return modes.containsKey(mode); + } + + /** + * Write the modes to the writer. + * + * @param writer + */ + public void write(ByteArrayWriter writer) { + modes.forEach((k, v) -> { + writer.write(k.toMode()); + try { + writer.writeInt(v); + } catch (IOException e) { + /* Hrm why does ByteArrayWriter throw IOExceptions? */ + throw new UncheckedIOException(e); + } + }); + } + + +} diff --git a/maverick-synergy-server/src/main/java/com/sshtools/server/SessionChannelNG.java b/maverick-synergy-server/src/main/java/com/sshtools/server/SessionChannelNG.java index 2ee349fa..e35566a1 100644 --- a/maverick-synergy-server/src/main/java/com/sshtools/server/SessionChannelNG.java +++ b/maverick-synergy-server/src/main/java/com/sshtools/server/SessionChannelNG.java @@ -50,6 +50,7 @@ import com.sshtools.common.util.Utils; import com.sshtools.synergy.ssh.ChannelNG; import com.sshtools.synergy.ssh.ChannelOutputStream; +import com.sshtools.synergy.ssh.TerminalModes; /** *

@@ -176,6 +177,9 @@ protected boolean requestAgentForwarding(String requestType) { /** * If the client requests a pseudo terminal for the session this method will * be invoked before the shell, exec or subsystem is started. + *

+ * Deprecated, at version 3.2.0 {@link #allocatePseudoTerminal(String, int, int, int, int, TerminalModes)}. + * will be made abstract and this method will be removed. * * @param term * @param cols @@ -185,8 +189,28 @@ protected boolean requestAgentForwarding(String requestType) { * @param modes * @return boolean */ - protected abstract boolean allocatePseudoTerminal(String term, int cols, - int rows, int width, int height, byte[] modes); + @Deprecated(forRemoval = true, since = "3.1.2") + protected boolean allocatePseudoTerminal(String term, int cols, + int rows, int width, int height, byte[] modes) { + throw new UnsupportedOperationException("No longer used, instead call allocatePseudoTerminal() with TerminalModes."); + } + + /** + * If the client requests a pseudo terminal for the session this method will + * be invoked before the shell, exec or subsystem is started. + * + * @param term + * @param cols + * @param rows + * @param width + * @param height + * @param modes + * @return boolean + */ + protected boolean allocatePseudoTerminal(String term, int cols, + int rows, int width, int height, TerminalModes modes) { + return allocatePseudoTerminal(term, cols, width, width, height, modes.toByteArray()); + } /** * When the window (terminal) size changes on the client side, it MAY send @@ -312,10 +336,10 @@ protected void onChannelRequest(String type, boolean wantreply, int rows = (int) bar.readInt(); int width = (int) bar.readInt(); int height = (int) bar.readInt(); - byte[] modes = bar.readBinaryString(); success = allocatePseudoTerminal(term, cols, rows, width, - height, modes); + height, new TerminalModes.TerminalModesBuilder().fromBytes(bar.readBinaryString()).build()); + if(Log.isDebugEnabled()) Log.debug(term + " pseudo terminal requested"); if(Log.isDebugEnabled()) diff --git a/maverick-synergy-server/src/main/java/com/sshtools/server/UnsupportedSession.java b/maverick-synergy-server/src/main/java/com/sshtools/server/UnsupportedSession.java index 61bb2e04..e659a3e2 100644 --- a/maverick-synergy-server/src/main/java/com/sshtools/server/UnsupportedSession.java +++ b/maverick-synergy-server/src/main/java/com/sshtools/server/UnsupportedSession.java @@ -10,12 +10,12 @@ * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see * . @@ -27,19 +27,20 @@ import com.sshtools.common.logger.Log; import com.sshtools.common.ssh.ConnectionAwareTask; import com.sshtools.common.ssh.SshConnection; +import com.sshtools.synergy.ssh.TerminalModes; /** * This is a basic session that provides a message to the user to inform them * that a shell or command cannot be executed because the server does not * support an interactive session. - * - * + * + * */ public class UnsupportedSession extends SessionChannelNG { String message = "This server does not support an interactive session.\r\nGoodbye.\r\n"; - + public UnsupportedSession(SshConnection con) { super(con); } @@ -48,10 +49,11 @@ protected boolean executeCommand(String cmd) { return false; } - protected boolean startShell() { - + @Override + protected boolean startShell() { + con.executeTask(new ConnectionAwareTask(con) { - + @Override protected void doTask() { try { @@ -66,17 +68,19 @@ protected void doTask() { } } }); - - + + return true; } - protected boolean allocatePseudoTerminal(String parm1, int parm2, int parm3, int parm4, int parm5, byte[] parm6) { + @Override + protected boolean allocatePseudoTerminal(String parm1, int parm2, int parm3, int parm4, int parm5, TerminalModes parm6) { return true; } - - public boolean setEnvironmentVariable(String name, String value) { + + @Override + public boolean setEnvironmentVariable(String name, String value) { return false; } @@ -92,6 +96,6 @@ protected void onLocalEOF() { @Override protected void processSignal(String signal) { - + } } diff --git a/maverick-virtual-session/src/main/java/com/sshtools/server/vsession/VirtualConsole.java b/maverick-virtual-session/src/main/java/com/sshtools/server/vsession/VirtualConsole.java index d0911c34..0139f36e 100644 --- a/maverick-virtual-session/src/main/java/com/sshtools/server/vsession/VirtualConsole.java +++ b/maverick-virtual-session/src/main/java/com/sshtools/server/vsession/VirtualConsole.java @@ -38,6 +38,7 @@ import com.sshtools.common.ssh.Context; import com.sshtools.common.ssh.SessionChannelServer; import com.sshtools.common.ssh.SshConnection; +import com.sshtools.synergy.ssh.TerminalModes; public class VirtualConsole { @@ -49,11 +50,11 @@ public class VirtualConsole { Msh shell; AbstractFile cwd; AbstractFileFactory fileFactory; - byte[] modes; + TerminalModes modes; static ThreadLocal threadConsoles = new ThreadLocal<>(); - public VirtualConsole(SessionChannelServer channel, Environment env, Terminal terminal, LineReader reader, Msh shell, byte[] modes) throws IOException, PermissionDeniedException { + public VirtualConsole(SessionChannelServer channel, Environment env, Terminal terminal, LineReader reader, Msh shell, TerminalModes modes) throws IOException, PermissionDeniedException { this.channel = channel; this.con = channel.getConnection(); this.env = env; @@ -65,7 +66,7 @@ public VirtualConsole(SessionChannelServer channel, Environment env, Terminal te this.modes = modes; } - public byte[] getPseudoTerminalModes() { + public TerminalModes getPseudoTerminalModes() { return modes; } diff --git a/maverick-virtual-session/src/main/java/com/sshtools/server/vsession/VirtualShellNG.java b/maverick-virtual-session/src/main/java/com/sshtools/server/vsession/VirtualShellNG.java index 3d740709..92e5f0f3 100644 --- a/maverick-virtual-session/src/main/java/com/sshtools/server/vsession/VirtualShellNG.java +++ b/maverick-virtual-session/src/main/java/com/sshtools/server/vsession/VirtualShellNG.java @@ -10,12 +10,12 @@ * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see * . @@ -55,20 +55,21 @@ import com.sshtools.common.util.Utils; import com.sshtools.server.AgentForwardingChannel; import com.sshtools.server.SessionChannelNG; +import com.sshtools.synergy.ssh.TerminalModes; public class VirtualShellNG extends SessionChannelNG { String shellCommand = null; Environment env = new Environment(); Set protectedEnvironmentVars = new HashSet<>(); - + public VirtualShellNG(SshConnection con, - ShellCommandFactory commandFactory, + ShellCommandFactory commandFactory, String shellCommand) { this(con, commandFactory); this.shellCommand = shellCommand; } - + public interface WindowSizeChangeListener { void newSize(int rows, int cols); } @@ -77,11 +78,11 @@ public interface WindowSizeChangeListener { protected VirtualConsole console; protected ShellCommandFactory commandFactory; - List listeners = new ArrayList(); - + List listeners = new ArrayList<>(); + private Terminal terminal; - private byte[] modes; - + private TerminalModes modes; + public VirtualShellNG(SshConnection con, ShellCommandFactory commandFactory) { super(con); @@ -95,26 +96,28 @@ public void addWindowSizeChangeListener(WindowSizeChangeListener listener) { public void removeWindowSizeChangeListener(WindowSizeChangeListener listener) { listeners.remove(listener); } - + public void addProtectedEnvironmentVar(String name) { protectedEnvironmentVars.add(name.toUpperCase()); } protected boolean executeCommand(String cmd) { - + try { shell = commandFactory.createShell(con); shell.execCommand(getInputStream(), console = createConsole(), cmd); return true; } catch (Exception e) { - if(Log.isErrorEnabled()) + if(Log.isErrorEnabled()) { Log.error("Failed to execute command " + cmd, e); - } + } + } return false; } + @Override protected void changeWindowDimensions(int cols, int rows, int width, int height) { - + console.getTerminal().setSize(new Size(cols, rows)); for (WindowSizeChangeListener l : listeners) { @@ -122,33 +125,35 @@ protected void changeWindowDimensions(int cols, int rows, int width, int height) } } + @Override public void onSessionOpen() { - + shell.start(); } + @Override protected boolean startShell() { - + if(Utils.isNotBlank(shellCommand)) { return executeCommand(shellCommand); } - + try { - shell = createShell(con); + shell = createShell(con); shell.startShell(null, console = createConsole()); return true; } catch (Throwable t) { Log.warn("Failed to start shell.", t); - } + } return false; } protected RootShell createShell(SshConnection con) throws PermissionDeniedException, IOException { return commandFactory.createShell(con); } - + private VirtualConsole createConsole() throws IOException, PermissionDeniedException { - + Attributes attrs = new Attributes(); attrs.setInputFlag(InputFlag.ICRNL, true); terminal = TerminalBuilder.builder(). @@ -158,14 +163,14 @@ private VirtualConsole createConsole() throws IOException, PermissionDeniedExcep size(new Size(env.getOrDefault("COLS", 80), env.getOrDefault("ROWS", 80))). encoding(Charset.forName("UTF-8")). attributes(attrs).build(); - + Map env = new HashMap<>(); env.put("connection", getConnection()); FileSystem fs = FileSystems.newFileSystem( - AbstractFileURI.create(getConnection(), ""), + AbstractFileURI.create(getConnection(), ""), env, getContext().getPolicy(ClassLoaderPolicy.class).getClassLoader()); - + final LineReaderBuilder lineReaderBuilder = LineReaderBuilder.builder() .terminal(terminal) .completer(new VirtualShellCompletor()) @@ -177,29 +182,31 @@ private VirtualConsole createConsole() throws IOException, PermissionDeniedExcep @Override protected boolean requestAgentForwarding(String requestType) { - + try { if(!getConnection().containsProperty(AgentForwardingChannel.SSH_AGENT_CLIENT)) { connection.openChannel(new AgentForwardingChannel(requestType, this)); - } + } return true; } catch (IOException e) { return false; } - + } - protected boolean allocatePseudoTerminal(String term, int cols, int rows, int width, int height, byte[] modes) { - + @Override + protected boolean allocatePseudoTerminal(String term, int cols, int rows, int width, int height, TerminalModes modes) { + env.put("TERM", term); env.put("COLS", cols); env.put("ROWS", rows); env.put("PTYMODES", modes); this.modes = modes; - + return true; } + @Override public boolean setEnvironmentVariable(String name, String value) { if(protectedEnvironmentVars.contains(name.toUpperCase())) { return false; @@ -208,6 +215,7 @@ public boolean setEnvironmentVariable(String name, String value) { return true; } + @Override protected void onChannelOpenFailure() { } @@ -215,12 +223,12 @@ protected void onChannelOpenFailure() { @Override protected void processSignal(String signal) { - + } @Override protected void onLocalEOF() { - + } @Override @@ -233,7 +241,7 @@ public void pauseDataCaching() { public void resumeDataCaching() { super.resumeDataCaching(); console.getTerminal().resume(); - + } class VirtualShellCompletor implements Completer, MshListener { @@ -243,17 +251,17 @@ class VirtualShellCompletor implements Completer, MshListener { VirtualShellCompletor() { shell.addListener(this); } - + @Override public void complete(LineReader reader, ParsedLine line, List candidates) { - + if(!inCommand.get()) { processShellCompletion(reader, line, candidates); } else { processInCommandCompletion(reader, line, candidates); } } - + private void processInCommandCompletion(LineReader reader, ParsedLine line, List candidates) { @SuppressWarnings("unchecked") List tmp = (List) console.getEnvironment().get("_COMPLETIONS"); @@ -263,7 +271,7 @@ private void processInCommandCompletion(LineReader reader, ParsedLine line, List } private void processShellCompletion(LineReader reader, ParsedLine line, List candidates) { - + switch(line.wordIndex()) { case 0: /** @@ -292,12 +300,12 @@ public void commandStarted(Command cmd, String[] args, VirtualConsole console) { inCommand.set(true); this.currentCommand = cmd; } - + @Override public void commandFinished(Command cmd, String[] args, VirtualConsole console) { inCommand.set(false); this.currentCommand = null; } - + } } diff --git a/maverick-virtual-session/src/main/java/com/sshtools/vsession/commands/ssh/SshClientCommand.java b/maverick-virtual-session/src/main/java/com/sshtools/vsession/commands/ssh/SshClientCommand.java index c2c50c39..57c9780a 100644 --- a/maverick-virtual-session/src/main/java/com/sshtools/vsession/commands/ssh/SshClientCommand.java +++ b/maverick-virtual-session/src/main/java/com/sshtools/vsession/commands/ssh/SshClientCommand.java @@ -140,10 +140,7 @@ public void onChannelClose(Channel channel) { }). onClose((t, session) -> ((VirtualShellNG)console.getSessionChannel()).removeWindowSizeChangeListener(listener)); if(console.getPseudoTerminalModes() != null) { - try { - builder.withModes(PseudoTerminalModesBuilder.create().fromBinaryModes(console.getPseudoTerminalModes()).build()); - } catch (IOException e) { - } + builder.withModes(console.getPseudoTerminalModes()); } task = builder. build(); From a8d22879af243876170832e05ceff6ccb2562a67 Mon Sep 17 00:00:00 2001 From: Brett Smith Date: Tue, 14 May 2024 14:14:20 +0100 Subject: [PATCH 23/51] Stray debug --- .../src/main/java/com/sshtools/synergy/ssh/TerminalModes.java | 1 - maverick-synergy-common/src/main/java/module-info.java | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/maverick-synergy-common/src/main/java/com/sshtools/synergy/ssh/TerminalModes.java b/maverick-synergy-common/src/main/java/com/sshtools/synergy/ssh/TerminalModes.java index 76579fe7..37211a11 100644 --- a/maverick-synergy-common/src/main/java/com/sshtools/synergy/ssh/TerminalModes.java +++ b/maverick-synergy-common/src/main/java/com/sshtools/synergy/ssh/TerminalModes.java @@ -676,7 +676,6 @@ public TerminalModesBuilder withMode(int mode, int value) { */ public TerminalModesBuilder withMode(Mode mode, int value) { codes.put(mode, value); - System.out.println(mode + " : " + value); return this; } diff --git a/maverick-synergy-common/src/main/java/module-info.java b/maverick-synergy-common/src/main/java/module-info.java index 9b79cb68..37e6b47c 100644 --- a/maverick-synergy-common/src/main/java/module-info.java +++ b/maverick-synergy-common/src/main/java/module-info.java @@ -33,6 +33,7 @@ exports com.sshtools.synergy.ssh.components; exports com.sshtools.synergy.ssh.components.jce; exports com.sshtools.synergy.util; + opens com.sshtools.synergy.ssh; uses SshCompressionFactory; } From ccbc4c92551fd9fbd0622dc284d5d78784469907 Mon Sep 17 00:00:00 2001 From: Lee Painter Date: Wed, 23 Aug 2023 16:04:47 +0100 Subject: [PATCH 24/51] Prevent logging if temporary IP address banning is disabled. --- .../java/com/sshtools/common/permissions/IPPolicy.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/maverick-base/src/main/java/com/sshtools/common/permissions/IPPolicy.java b/maverick-base/src/main/java/com/sshtools/common/permissions/IPPolicy.java index 9f1a0930..a4da4e89 100644 --- a/maverick-base/src/main/java/com/sshtools/common/permissions/IPPolicy.java +++ b/maverick-base/src/main/java/com/sshtools/common/permissions/IPPolicy.java @@ -136,17 +136,25 @@ public void flagAddress(String addr) { public void flagAddress(InetAddress addr) { + if(check(DISABLE_BAN)) { + return; + } + Integer count = flaggedAddressCounts.getOrDefault(addr, 0); if(count >= failedAuthenticationThreshold) { + if(Log.isInfoEnabled()) { Log.info("Temporarily banning IP address {} due to failed authentication count of {}", addr.getHostAddress(), count); + } temporaryBans.put(addr, true); return; } ++count; - Log.info("Flagging IP address {} with failed authentication count of {}", addr.getHostAddress(), count); + if(Log.isInfoEnabled()) { + Log.info("Flagging IP address {} with failed authentication count of {}", addr.getHostAddress(), count); + } flaggedAddressCounts.put(addr, count); } From 826cb7eea05e4d2c0a9bad7e0136da776687b66a Mon Sep 17 00:00:00 2001 From: Lee Painter Date: Mon, 3 Jun 2024 14:59:27 +0100 Subject: [PATCH 25/51] Merged some issues from hotfixes. Updated change log. --- .../common/policy/SignaturePolicy.java | 24 ++++++++++++++++--- .../publickey/SshCertificateAuthority.java | 21 +++++++++++++++- maverick-synergy-assembly/notes/CHANGES | 12 ++++------ .../client/PublicKeyAuthenticator.java | 8 +------ 4 files changed, 47 insertions(+), 18 deletions(-) diff --git a/maverick-base/src/main/java/com/sshtools/common/policy/SignaturePolicy.java b/maverick-base/src/main/java/com/sshtools/common/policy/SignaturePolicy.java index e29f2761..ce92e64d 100644 --- a/maverick-base/src/main/java/com/sshtools/common/policy/SignaturePolicy.java +++ b/maverick-base/src/main/java/com/sshtools/common/policy/SignaturePolicy.java @@ -30,16 +30,34 @@ public class SignaturePolicy { Set supportedSignatures = new TreeSet<>(); - - public SignaturePolicy() { + boolean strictMode; + + public SignaturePolicy() { + } + public SignaturePolicy(Collection supportedSignatures) { + this.supportedSignatures.addAll(supportedSignatures); + this.strictMode = false; } - public SignaturePolicy(Collection supportedSignatures) { + public SignaturePolicy(Collection supportedSignatures, boolean strictMode) { this.supportedSignatures.addAll(supportedSignatures); + this.strictMode = strictMode; } public Set getSupportedSignatures() { return Collections.unmodifiableSet(supportedSignatures); } + + public void setSupportedSignatures(Collection supportedSignatures) { + this.supportedSignatures = new TreeSet<>(supportedSignatures); + } + + public boolean isStrictMode() { + return strictMode; + } + + public void setStrictMode(boolean strictMode) { + this.strictMode = strictMode; + } } diff --git a/maverick-base/src/main/java/com/sshtools/common/publickey/SshCertificateAuthority.java b/maverick-base/src/main/java/com/sshtools/common/publickey/SshCertificateAuthority.java index d6c694f1..e066e45e 100644 --- a/maverick-base/src/main/java/com/sshtools/common/publickey/SshCertificateAuthority.java +++ b/maverick-base/src/main/java/com/sshtools/common/publickey/SshCertificateAuthority.java @@ -28,6 +28,7 @@ import java.util.Collections; import java.util.Date; import java.util.List; +import java.util.TimeZone; import com.sshtools.common.ssh.SshException; import com.sshtools.common.ssh.components.SshCertificate; @@ -94,6 +95,22 @@ public static SshCertificate generateCertificate(SshKeyPair key, List criticalOptions, List extensions, SshKeyPair signedBy) throws SshException, IOException { + return generateCertificate(key, serial, type, keyId, validPrincipals, + validityDays, Calendar.DAY_OF_MONTH, TimeZone.getTimeZone("UTC"), + criticalOptions, extensions, signedBy); + } + + public static SshCertificate generateCertificate(SshKeyPair key, + long serial, + int type, + String keyId, + List validPrincipals, + int validity, + int validityTimeUnit, + TimeZone timeZone, + List criticalOptions, + List extensions, + SshKeyPair signedBy) throws SshException, IOException { switch(type) { case SshCertificate.SSH_CERT_TYPE_HOST: @@ -111,9 +128,11 @@ public static SshCertificate generateCertificate(SshKeyPair key, c.set(Calendar.MINUTE, 0); c.set(Calendar.SECOND, 0); c.set(Calendar.MILLISECOND, 0); + c.setTimeZone(timeZone); + UnsignedInteger64 validAfter = new UnsignedInteger64(c.getTimeInMillis() / 1000); - c.add(Calendar.DAY_OF_MONTH, validityDays); + c.add(Calendar.DAY_OF_MONTH, validity); UnsignedInteger64 validBefore = new UnsignedInteger64(c.getTimeInMillis() / 1000); @SuppressWarnings("unused") diff --git a/maverick-synergy-assembly/notes/CHANGES b/maverick-synergy-assembly/notes/CHANGES index 12be0ee9..88b6209f 100644 --- a/maverick-synergy-assembly/notes/CHANGES +++ b/maverick-synergy-assembly/notes/CHANGES @@ -1,10 +1,14 @@ Maverick Synergy 3.1.2 - TBC Bug Fixes + o Added strict mode option to SignaturePolicy for clients to require declaration of signature support in SSH_MSG_EXT_INFO when provided. + o Specifically calculate dates with UTC time zone set to avoid local time zone defaults. o User and group information can now be defaulted on an AbstractMount. o Changed default public key preference from ecdsa to ed25519. o Incorrect parent mount passed to VirtualMountFile when resolving children. - + o CommandTaskBulder missing withConnection method. + o Fixes regression found in callback service where `osshell` would fail to send keyboard input. + ================= = HISTORY = ================= @@ -13,12 +17,6 @@ Maverick Synergy 3.1.1 - 3 Apr, 2024 Bug Fixes o SshClient put file throws No connection or client supplied. - ------------------ - -Maverick Synergy 3.1.1 - 3 Apr, 2024 - -Bug Fixes o Fixed issue with Terrapin strict kex mode and messages after SSH_MSG_NEWKEYS. ----------------- diff --git a/maverick-synergy-client/src/main/java/com/sshtools/client/PublicKeyAuthenticator.java b/maverick-synergy-client/src/main/java/com/sshtools/client/PublicKeyAuthenticator.java index d3bf82fc..c91ddfb5 100644 --- a/maverick-synergy-client/src/main/java/com/sshtools/client/PublicKeyAuthenticator.java +++ b/maverick-synergy-client/src/main/java/com/sshtools/client/PublicKeyAuthenticator.java @@ -105,13 +105,7 @@ private boolean setupNextKey() throws IOException, SshException { if(Log.isDebugEnabled()) { Log.debug("Upgrading certificate {} to use {} signature", currentKey.getAlgorithm(), signingAlgorithm); } - } - - - - - - else if(!policy.getSupportedSignatures().contains(signingAlgorithm)) { + } else if(policy.isStrictMode() && !policy.getSupportedSignatures().contains(signingAlgorithm)) { Log.debug("Server does not support {} signature for key {}", currentKey.getSigningAlgorithm(), SshKeyUtils.getOpenSSHFormattedKey(currentKey)); From e2e859624aa70a36222a04576c53acddb5a366ab Mon Sep 17 00:00:00 2001 From: Lee Painter Date: Mon, 3 Jun 2024 15:26:20 +0100 Subject: [PATCH 26/51] Merged some issues from hotfixes. Updated change log. --- maverick-synergy-assembly/notes/CHANGES | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/maverick-synergy-assembly/notes/CHANGES b/maverick-synergy-assembly/notes/CHANGES index 88b6209f..10e22fa3 100644 --- a/maverick-synergy-assembly/notes/CHANGES +++ b/maverick-synergy-assembly/notes/CHANGES @@ -8,7 +8,8 @@ Bug Fixes o Incorrect parent mount passed to VirtualMountFile when resolving children. o CommandTaskBulder missing withConnection method. o Fixes regression found in callback service where `osshell` would fail to send keyboard input. - + o Logging indicates that an IP address will be temporarily banned even if banning has been disabled. + ================= = HISTORY = ================= From e55ab4a753e935669bfb253a43af20c77568df24 Mon Sep 17 00:00:00 2001 From: Lee Painter Date: Tue, 4 Jun 2024 19:08:38 +0100 Subject: [PATCH 27/51] Turn off sandbox setting for ScpClient in default set up. --- .../src/main/java/com/sshtools/client/scp/ScpClient.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/maverick-synergy-client/src/main/java/com/sshtools/client/scp/ScpClient.java b/maverick-synergy-client/src/main/java/com/sshtools/client/scp/ScpClient.java index 1f030153..082fda53 100644 --- a/maverick-synergy-client/src/main/java/com/sshtools/client/scp/ScpClient.java +++ b/maverick-synergy-client/src/main/java/com/sshtools/client/scp/ScpClient.java @@ -67,7 +67,9 @@ public class ScpClient extends ScpClientIO { * @throws PermissionDeniedException */ public ScpClient(SshClient ssh) throws PermissionDeniedException, IOException { - this(NioFileFactoryBuilder.create().build(), ssh); + this(NioFileFactoryBuilder.create() + .withoutSandbox() + .build(), ssh); } /** @@ -78,7 +80,9 @@ public ScpClient(SshClient ssh) throws PermissionDeniedException, IOException { * @throws IOException */ public ScpClient(File cwd, SshClient ssh) throws PermissionDeniedException, IOException { - this(NioFileFactoryBuilder.create().withHome(cwd).build(), ssh); + this(NioFileFactoryBuilder.create() + .withoutSandbox() + .withHome(cwd).build(), ssh); } /** From c9272b6e184d61fb7340afdeb6fbe7ec43443eda Mon Sep 17 00:00:00 2001 From: Lee Painter Date: Tue, 4 Jun 2024 19:08:48 +0100 Subject: [PATCH 28/51] Updated change log. --- maverick-synergy-assembly/notes/CHANGES | 1 + 1 file changed, 1 insertion(+) diff --git a/maverick-synergy-assembly/notes/CHANGES b/maverick-synergy-assembly/notes/CHANGES index 10e22fa3..60d3714f 100644 --- a/maverick-synergy-assembly/notes/CHANGES +++ b/maverick-synergy-assembly/notes/CHANGES @@ -9,6 +9,7 @@ Bug Fixes o CommandTaskBulder missing withConnection method. o Fixes regression found in callback service where `osshell` would fail to send keyboard input. o Logging indicates that an IP address will be temporarily banned even if banning has been disabled. + o ScpClient and SftpClient have differing strategies for the default sandbox value. By default sandboxing is now turned off for both SFTP and SCP clients. ================= = HISTORY = From 7c28fcf2afe22ba454af376ac7f243ea425cf5ca Mon Sep 17 00:00:00 2001 From: Lee Painter Date: Sat, 8 Jun 2024 12:09:33 +0100 Subject: [PATCH 29/51] Refactor to make callback reconnects simpler and easier to debug. Removed setup via events in favour of doing everything in the connect method directly, waiting for disconnect, then looping in the executor thread. --- .../callback/client/CallbackClient.java | 48 ++--- .../callback/client/CallbackSession.java | 173 ++++++++---------- 2 files changed, 101 insertions(+), 120 deletions(-) diff --git a/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackClient.java b/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackClient.java index a13974e9..d237772e 100644 --- a/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackClient.java +++ b/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackClient.java @@ -185,32 +185,32 @@ public void run() { client.getConfig().getServerPort()); } - onClientStop(client, con); +// onClientStop(client, con); con.removeProperty(CALLBACK_CLIENT); clients.remove(client); - - if(!client.isStopped() && client.getConfig().isReconnect()) { - while(getSshEngine().isStarted()) { - - if(Log.isInfoEnabled()) { - Log.info("Will connect again to {}:{} in {} seconds" , - client.getConfig().getServerHost(), - client.getConfig().getServerPort(), - client.getConfig().getReconnectIntervalMs() / 1000); - } - try { - try { - Thread.sleep(client.getConfig().getReconnectIntervalMs()); - } catch (InterruptedException e1) { - } - client.connect(); - break; - } catch (IOException e) { - } - } - } else { - stop(); - } +// +// if(!client.isStopped() && client.getConfig().isReconnect()) { +// while(getSshEngine().isStarted()) { +// +// if(Log.isInfoEnabled()) { +// Log.info("Will connect again to {}:{} in {} seconds" , +// client.getConfig().getServerHost(), +// client.getConfig().getServerPort(), +// client.getConfig().getReconnectIntervalMs() / 1000); +// } +// try { +// try { +// Thread.sleep(client.getConfig().getReconnectIntervalMs()); +// } catch (InterruptedException e1) { +// } +// client.connect(); +// break; +// } catch (IOException e) { +// } +// } +// } else { +// stop(); +// } } } }); diff --git a/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackSession.java b/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackSession.java index 94aa287b..b2cf3fd0 100644 --- a/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackSession.java +++ b/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackSession.java @@ -64,119 +64,100 @@ public CallbackSession(CallbackConfiguration config, CallbackClient app, String } public void run() { - try { - connect(); - } catch (IOException e) { - Log.error("Failed to startup", e); - } + while(app.getSshEngine().isStarted()) { + + if(isStopped) { + Log.info("Callback to {}:{} has been stopped", hostname, port); + break; + } + + try { + connect(); + } catch (IOException | SshException e) { + Log.error("Connection failed to {}:{}", hostname, port); + } + + if(Log.isInfoEnabled()) { + Log.info("Connection disconnected from {}:{}", hostname, port); + } + + if(!config.isReconnect()) { + break; + } + + try { + long interval = config.getReconnectIntervalMs(); + + if(Log.isInfoEnabled()) { + Log.info("Will reconnect to {}:{} in {} seconds", hostname, port, interval / 1000); + } + Thread.sleep(interval); + } catch (InterruptedException e) { + } + } } - public void connect() throws IOException { - - if(isStopped) { - throw new IOException("Client has been stopped"); - } + public void connect() throws IOException, SshException { if(Log.isInfoEnabled()) { Log.info("Connecting to {}:{}", hostname, port); } - synchronized(app) { - if(!app.getSshEngine().isStarted() && !app.getSshEngine().isStarting()) { - if(!app.getSshEngine().startup()) { - throw new IOException("SSH Engine failed to start"); - } - } - } +// synchronized(app) { +// if(!app.getSshEngine().isStarted() && !app.getSshEngine().isStarting()) { +// if(!app.getSshEngine().startup()) { +// throw new IOException("SSH Engine failed to start"); +// } +// } +// } - while(app.getSshEngine().isStarted()) { - SshConnection currentConnection = null; - try { - future = app.getSshEngine().connect( - hostname, - port, - createContext(config)); - - future.waitFor(30000L); - if(future.isDone() && future.isSuccess()) { - - currentConnection = future.getConnection(); - currentConnection.getAuthenticatedFuture().waitFor(30000L); - - if(currentConnection.getAuthenticatedFuture().isDone() - && currentConnection.getAuthenticatedFuture().isSuccess()) { - - if(Log.isInfoEnabled()) { - Log.info("Callback {} registering with memo {}", currentConnection.getUUID(), config.getMemo()); - } - GlobalRequest req = new GlobalRequest("memo@jadaptive.com", - currentConnection, ByteArrayWriter.encodeString(config.getMemo())); - currentConnection.sendGlobalRequest(req, false); - app.onClientConnected(this, currentConnection); - if(Log.isInfoEnabled()) { - Log.info("Client is connected to {}:{}", hostname, port); - } - break; - } else { - if(Log.isInfoEnabled()) { - Log.info("Could not authenticate to {}:{}", hostname, port); - } - currentConnection.disconnect(); - } - - } - - if(Objects.isNull(currentConnection)) { - - if(Log.isInfoEnabled()) { - Log.info("Connection did not complete to {}:{}", hostname, port); - } - - if(!config.isReconnect()) { - break; - } - - try { - long interval = config.getReconnectIntervalMs(); - - if(Log.isInfoEnabled()) { - Log.info("Will reconnect to {}:{} in {} seconds", hostname, port, interval / 1000); - } - Thread.sleep(interval); - } catch (InterruptedException e) { - } - } - } catch(Throwable e) { - Log.error("{} on {}:{}", - e, - e.getMessage(), - config.getServerHost(), - config.getServerPort()); - - if(Objects.nonNull(currentConnection)) { - currentConnection.disconnect(); + + SshConnection con = null; + + future = app.getSshEngine().connect( + hostname, + port, + createContext(config)); + + future.waitFor(30000L); + if(future.isDone() && future.isSuccess()) { + + con = future.getConnection(); + con.setProperty(CallbackClient.CALLBACK_CLIENT, CallbackSession.this); + con.getAuthenticatedFuture().waitFor(30000L); + + if(con.getAuthenticatedFuture().isDone() + && con.getAuthenticatedFuture().isSuccess()) { + + if(Log.isInfoEnabled()) { + Log.info("Callback {} registering with memo {}", con.getUUID(), config.getMemo()); } - - long interval = config.getReconnectIntervalMs(); + GlobalRequest req = new GlobalRequest("memo@jadaptive.com", + con, ByteArrayWriter.encodeString(config.getMemo())); + con.sendGlobalRequest(req, false); + app.onClientConnected(this, con); if(Log.isInfoEnabled()) { - Log.info("Reconnecting to {}:{} in {} seconds", hostname, port, interval / 1000); + Log.info("Client is connected to {}:{}", hostname, port); } - try { - Thread.sleep(interval); - } catch (InterruptedException e1) { + + con.getDisconnectFuture().waitForever(); + } else { + if(Log.isInfoEnabled()) { + Log.info("Could not authenticate to {}:{}", hostname, port); } + con.disconnect(); } + + app.onClientStop(this, con); + con.removeProperty(CallbackClient.CALLBACK_CLIENT); + app.clients.remove(this); } + + } protected ProtocolContext createContext(CallbackConfiguration config) throws IOException, SshException { - SshServerContext ctx = app.createContext(app.getSshEngine().getContext(), config); - ctx.addStateListener(new ServerConnectionStateListener() { - public void connected(SshConnection con) { - con.setProperty(CallbackClient.CALLBACK_CLIENT, CallbackSession.this); - } - }); - return ctx; + return app.createContext(app.getSshEngine().getContext(), config); } public void disconnect() { From 50ee4de6e6979f4effdaed1b2532525b65ea414e Mon Sep 17 00:00:00 2001 From: Brett Smith Date: Sat, 8 Jun 2024 14:10:22 +0100 Subject: [PATCH 30/51] Start the SshEngine during construction of client. --- .../java/com/sshtools/callback/client/CallbackClient.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackClient.java b/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackClient.java index d237772e..a014cc57 100644 --- a/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackClient.java +++ b/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackClient.java @@ -23,6 +23,7 @@ */ import java.io.IOException; +import java.io.UncheckedIOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -70,6 +71,11 @@ public CallbackClient() { executor = getExecutorService(); EventServiceImplementation.getInstance().addListener(new DisconnectionListener()); channelFactory = new DefaultServerChannelFactory(); + try { + ssh.startup(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } } public SshEngine getSshEngine() { From b76088db863a3f7bdb856263f160c3efb3255c26 Mon Sep 17 00:00:00 2001 From: Brett Smith Date: Sat, 8 Jun 2024 15:57:33 +0100 Subject: [PATCH 31/51] Tidy up, null guard. --- .../callback/client/CallbackClient.java | 40 +++---------------- .../callback/client/CallbackSession.java | 12 ------ 2 files changed, 6 insertions(+), 46 deletions(-) diff --git a/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackClient.java b/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackClient.java index a014cc57..f490d4bd 100644 --- a/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackClient.java +++ b/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackClient.java @@ -173,50 +173,22 @@ class DisconnectionListener implements EventListener { @Override public void processEvent(Event evt) { - switch(evt.getId()) { case EventCodes.EVENT_DISCONNECTED: - final SshConnection con = (SshConnection)evt.getAttribute(EventCodes.ATTRIBUTE_CONNECTION); - if(!executor.isShutdown()) { executor.execute(new Runnable() { public void run() { if(con.containsProperty(CALLBACK_CLIENT)) { CallbackSession client = (CallbackSession) con.getProperty(CALLBACK_CLIENT); - - if(Log.isInfoEnabled()) { + if(client != null) {if(Log.isInfoEnabled()) { Log.info("Disconnected from {}:{}" , - client.getConfig().getServerHost(), - client.getConfig().getServerPort()); + client.getConfig().getServerHost(), + client.getConfig().getServerPort()); + } + con.removeProperty(CALLBACK_CLIENT); + clients.remove(client); } - -// onClientStop(client, con); - con.removeProperty(CALLBACK_CLIENT); - clients.remove(client); -// -// if(!client.isStopped() && client.getConfig().isReconnect()) { -// while(getSshEngine().isStarted()) { -// -// if(Log.isInfoEnabled()) { -// Log.info("Will connect again to {}:{} in {} seconds" , -// client.getConfig().getServerHost(), -// client.getConfig().getServerPort(), -// client.getConfig().getReconnectIntervalMs() / 1000); -// } -// try { -// try { -// Thread.sleep(client.getConfig().getReconnectIntervalMs()); -// } catch (InterruptedException e1) { -// } -// client.connect(); -// break; -// } catch (IOException e) { -// } -// } -// } else { -// stop(); -// } } } }); diff --git a/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackSession.java b/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackSession.java index b2cf3fd0..29998b09 100644 --- a/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackSession.java +++ b/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackSession.java @@ -25,15 +25,12 @@ import java.io.IOException; import java.util.HashMap; import java.util.Map; -import java.util.Objects; import com.sshtools.common.logger.Log; import com.sshtools.common.ssh.GlobalRequest; import com.sshtools.common.ssh.SshConnection; import com.sshtools.common.ssh.SshException; import com.sshtools.common.util.ByteArrayWriter; -import com.sshtools.server.ServerConnectionStateListener; -import com.sshtools.server.SshServerContext; import com.sshtools.synergy.nio.ConnectRequestFuture; import com.sshtools.synergy.nio.DisconnectRequestFuture; import com.sshtools.synergy.nio.ProtocolContext; @@ -103,15 +100,6 @@ public void connect() throws IOException, SshException { Log.info("Connecting to {}:{}", hostname, port); } -// synchronized(app) { -// if(!app.getSshEngine().isStarted() && !app.getSshEngine().isStarting()) { -// if(!app.getSshEngine().startup()) { -// throw new IOException("SSH Engine failed to start"); -// } -// } -// } - - SshConnection con = null; future = app.getSshEngine().connect( From f39ab470a1fee4eb2b592c5ab0dc8ac594ec4754 Mon Sep 17 00:00:00 2001 From: Brett Smith Date: Wed, 12 Jun 2024 16:03:50 +0100 Subject: [PATCH 32/51] Could not get at last error. --- .../callback/client/CallbackSession.java | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackSession.java b/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackSession.java index 29998b09..018a26df 100644 --- a/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackSession.java +++ b/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackSession.java @@ -52,6 +52,8 @@ public class CallbackSession implements Runnable { int port; Map attributes = new HashMap(); int numberOfAuthenticationErrors = 0; + long reconnectStartedAt = -1; + Throwable exception; public CallbackSession(CallbackConfiguration config, CallbackClient app, String hostname, int port) throws IOException { this.config = config; @@ -71,6 +73,7 @@ public void run() { try { connect(); } catch (IOException | SshException e) { + exception = e; Log.error("Connection failed to {}:{}", hostname, port); } @@ -81,6 +84,7 @@ public void run() { if(!config.isReconnect()) { break; } + reconnectStartedAt = System.currentTimeMillis(); try { long interval = config.getReconnectIntervalMs(); @@ -90,9 +94,23 @@ public void run() { } Thread.sleep(interval); } catch (InterruptedException e) { + } finally { + reconnectStartedAt = -1; } } } + + public Throwable getLastError() { + return exception; + } + + public long getTimeRemainingUntilReconnect() { + if(reconnectStartedAt == -1) + return -1; + else { + return Math.min(config.getReconnectIntervalMs(), Math.max(0, config.getReconnectIntervalMs() - ( System.currentTimeMillis() - reconnectStartedAt ))); + } + } public void connect() throws IOException, SshException { @@ -111,6 +129,23 @@ public void connect() throws IOException, SshException { if(future.isDone() && future.isSuccess()) { con = future.getConnection(); + + if(!con.isConnected() || con.isDisconnecting()) { + Throwable exception = app.getSshEngine().getLastError(); + if(exception == null) { + throw new IOException("Failed to connect."); + } + else if(exception instanceof IOException) { + throw (IOException)exception; + } + else if(exception instanceof SshException) { + throw (SshException)exception; + } + else { + throw new IOException("Failed to connect.", exception); + } + } + con.setProperty(CallbackClient.CALLBACK_CLIENT, CallbackSession.this); con.getAuthenticatedFuture().waitFor(30000L); @@ -128,11 +163,15 @@ public void connect() throws IOException, SshException { Log.info("Client is connected to {}:{}", hostname, port); } + exception = null; + con.getDisconnectFuture().waitForever(); } else { if(Log.isInfoEnabled()) { Log.info("Could not authenticate to {}:{}", hostname, port); } + + exception = new IOException("Authentication failed."); con.disconnect(); } From 04ce445191b93edec355eb914c35a213a8f47249 Mon Sep 17 00:00:00 2001 From: Lee Painter Date: Tue, 18 Jun 2024 17:53:39 +0100 Subject: [PATCH 33/51] Handle does not log sending of close message in DEBUG level --- .../src/main/java/com/sshtools/client/sftp/SftpHandle.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpHandle.java b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpHandle.java index 9e10ec6a..a3ad35a8 100644 --- a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpHandle.java +++ b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpHandle.java @@ -243,6 +243,9 @@ public void close() throws IOException { msg.writeInt(requestId.longValue()); msg.writeBinaryString(handle); + if(Log.isDebugEnabled()) { + Log.debug("Sending SSH_FXP_CLOSE for {}", file.getFilename()); + } sftp.sendMessage(msg); sftp.getOKRequestStatus(requestId); From d3433e01063d1ec3e12a668385b9d584f7a729de Mon Sep 17 00:00:00 2001 From: Lee Painter Date: Thu, 20 Jun 2024 22:13:25 +0100 Subject: [PATCH 34/51] More logging improvements and length check in SftpFileInputStream --- .../common/sftp/SftpStatusException.java | 78 +++++++++++++++ .../com/sshtools/client/sftp/SftpChannel.java | 95 +++++++++---------- .../com/sshtools/client/sftp/SftpClient.java | 6 +- .../client/sftp/SftpFileInputStream.java | 39 +++++++- .../com/sshtools/client/sftp/SftpHandle.java | 4 +- 5 files changed, 165 insertions(+), 57 deletions(-) diff --git a/maverick-base/src/main/java/com/sshtools/common/sftp/SftpStatusException.java b/maverick-base/src/main/java/com/sshtools/common/sftp/SftpStatusException.java index fc505a42..c5a68a13 100644 --- a/maverick-base/src/main/java/com/sshtools/common/sftp/SftpStatusException.java +++ b/maverick-base/src/main/java/com/sshtools/common/sftp/SftpStatusException.java @@ -203,5 +203,83 @@ public static String getStatusText(int status) { return "Unknown status type " + String.valueOf(status); } } + + public static String getStatusMessage(int status) { + switch (status) { + case SSH_FX_OK: + return "SSH_FX_OK"; + case SSH_FX_EOF: + return "SSH_FX_EOF"; + case SSH_FX_NO_SUCH_FILE: + return "SSH_FX_NO_SUCH_FILE"; + case SSH_FX_PERMISSION_DENIED: + return "SSH_FX_PERMISSION_DENIED"; + case SSH_FX_FAILURE: + return "SSH_FX_FAILURE"; + case SSH_FX_BAD_MESSAGE: + return "SSH_FX_BAD_MESSAGE"; + case SSH_FX_NO_CONNECTION: + return "SSH_FX_NO_CONNECTION"; + case SSH_FX_CONNECTION_LOST: + return "SSH_FX_CONNECTION_LOST"; + case SSH_FX_OP_UNSUPPORTED: + return "SSH_FX_OP_UNSUPPORTED"; + case SSH_FX_INVALID_HANDLE: + case INVALID_HANDLE: + return "SSH_FX_INVALID_HANDLE"; + case SSH_FX_NO_SUCH_PATH: + return "SSH_FX_NO_SUCH_PATH"; + case SSH_FX_FILE_ALREADY_EXISTS: + return "SSH_FX_FILE_ALREADY_EXISTS"; + case SSH_FX_WRITE_PROTECT: + return "SSH_FX_WRITE_PROTECT"; + case SSH_FX_NO_MEDIA: + return "SSH_FX_NO_MEDIA"; + case SSH_FX_NO_SPACE_ON_FILESYSTEM: + return "SSH_FX_NO_SPACE_ON_FILESYSTEM"; + case SSH_FX_QUOTA_EXCEEDED: + return "SSH_FX_QUOTA_EXCEEDED"; + case SSH_FX_UNKNOWN_PRINCIPAL: + return "SSH_FX_UNKNOWN_PRINCIPAL"; + case SSH_FX_LOCK_CONFLICT: + return "SSH_FX_LOCK_CONFLICT"; + case SSH_FX_DIR_NOT_EMPTY: + return "SSH_FX_DIR_NOT_EMPTY"; + case SSH_FX_NOT_A_DIRECTORY: + return "SSH_FX_NOT_A_DIRECTORY"; + case SSH_FX_INVALID_FILENAME: + return "SSH_FX_INVALID_FILENAME"; + case SSH_FX_LINK_LOOP: + return "SSH_FX_LINK_LOOP"; + case SSH_FX_CANNOT_DELETE: + return "SSH_FX_CANNOT_DELETE"; + case SSH_FX_INVALID_PARAMETER: + return "SSH_FX_INVALID_PARAMETER"; + case SSH_FX_FILE_IS_A_DIRECTORY: + return "SSH_FX_FILE_IS_A_DIRECTORY"; + case SSH_FX_BYTE_RANGE_LOCK_CONFLICT: + return "SSH_FX_BYTE_RANGE_LOCK_CONFLICT"; + case SSH_FX_BYTE_RANGE_LOCK_REFUSED: + return "SSH_FX_BYTE_RANGE_LOCK_REFUSED"; + case SSH_FX_DELETE_PENDING: + return "SSH_FX_DELETE_PENDING"; + case SSH_FX_FILE_CORRUPT: + return "SSH_FX_FILE_CORRUPT"; + case SSH_FX_OWNER_INVALID: + return "SSH_FX_OWNER_INVALID"; + case SSH_FX_GROUP_INVALID: + return "SSH_FX_GROUP_INVALID"; + case SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK: + return "SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK"; + case INVALID_RESUME_STATE: + return "INVALID_RESUME_STATE"; + case ATTRIBUTE_BITS_NOT_AVAILABLE: + return "ATTRIBUTE_BITS_NOT_AVAILABLE"; + case BAD_API_USAGE: + return "BAD_API_USAGE"; + default: + return "SSH_FX_UNKNOWN/" + String.valueOf(status); + } + } } diff --git a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java index 28c4c6f2..73a0e610 100644 --- a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java +++ b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java @@ -555,7 +555,9 @@ public SftpMessage getResponse(UnsignedInteger32 requestId) throws SshException } } - return (SftpMessage) responses.remove(requestId); + SftpMessage m = (SftpMessage) responses.remove(requestId); + + return m; } @@ -670,17 +672,8 @@ public void getOKRequestStatus(UnsignedInteger32 requestId) SftpMessage bar = getResponse(requestId); try { if (bar.getType() == SSH_FXP_STATUS) { - int status = (int) bar.readInt(); - if (status == SftpStatusException.SSH_FX_OK) { - return; - } - - if (version >= 3) { - String msg = bar.readString(); - throw new SftpStatusException(status, msg); - } + int status = processStatusResponse(bar); throw new SftpStatusException(status); - } close(); throw new SshException( @@ -1251,12 +1244,8 @@ public String getSymbolicLinkTarget(String linkpath) SftpMessage fileMsg = getResponse(requestId); if (fileMsg.getType() == SSH_FXP_STATUS) { - int status = (int) fileMsg.readInt(); - if (version >= 3) { - throw new SftpStatusException(status, fileMsg.readString()); - } + int status = processStatusResponse(fileMsg); throw new SftpStatusException(status); - } try { SftpFile[] files = extractFiles(fileMsg, null); @@ -1323,6 +1312,9 @@ public SftpFile getSingleFileResponse(SftpMessage bar, String messageName) throw if (bar.getType() == SSH_FXP_NAME) { SftpFile[] files = extractFiles(bar, null); + if(Log.isDebugEnabled()) { + Log.debug("Received SSH_FXP_NAME with {} files", files.length); + } if (files.length != 1) { close(); throw new SshException( @@ -1333,11 +1325,7 @@ public SftpFile getSingleFileResponse(SftpMessage bar, String messageName) throw return files[0]; } else if (bar.getType() == SSH_FXP_STATUS) { - int status = (int) bar.readInt(); - if (version >= 3) { - String desc = bar.readString(); - throw new SftpStatusException(status, desc); - } + int status = processStatusResponse(bar); throw new SftpStatusException(status); } else { close(); @@ -1916,7 +1904,7 @@ protected SftpFileAttributes getAttributes(String path, int messageId) SftpMessage bar = getResponse(requestId); try { - return extractAttributes(bar); + return extractAttributes(bar, path); } finally { bar.release(); } @@ -1927,19 +1915,16 @@ protected SftpFileAttributes getAttributes(String path, int messageId) } } - SftpFileAttributes extractAttributes(SftpMessage bar) + SftpFileAttributes extractAttributes(SftpMessage bar, String filename) throws SftpStatusException, SshException { try { if (bar.getType() == SSH_FXP_ATTRS) { + if(Log.isDebugEnabled()) { + Log.debug("Received SSH_FXP_ATTRS for {}", filename); + } return SftpFileAttributesBuilder.of(bar, getVersion(), getCharsetEncoding()).build(); } else if (bar.getType() == SSH_FXP_STATUS) { - int status = (int) bar.readInt(); - - // Only read the message string if the version is >= 3 - if (version >= 3) { - String msg = bar.readString(); - throw new SftpStatusException(status, msg); - } + int status = processStatusResponse(bar); throw new SftpStatusException(status); } else { close(); @@ -1954,6 +1939,26 @@ SftpFileAttributes extractAttributes(SftpMessage bar) } } + int processStatusResponse(SftpMessage bar) throws SftpStatusException, IOException { + + int status = (int) bar.readInt(); + + if (version >= 3) { + String desc = bar.readString(); + if(Log.isDebugEnabled()) { + Log.debug("Received {} with message {}", + SftpStatusException.getStatusMessage(status), desc); + } + throw new SftpStatusException(status, desc); + } + + if(Log.isDebugEnabled()) { + Log.debug("Received {}", SftpStatusException.getStatusMessage(status)); + } + + return status; + } + /** * Get the attributes of a file. * @@ -2028,14 +2033,12 @@ public SftpMessage getExtendedReply(UnsignedInteger32 requestId) throws SftpStat public SftpMessage getExtendedReply(SftpMessage bar) throws SftpStatusException, SshException { try { if (bar.getType() == SSH_FXP_EXTENDED_REPLY) { + if(Log.isDebugEnabled()) { + Log.debug("Received SSH_FX_EXTENDED_REPLY"); + } return bar; } else if (bar.getType() == SSH_FXP_STATUS) { - int status = (int) bar.readInt(); - - if (version >= 3) { - String msg = bar.readString(); - throw new SftpStatusException(status, msg); - } + int status = processStatusResponse(bar); throw new SftpStatusException(status); } else { close(); @@ -2065,14 +2068,12 @@ public byte[] getHandleResponse(SftpMessage bar) try { if (bar.getType() == SSH_FXP_HANDLE) { + if(Log.isDebugEnabled()) { + Log.debug("Received SSH_FXP_HANDLE"); + } return bar.readBinaryString(); } else if (bar.getType() == SSH_FXP_STATUS) { - int status = (int) bar.readInt(); - - if (version >= 3) { - String msg = bar.readString(); - throw new SftpStatusException(status, msg); - } + int status = processStatusResponse(bar); throw new SftpStatusException(status); } else { close(); @@ -2093,14 +2094,12 @@ SftpMessage getExtensionResponse(UnsignedInteger32 requestId) SftpMessage bar = getResponse(requestId); try { if (bar.getType() == SSH_FXP_EXTENDED_REPLY) { + if(Log.isDebugEnabled()) { + Log.debug("Received SSH_FXP_EXTENDED_REPLY"); + } return bar; } else if (bar.getType() == SSH_FXP_STATUS) { - int status = (int) bar.readInt(); - - if (version >= 3) { - String msg = bar.readString(); - throw new SftpStatusException(status, msg); - } + int status = processStatusResponse(bar); throw new SftpStatusException(status); } else { close(); diff --git a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpClient.java b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpClient.java index c1f760bd..3e7df00a 100644 --- a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpClient.java +++ b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpClient.java @@ -3911,11 +3911,7 @@ public StatVfs statVFS(String path) throws SshException, SftpStatusException { UnsignedInteger32 requestId = channel.sendExtensionMessage("statvfs@openssh.com", msg.toByteArray()); SftpMessage response = channel.getResponse(requestId); if (response.getType() == SftpChannel.SSH_FXP_STATUS) { - int status = (int) response.readInt(); - if (sftp.version >= 3) { - String errmsg = response.readString(); - throw new SftpStatusException(status, errmsg); - } + int status = sftp.processStatusResponse(response); throw new SftpStatusException(status); } else { return new StatVfs(response); diff --git a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpFileInputStream.java b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpFileInputStream.java index aaf1634f..109748bb 100644 --- a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpFileInputStream.java +++ b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpFileInputStream.java @@ -26,10 +26,12 @@ import java.io.InputStream; import java.util.Vector; +import com.sshtools.common.logger.Log; import com.sshtools.common.sftp.SftpStatusException; import com.sshtools.common.ssh.SshException; import com.sshtools.common.ssh.SshIOException; import com.sshtools.common.util.UnsignedInteger32; +import com.sshtools.common.util.UnsignedInteger64; /** * An InputStream to read the contents of a remote file. @@ -44,6 +46,7 @@ public class SftpFileInputStream extends InputStream { private int currentMessageRemaining; private boolean isEOF = false; private boolean error = false; + private UnsignedInteger64 length; /** * @@ -77,6 +80,7 @@ public SftpFileInputStream(SftpFile file, long position) throws SftpStatusExcept this.sftp = file.getSFTPChannel(); this.handle = file.openFile(SftpChannel.OPEN_READ); this.position = position; + this.length = handle.getAttributes().size(); } /** @@ -99,10 +103,11 @@ public SftpFileInputStream(SftpFile file, long position) throws SftpStatusExcept * @throws SftpStatusException * @throws SshException */ - SftpFileInputStream(SftpHandle handle, long position) { + SftpFileInputStream(SftpHandle handle, long position) throws SftpStatusException, SshException { this.handle = handle; this.position = position; this.sftp = handle.getSFTPChannel(); + this.length = handle.getAttributes().size(); } /* @@ -174,19 +179,36 @@ private void bufferNextMessage() throws SshException, IOException, currentMessage = sftp.getResponse(requestid); if (currentMessage.getType() == SftpChannel.SSH_FXP_DATA) { + if(Log.isDebugEnabled()) { + Log.debug("Received SSH_FXP_DATA for {}", handle.getFile().getFilename()); + } currentMessageRemaining = (int) currentMessage.readInt(); } else if (currentMessage.getType() == SftpChannel.SSH_FXP_STATUS) { try { int status = (int) currentMessage.readInt(); if (status == SftpStatusException.SSH_FX_EOF) { + if(Log.isDebugEnabled()) { + Log.debug("Received SSH_FX_EOF for {}", handle.getFile().getFilename()); + } isEOF = true; return; } if (sftp.getVersion() >= 3) { String desc = currentMessage.readString(); + if(Log.isDebugEnabled()) { + Log.debug("Received SSH_FXP_STATUS {}/{} for {}", + status, + desc, + handle.getFile().getFilename()); + } + throw new IOException(desc); } + if(Log.isDebugEnabled()) { + Log.debug("Received SSH_FXP_STATUS {} for {}", + status, handle.getFile().getFilename()); + } throw new IOException("Unexpected status " + status); } finally { currentMessage.release(); @@ -207,7 +229,20 @@ private void bufferNextMessage() throws SshException, IOException, } private void bufferMoreData() throws SftpStatusException, SshException { - while (outstandingRequests.size() < 100) { + + /** + * Read up to length of file + */ + while (outstandingRequests.size() < 100 && length.longValue() > position) { + outstandingRequests.addElement(handle.postReadRequest(position, 32768)); + position += 32768; + } + + /** + * If there are no requests then add one to ensure we are still reading if file size + * has changed. + */ + if(outstandingRequests.isEmpty()) { outstandingRequests.addElement(handle.postReadRequest(position, 32768)); position += 32768; } diff --git a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpHandle.java b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpHandle.java index a3ad35a8..83d57cbe 100644 --- a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpHandle.java +++ b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpHandle.java @@ -317,7 +317,7 @@ public int listChildren(List children) throws SftpStatusException, Ssh SftpFile[] files = sftp.extractFiles(bar, file.getAbsolutePath()); if (Log.isDebugEnabled()) { - Log.debug("THere are {} results in this packet", files.length); + Log.debug("There are {} results in this packet", files.length); } for (int i = 0; i < files.length; i++) { @@ -417,7 +417,7 @@ public SftpFileAttributes getAttributes() throws SftpStatusException, SshExcepti SftpMessage attrMessage = sftp.getResponse(requestId); try { - return sftp.extractAttributes(attrMessage); + return sftp.extractAttributes(attrMessage, getFile().getFilename()); } finally { attrMessage.release(); } From 3585fe6926878ceab1553543818f4addaa081f69 Mon Sep 17 00:00:00 2001 From: Brett Smith Date: Thu, 20 Jun 2024 23:10:19 +0100 Subject: [PATCH 35/51] Allow the memo to be updated. Have also added `private` to all member variables used in the classes in this module that were package protected. As is nearly always the cases with code of any maturity, this exposes a whole bunch of unused members, and 99% of them did not need to be package protected anyway. --- ...allbackAuthenticationMechanismFactory.java | 3 +- .../callback/client/CallbackClient.java | 32 ++++++++++++---- .../client/CallbackConfiguration.java | 20 +++++----- .../client/CallbackServerAuthentication.java | 2 +- .../callback/client/CallbackSession.java | 38 ++++++++++--------- .../client/MutualCallbackAuthentication.java | 8 ++-- .../MutualCallbackAuthenticationProvider.java | 3 +- 7 files changed, 61 insertions(+), 45 deletions(-) diff --git a/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackAuthenticationMechanismFactory.java b/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackAuthenticationMechanismFactory.java index 2c3c274b..a7c07469 100644 --- a/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackAuthenticationMechanismFactory.java +++ b/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackAuthenticationMechanismFactory.java @@ -31,9 +31,8 @@ import com.sshtools.common.sshd.AbstractServerTransport; public class CallbackAuthenticationMechanismFactory extends DefaultAuthenticationMechanismFactory { - - MutualCallbackAuthenticationProvider provider; + private final MutualCallbackAuthenticationProvider provider; public CallbackAuthenticationMechanismFactory(MutualCallbackAuthenticationProvider provider) { this.provider = provider; diff --git a/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackClient.java b/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackClient.java index f490d4bd..bc8b7f9b 100644 --- a/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackClient.java +++ b/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackClient.java @@ -27,6 +27,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -58,14 +59,13 @@ public class CallbackClient implements ChannelFactoryListener public static final String CALLBACK_CLIENT = "callbackClient"; - SshEngine ssh = new SshEngine(); - Set clients = new HashSet(); - ExecutorService executor; - List hostKeys = new ArrayList<>(); - ChannelFactory channelFactory; - List defaultPolicies = new ArrayList<>(); - FileFactory fileFactory; - String welcomeText = "Callback Client"; + private SshEngine ssh = new SshEngine(); + private ExecutorService executor; + private List hostKeys = new ArrayList<>(); + private ChannelFactory channelFactory; + private List defaultPolicies = new ArrayList<>(); + private FileFactory fileFactory; + private Set clients = Collections.synchronizedSet(new HashSet()); public CallbackClient() { executor = getExecutorService(); @@ -101,6 +101,22 @@ public synchronized CallbackSession start(CallbackConfiguration config, String h return session; } + public void updateMemo(String memo) throws IOException { + synchronized(clients) { + IOException exception = null; + for(var clnt : clients) { + try { + clnt.updateMemo(memo); + } catch (IOException e) { + if(exception == null) + exception = e; + } + } + if(exception != null) + throw exception; + } + } + public synchronized void start(CallbackSession client) { if(Log.isInfoEnabled()) { diff --git a/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackConfiguration.java b/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackConfiguration.java index 67172a02..f09b9d65 100644 --- a/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackConfiguration.java +++ b/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackConfiguration.java @@ -32,17 +32,15 @@ public class CallbackConfiguration { public static final String DEFAULT_CALLBACK_ID = "CallbackClient"; - String agentName; - String serverHost; - int serverPort = 22; - String remoteUUID; - String localUUID; - Long reconnectIntervalMs; - SshKeyPair privateKey; - SshPublicKey publicKey; - String memo; - String callbackIdentifier = DEFAULT_CALLBACK_ID; - boolean reconnect = true; + private String agentName; + private String serverHost; + private int serverPort = 22; + private Long reconnectIntervalMs; + private SshKeyPair privateKey; + private SshPublicKey publicKey; + private String memo; + private String callbackIdentifier = DEFAULT_CALLBACK_ID; + private boolean reconnect = true; Map properties = new HashMap<>(); diff --git a/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackServerAuthentication.java b/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackServerAuthentication.java index 79de3b96..1ff5da3d 100644 --- a/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackServerAuthentication.java +++ b/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackServerAuthentication.java @@ -31,7 +31,7 @@ public class CallbackServerAuthentication extends AbstractPublicKeyAuthenticationProvider { - Set serverKeys; + private Set serverKeys; CallbackServerAuthentication(Set serverKeys) throws IOException { diff --git a/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackSession.java b/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackSession.java index 018a26df..e1072d12 100644 --- a/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackSession.java +++ b/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/CallbackSession.java @@ -28,12 +28,12 @@ import com.sshtools.common.logger.Log; import com.sshtools.common.ssh.GlobalRequest; -import com.sshtools.common.ssh.SshConnection; import com.sshtools.common.ssh.SshException; import com.sshtools.common.util.ByteArrayWriter; import com.sshtools.synergy.nio.ConnectRequestFuture; import com.sshtools.synergy.nio.DisconnectRequestFuture; import com.sshtools.synergy.nio.ProtocolContext; +import com.sshtools.synergy.ssh.Connection; import com.sshtools.synergy.ssh.TransportProtocol; /** @@ -43,17 +43,17 @@ */ public class CallbackSession implements Runnable { - CallbackConfiguration config; - CallbackClient app; - ConnectRequestFuture future; - - boolean isStopped = false; - String hostname; - int port; - Map attributes = new HashMap(); - int numberOfAuthenticationErrors = 0; - long reconnectStartedAt = -1; - Throwable exception; + private final CallbackClient app; + private final String hostname; + private final int port; + + private CallbackConfiguration config; + private ConnectRequestFuture future; + private boolean isStopped = false; + private Map attributes = new HashMap(); + private long reconnectStartedAt = -1; + private Throwable exception; + private Connection con; public CallbackSession(CallbackConfiguration config, CallbackClient app, String hostname, int port) throws IOException { this.config = config; @@ -111,6 +111,12 @@ public long getTimeRemainingUntilReconnect() { return Math.min(config.getReconnectIntervalMs(), Math.max(0, config.getReconnectIntervalMs() - ( System.currentTimeMillis() - reconnectStartedAt ))); } } + + public void updateMemo(String memo) throws IOException { + GlobalRequest req = new GlobalRequest("memo@jadaptive.com", + con, ByteArrayWriter.encodeString(config.getMemo())); + con.sendGlobalRequest(req, false); + } public void connect() throws IOException, SshException { @@ -118,8 +124,6 @@ public void connect() throws IOException, SshException { Log.info("Connecting to {}:{}", hostname, port); } - SshConnection con = null; - future = app.getSshEngine().connect( hostname, port, @@ -155,9 +159,7 @@ else if(exception instanceof SshException) { if(Log.isInfoEnabled()) { Log.info("Callback {} registering with memo {}", con.getUUID(), config.getMemo()); } - GlobalRequest req = new GlobalRequest("memo@jadaptive.com", - con, ByteArrayWriter.encodeString(config.getMemo())); - con.sendGlobalRequest(req, false); + updateMemo(config.getMemo()); app.onClientConnected(this, con); if(Log.isInfoEnabled()) { Log.info("Client is connected to {}:{}", hostname, port); @@ -177,7 +179,7 @@ else if(exception instanceof SshException) { app.onClientStop(this, con); con.removeProperty(CallbackClient.CALLBACK_CLIENT); - app.clients.remove(this); + app.getClients() .remove(this); } diff --git a/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/MutualCallbackAuthentication.java b/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/MutualCallbackAuthentication.java index 7c67ac28..4207a113 100644 --- a/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/MutualCallbackAuthentication.java +++ b/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/MutualCallbackAuthentication.java @@ -45,10 +45,10 @@ public class MutualCallbackAuthentication implements Authenti public static final int SSH_MSG_USERAUTH_SIGNED_CHALLENGE = 60; - AbstractServerTransport transport; - AbstractAuthenticationProtocol authentication; - SshConnection con; - MutualCallbackAuthenticationProvider provider; + private AbstractServerTransport transport; + private AbstractAuthenticationProtocol authentication; + private SshConnection con; + private MutualCallbackAuthenticationProvider provider; public static final String AUTHENTICATION_METHOD = "publickey"; diff --git a/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/MutualCallbackAuthenticationProvider.java b/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/MutualCallbackAuthenticationProvider.java index 742b50eb..0f2a44bb 100644 --- a/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/MutualCallbackAuthenticationProvider.java +++ b/maverick-synergy-callback-client/src/main/java/com/sshtools/callback/client/MutualCallbackAuthenticationProvider.java @@ -31,7 +31,8 @@ public class MutualCallbackAuthenticationProvider implements Authenticator { public static final String MUTUAL_KEY_AUTHENTICATION = "mutual-key-auth@sshtools.com"; - MutualKeyAuthenticatonStore authenticationStore; + + private final MutualKeyAuthenticatonStore authenticationStore; public MutualCallbackAuthenticationProvider(MutualKeyAuthenticatonStore authenticationStore) { this.authenticationStore = authenticationStore; From d99260535ad382f8ef826c27ab5914090d476b51 Mon Sep 17 00:00:00 2001 From: Lee Painter Date: Thu, 20 Jun 2024 23:38:40 +0100 Subject: [PATCH 36/51] Get existing attributes from file object that are already there. --- .../java/com/sshtools/client/sftp/SftpFileInputStream.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpFileInputStream.java b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpFileInputStream.java index 109748bb..f12678b2 100644 --- a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpFileInputStream.java +++ b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpFileInputStream.java @@ -78,9 +78,10 @@ public SftpFileInputStream(SftpFile file) throws SftpStatusException, @Deprecated(since = "3.1.0", forRemoval = true) public SftpFileInputStream(SftpFile file, long position) throws SftpStatusException, SshException { this.sftp = file.getSFTPChannel(); + this.length = file.getAttributes().size(); this.handle = file.openFile(SftpChannel.OPEN_READ); this.position = position; - this.length = handle.getAttributes().size(); + } /** @@ -107,7 +108,7 @@ public SftpFileInputStream(SftpFile file, long position) throws SftpStatusExcept this.handle = handle; this.position = position; this.sftp = handle.getSFTPChannel(); - this.length = handle.getAttributes().size(); + this.length = handle.getFile().getAttributes().size(); } /* From 3b21094e6fdc17643ef213450fa1a69d3051044a Mon Sep 17 00:00:00 2001 From: Brett Smith Date: Fri, 21 Jun 2024 11:58:33 +0100 Subject: [PATCH 37/51] Revert "More logging improvements and length check in SftpFileInputStream". @ludup, please see https://logonboxlimited.slack.com/archives/GNCA26336/p1718966835514609 This reverts commit d3433e01063d1ec3e12a668385b9d584f7a729de. --- .../common/sftp/SftpStatusException.java | 78 --------------- .../com/sshtools/client/sftp/SftpChannel.java | 95 ++++++++++--------- .../com/sshtools/client/sftp/SftpClient.java | 6 +- .../client/sftp/SftpFileInputStream.java | 40 +------- .../com/sshtools/client/sftp/SftpHandle.java | 4 +- 5 files changed, 57 insertions(+), 166 deletions(-) diff --git a/maverick-base/src/main/java/com/sshtools/common/sftp/SftpStatusException.java b/maverick-base/src/main/java/com/sshtools/common/sftp/SftpStatusException.java index c5a68a13..fc505a42 100644 --- a/maverick-base/src/main/java/com/sshtools/common/sftp/SftpStatusException.java +++ b/maverick-base/src/main/java/com/sshtools/common/sftp/SftpStatusException.java @@ -203,83 +203,5 @@ public static String getStatusText(int status) { return "Unknown status type " + String.valueOf(status); } } - - public static String getStatusMessage(int status) { - switch (status) { - case SSH_FX_OK: - return "SSH_FX_OK"; - case SSH_FX_EOF: - return "SSH_FX_EOF"; - case SSH_FX_NO_SUCH_FILE: - return "SSH_FX_NO_SUCH_FILE"; - case SSH_FX_PERMISSION_DENIED: - return "SSH_FX_PERMISSION_DENIED"; - case SSH_FX_FAILURE: - return "SSH_FX_FAILURE"; - case SSH_FX_BAD_MESSAGE: - return "SSH_FX_BAD_MESSAGE"; - case SSH_FX_NO_CONNECTION: - return "SSH_FX_NO_CONNECTION"; - case SSH_FX_CONNECTION_LOST: - return "SSH_FX_CONNECTION_LOST"; - case SSH_FX_OP_UNSUPPORTED: - return "SSH_FX_OP_UNSUPPORTED"; - case SSH_FX_INVALID_HANDLE: - case INVALID_HANDLE: - return "SSH_FX_INVALID_HANDLE"; - case SSH_FX_NO_SUCH_PATH: - return "SSH_FX_NO_SUCH_PATH"; - case SSH_FX_FILE_ALREADY_EXISTS: - return "SSH_FX_FILE_ALREADY_EXISTS"; - case SSH_FX_WRITE_PROTECT: - return "SSH_FX_WRITE_PROTECT"; - case SSH_FX_NO_MEDIA: - return "SSH_FX_NO_MEDIA"; - case SSH_FX_NO_SPACE_ON_FILESYSTEM: - return "SSH_FX_NO_SPACE_ON_FILESYSTEM"; - case SSH_FX_QUOTA_EXCEEDED: - return "SSH_FX_QUOTA_EXCEEDED"; - case SSH_FX_UNKNOWN_PRINCIPAL: - return "SSH_FX_UNKNOWN_PRINCIPAL"; - case SSH_FX_LOCK_CONFLICT: - return "SSH_FX_LOCK_CONFLICT"; - case SSH_FX_DIR_NOT_EMPTY: - return "SSH_FX_DIR_NOT_EMPTY"; - case SSH_FX_NOT_A_DIRECTORY: - return "SSH_FX_NOT_A_DIRECTORY"; - case SSH_FX_INVALID_FILENAME: - return "SSH_FX_INVALID_FILENAME"; - case SSH_FX_LINK_LOOP: - return "SSH_FX_LINK_LOOP"; - case SSH_FX_CANNOT_DELETE: - return "SSH_FX_CANNOT_DELETE"; - case SSH_FX_INVALID_PARAMETER: - return "SSH_FX_INVALID_PARAMETER"; - case SSH_FX_FILE_IS_A_DIRECTORY: - return "SSH_FX_FILE_IS_A_DIRECTORY"; - case SSH_FX_BYTE_RANGE_LOCK_CONFLICT: - return "SSH_FX_BYTE_RANGE_LOCK_CONFLICT"; - case SSH_FX_BYTE_RANGE_LOCK_REFUSED: - return "SSH_FX_BYTE_RANGE_LOCK_REFUSED"; - case SSH_FX_DELETE_PENDING: - return "SSH_FX_DELETE_PENDING"; - case SSH_FX_FILE_CORRUPT: - return "SSH_FX_FILE_CORRUPT"; - case SSH_FX_OWNER_INVALID: - return "SSH_FX_OWNER_INVALID"; - case SSH_FX_GROUP_INVALID: - return "SSH_FX_GROUP_INVALID"; - case SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK: - return "SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK"; - case INVALID_RESUME_STATE: - return "INVALID_RESUME_STATE"; - case ATTRIBUTE_BITS_NOT_AVAILABLE: - return "ATTRIBUTE_BITS_NOT_AVAILABLE"; - case BAD_API_USAGE: - return "BAD_API_USAGE"; - default: - return "SSH_FX_UNKNOWN/" + String.valueOf(status); - } - } } diff --git a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java index 73a0e610..28c4c6f2 100644 --- a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java +++ b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java @@ -555,9 +555,7 @@ public SftpMessage getResponse(UnsignedInteger32 requestId) throws SshException } } - SftpMessage m = (SftpMessage) responses.remove(requestId); - - return m; + return (SftpMessage) responses.remove(requestId); } @@ -672,8 +670,17 @@ public void getOKRequestStatus(UnsignedInteger32 requestId) SftpMessage bar = getResponse(requestId); try { if (bar.getType() == SSH_FXP_STATUS) { - int status = processStatusResponse(bar); + int status = (int) bar.readInt(); + if (status == SftpStatusException.SSH_FX_OK) { + return; + } + + if (version >= 3) { + String msg = bar.readString(); + throw new SftpStatusException(status, msg); + } throw new SftpStatusException(status); + } close(); throw new SshException( @@ -1244,8 +1251,12 @@ public String getSymbolicLinkTarget(String linkpath) SftpMessage fileMsg = getResponse(requestId); if (fileMsg.getType() == SSH_FXP_STATUS) { - int status = processStatusResponse(fileMsg); + int status = (int) fileMsg.readInt(); + if (version >= 3) { + throw new SftpStatusException(status, fileMsg.readString()); + } throw new SftpStatusException(status); + } try { SftpFile[] files = extractFiles(fileMsg, null); @@ -1312,9 +1323,6 @@ public SftpFile getSingleFileResponse(SftpMessage bar, String messageName) throw if (bar.getType() == SSH_FXP_NAME) { SftpFile[] files = extractFiles(bar, null); - if(Log.isDebugEnabled()) { - Log.debug("Received SSH_FXP_NAME with {} files", files.length); - } if (files.length != 1) { close(); throw new SshException( @@ -1325,7 +1333,11 @@ public SftpFile getSingleFileResponse(SftpMessage bar, String messageName) throw return files[0]; } else if (bar.getType() == SSH_FXP_STATUS) { - int status = processStatusResponse(bar); + int status = (int) bar.readInt(); + if (version >= 3) { + String desc = bar.readString(); + throw new SftpStatusException(status, desc); + } throw new SftpStatusException(status); } else { close(); @@ -1904,7 +1916,7 @@ protected SftpFileAttributes getAttributes(String path, int messageId) SftpMessage bar = getResponse(requestId); try { - return extractAttributes(bar, path); + return extractAttributes(bar); } finally { bar.release(); } @@ -1915,16 +1927,19 @@ protected SftpFileAttributes getAttributes(String path, int messageId) } } - SftpFileAttributes extractAttributes(SftpMessage bar, String filename) + SftpFileAttributes extractAttributes(SftpMessage bar) throws SftpStatusException, SshException { try { if (bar.getType() == SSH_FXP_ATTRS) { - if(Log.isDebugEnabled()) { - Log.debug("Received SSH_FXP_ATTRS for {}", filename); - } return SftpFileAttributesBuilder.of(bar, getVersion(), getCharsetEncoding()).build(); } else if (bar.getType() == SSH_FXP_STATUS) { - int status = processStatusResponse(bar); + int status = (int) bar.readInt(); + + // Only read the message string if the version is >= 3 + if (version >= 3) { + String msg = bar.readString(); + throw new SftpStatusException(status, msg); + } throw new SftpStatusException(status); } else { close(); @@ -1939,26 +1954,6 @@ SftpFileAttributes extractAttributes(SftpMessage bar, String filename) } } - int processStatusResponse(SftpMessage bar) throws SftpStatusException, IOException { - - int status = (int) bar.readInt(); - - if (version >= 3) { - String desc = bar.readString(); - if(Log.isDebugEnabled()) { - Log.debug("Received {} with message {}", - SftpStatusException.getStatusMessage(status), desc); - } - throw new SftpStatusException(status, desc); - } - - if(Log.isDebugEnabled()) { - Log.debug("Received {}", SftpStatusException.getStatusMessage(status)); - } - - return status; - } - /** * Get the attributes of a file. * @@ -2033,12 +2028,14 @@ public SftpMessage getExtendedReply(UnsignedInteger32 requestId) throws SftpStat public SftpMessage getExtendedReply(SftpMessage bar) throws SftpStatusException, SshException { try { if (bar.getType() == SSH_FXP_EXTENDED_REPLY) { - if(Log.isDebugEnabled()) { - Log.debug("Received SSH_FX_EXTENDED_REPLY"); - } return bar; } else if (bar.getType() == SSH_FXP_STATUS) { - int status = processStatusResponse(bar); + int status = (int) bar.readInt(); + + if (version >= 3) { + String msg = bar.readString(); + throw new SftpStatusException(status, msg); + } throw new SftpStatusException(status); } else { close(); @@ -2068,12 +2065,14 @@ public byte[] getHandleResponse(SftpMessage bar) try { if (bar.getType() == SSH_FXP_HANDLE) { - if(Log.isDebugEnabled()) { - Log.debug("Received SSH_FXP_HANDLE"); - } return bar.readBinaryString(); } else if (bar.getType() == SSH_FXP_STATUS) { - int status = processStatusResponse(bar); + int status = (int) bar.readInt(); + + if (version >= 3) { + String msg = bar.readString(); + throw new SftpStatusException(status, msg); + } throw new SftpStatusException(status); } else { close(); @@ -2094,12 +2093,14 @@ SftpMessage getExtensionResponse(UnsignedInteger32 requestId) SftpMessage bar = getResponse(requestId); try { if (bar.getType() == SSH_FXP_EXTENDED_REPLY) { - if(Log.isDebugEnabled()) { - Log.debug("Received SSH_FXP_EXTENDED_REPLY"); - } return bar; } else if (bar.getType() == SSH_FXP_STATUS) { - int status = processStatusResponse(bar); + int status = (int) bar.readInt(); + + if (version >= 3) { + String msg = bar.readString(); + throw new SftpStatusException(status, msg); + } throw new SftpStatusException(status); } else { close(); diff --git a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpClient.java b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpClient.java index 3e7df00a..c1f760bd 100644 --- a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpClient.java +++ b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpClient.java @@ -3911,7 +3911,11 @@ public StatVfs statVFS(String path) throws SshException, SftpStatusException { UnsignedInteger32 requestId = channel.sendExtensionMessage("statvfs@openssh.com", msg.toByteArray()); SftpMessage response = channel.getResponse(requestId); if (response.getType() == SftpChannel.SSH_FXP_STATUS) { - int status = sftp.processStatusResponse(response); + int status = (int) response.readInt(); + if (sftp.version >= 3) { + String errmsg = response.readString(); + throw new SftpStatusException(status, errmsg); + } throw new SftpStatusException(status); } else { return new StatVfs(response); diff --git a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpFileInputStream.java b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpFileInputStream.java index f12678b2..aaf1634f 100644 --- a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpFileInputStream.java +++ b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpFileInputStream.java @@ -26,12 +26,10 @@ import java.io.InputStream; import java.util.Vector; -import com.sshtools.common.logger.Log; import com.sshtools.common.sftp.SftpStatusException; import com.sshtools.common.ssh.SshException; import com.sshtools.common.ssh.SshIOException; import com.sshtools.common.util.UnsignedInteger32; -import com.sshtools.common.util.UnsignedInteger64; /** * An InputStream to read the contents of a remote file. @@ -46,7 +44,6 @@ public class SftpFileInputStream extends InputStream { private int currentMessageRemaining; private boolean isEOF = false; private boolean error = false; - private UnsignedInteger64 length; /** * @@ -78,10 +75,8 @@ public SftpFileInputStream(SftpFile file) throws SftpStatusException, @Deprecated(since = "3.1.0", forRemoval = true) public SftpFileInputStream(SftpFile file, long position) throws SftpStatusException, SshException { this.sftp = file.getSFTPChannel(); - this.length = file.getAttributes().size(); this.handle = file.openFile(SftpChannel.OPEN_READ); this.position = position; - } /** @@ -104,11 +99,10 @@ public SftpFileInputStream(SftpFile file, long position) throws SftpStatusExcept * @throws SftpStatusException * @throws SshException */ - SftpFileInputStream(SftpHandle handle, long position) throws SftpStatusException, SshException { + SftpFileInputStream(SftpHandle handle, long position) { this.handle = handle; this.position = position; this.sftp = handle.getSFTPChannel(); - this.length = handle.getFile().getAttributes().size(); } /* @@ -180,36 +174,19 @@ private void bufferNextMessage() throws SshException, IOException, currentMessage = sftp.getResponse(requestid); if (currentMessage.getType() == SftpChannel.SSH_FXP_DATA) { - if(Log.isDebugEnabled()) { - Log.debug("Received SSH_FXP_DATA for {}", handle.getFile().getFilename()); - } currentMessageRemaining = (int) currentMessage.readInt(); } else if (currentMessage.getType() == SftpChannel.SSH_FXP_STATUS) { try { int status = (int) currentMessage.readInt(); if (status == SftpStatusException.SSH_FX_EOF) { - if(Log.isDebugEnabled()) { - Log.debug("Received SSH_FX_EOF for {}", handle.getFile().getFilename()); - } isEOF = true; return; } if (sftp.getVersion() >= 3) { String desc = currentMessage.readString(); - if(Log.isDebugEnabled()) { - Log.debug("Received SSH_FXP_STATUS {}/{} for {}", - status, - desc, - handle.getFile().getFilename()); - } - throw new IOException(desc); } - if(Log.isDebugEnabled()) { - Log.debug("Received SSH_FXP_STATUS {} for {}", - status, handle.getFile().getFilename()); - } throw new IOException("Unexpected status " + status); } finally { currentMessage.release(); @@ -230,20 +207,7 @@ private void bufferNextMessage() throws SshException, IOException, } private void bufferMoreData() throws SftpStatusException, SshException { - - /** - * Read up to length of file - */ - while (outstandingRequests.size() < 100 && length.longValue() > position) { - outstandingRequests.addElement(handle.postReadRequest(position, 32768)); - position += 32768; - } - - /** - * If there are no requests then add one to ensure we are still reading if file size - * has changed. - */ - if(outstandingRequests.isEmpty()) { + while (outstandingRequests.size() < 100) { outstandingRequests.addElement(handle.postReadRequest(position, 32768)); position += 32768; } diff --git a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpHandle.java b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpHandle.java index 83d57cbe..a3ad35a8 100644 --- a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpHandle.java +++ b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpHandle.java @@ -317,7 +317,7 @@ public int listChildren(List children) throws SftpStatusException, Ssh SftpFile[] files = sftp.extractFiles(bar, file.getAbsolutePath()); if (Log.isDebugEnabled()) { - Log.debug("There are {} results in this packet", files.length); + Log.debug("THere are {} results in this packet", files.length); } for (int i = 0; i < files.length; i++) { @@ -417,7 +417,7 @@ public SftpFileAttributes getAttributes() throws SftpStatusException, SshExcepti SftpMessage attrMessage = sftp.getResponse(requestId); try { - return sftp.extractAttributes(attrMessage, getFile().getFilename()); + return sftp.extractAttributes(attrMessage); } finally { attrMessage.release(); } From 77225b673e1580fbc046b4c74655b847892bb01d Mon Sep 17 00:00:00 2001 From: Brett Smith Date: Fri, 21 Jun 2024 17:11:58 +0100 Subject: [PATCH 38/51] More logging improvements and length check in SftpFileInputStream. note: This revert contains additional fix for problem with OK status on status calls being treated as errors. --- .../common/sftp/SftpStatusException.java | 78 ++++++++++++++ .../com/sshtools/client/sftp/SftpChannel.java | 102 ++++++++++-------- .../com/sshtools/client/sftp/SftpClient.java | 6 +- .../client/sftp/SftpFileInputStream.java | 39 ++++++- .../com/sshtools/client/sftp/SftpHandle.java | 4 +- 5 files changed, 173 insertions(+), 56 deletions(-) diff --git a/maverick-base/src/main/java/com/sshtools/common/sftp/SftpStatusException.java b/maverick-base/src/main/java/com/sshtools/common/sftp/SftpStatusException.java index fc505a42..c5a68a13 100644 --- a/maverick-base/src/main/java/com/sshtools/common/sftp/SftpStatusException.java +++ b/maverick-base/src/main/java/com/sshtools/common/sftp/SftpStatusException.java @@ -203,5 +203,83 @@ public static String getStatusText(int status) { return "Unknown status type " + String.valueOf(status); } } + + public static String getStatusMessage(int status) { + switch (status) { + case SSH_FX_OK: + return "SSH_FX_OK"; + case SSH_FX_EOF: + return "SSH_FX_EOF"; + case SSH_FX_NO_SUCH_FILE: + return "SSH_FX_NO_SUCH_FILE"; + case SSH_FX_PERMISSION_DENIED: + return "SSH_FX_PERMISSION_DENIED"; + case SSH_FX_FAILURE: + return "SSH_FX_FAILURE"; + case SSH_FX_BAD_MESSAGE: + return "SSH_FX_BAD_MESSAGE"; + case SSH_FX_NO_CONNECTION: + return "SSH_FX_NO_CONNECTION"; + case SSH_FX_CONNECTION_LOST: + return "SSH_FX_CONNECTION_LOST"; + case SSH_FX_OP_UNSUPPORTED: + return "SSH_FX_OP_UNSUPPORTED"; + case SSH_FX_INVALID_HANDLE: + case INVALID_HANDLE: + return "SSH_FX_INVALID_HANDLE"; + case SSH_FX_NO_SUCH_PATH: + return "SSH_FX_NO_SUCH_PATH"; + case SSH_FX_FILE_ALREADY_EXISTS: + return "SSH_FX_FILE_ALREADY_EXISTS"; + case SSH_FX_WRITE_PROTECT: + return "SSH_FX_WRITE_PROTECT"; + case SSH_FX_NO_MEDIA: + return "SSH_FX_NO_MEDIA"; + case SSH_FX_NO_SPACE_ON_FILESYSTEM: + return "SSH_FX_NO_SPACE_ON_FILESYSTEM"; + case SSH_FX_QUOTA_EXCEEDED: + return "SSH_FX_QUOTA_EXCEEDED"; + case SSH_FX_UNKNOWN_PRINCIPAL: + return "SSH_FX_UNKNOWN_PRINCIPAL"; + case SSH_FX_LOCK_CONFLICT: + return "SSH_FX_LOCK_CONFLICT"; + case SSH_FX_DIR_NOT_EMPTY: + return "SSH_FX_DIR_NOT_EMPTY"; + case SSH_FX_NOT_A_DIRECTORY: + return "SSH_FX_NOT_A_DIRECTORY"; + case SSH_FX_INVALID_FILENAME: + return "SSH_FX_INVALID_FILENAME"; + case SSH_FX_LINK_LOOP: + return "SSH_FX_LINK_LOOP"; + case SSH_FX_CANNOT_DELETE: + return "SSH_FX_CANNOT_DELETE"; + case SSH_FX_INVALID_PARAMETER: + return "SSH_FX_INVALID_PARAMETER"; + case SSH_FX_FILE_IS_A_DIRECTORY: + return "SSH_FX_FILE_IS_A_DIRECTORY"; + case SSH_FX_BYTE_RANGE_LOCK_CONFLICT: + return "SSH_FX_BYTE_RANGE_LOCK_CONFLICT"; + case SSH_FX_BYTE_RANGE_LOCK_REFUSED: + return "SSH_FX_BYTE_RANGE_LOCK_REFUSED"; + case SSH_FX_DELETE_PENDING: + return "SSH_FX_DELETE_PENDING"; + case SSH_FX_FILE_CORRUPT: + return "SSH_FX_FILE_CORRUPT"; + case SSH_FX_OWNER_INVALID: + return "SSH_FX_OWNER_INVALID"; + case SSH_FX_GROUP_INVALID: + return "SSH_FX_GROUP_INVALID"; + case SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK: + return "SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK"; + case INVALID_RESUME_STATE: + return "INVALID_RESUME_STATE"; + case ATTRIBUTE_BITS_NOT_AVAILABLE: + return "ATTRIBUTE_BITS_NOT_AVAILABLE"; + case BAD_API_USAGE: + return "BAD_API_USAGE"; + default: + return "SSH_FX_UNKNOWN/" + String.valueOf(status); + } + } } diff --git a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java index 28c4c6f2..6b32f52f 100644 --- a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java +++ b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java @@ -555,7 +555,9 @@ public SftpMessage getResponse(UnsignedInteger32 requestId) throws SshException } } - return (SftpMessage) responses.remove(requestId); + SftpMessage m = (SftpMessage) responses.remove(requestId); + + return m; } @@ -670,17 +672,17 @@ public void getOKRequestStatus(UnsignedInteger32 requestId) SftpMessage bar = getResponse(requestId); try { if (bar.getType() == SSH_FXP_STATUS) { - int status = (int) bar.readInt(); - if (status == SftpStatusException.SSH_FX_OK) { - return; + try { + throw new SftpStatusException(processStatusResponse(bar)); } - - if (version >= 3) { - String msg = bar.readString(); - throw new SftpStatusException(status, msg); + catch(SftpStatusException sse) { + if(sse.getStatus() == SftpStatusException.SSH_FX_OK) { + return; + } + else { + throw sse; + } } - throw new SftpStatusException(status); - } close(); throw new SshException( @@ -1251,12 +1253,8 @@ public String getSymbolicLinkTarget(String linkpath) SftpMessage fileMsg = getResponse(requestId); if (fileMsg.getType() == SSH_FXP_STATUS) { - int status = (int) fileMsg.readInt(); - if (version >= 3) { - throw new SftpStatusException(status, fileMsg.readString()); - } + int status = processStatusResponse(fileMsg); throw new SftpStatusException(status); - } try { SftpFile[] files = extractFiles(fileMsg, null); @@ -1323,6 +1321,9 @@ public SftpFile getSingleFileResponse(SftpMessage bar, String messageName) throw if (bar.getType() == SSH_FXP_NAME) { SftpFile[] files = extractFiles(bar, null); + if(Log.isDebugEnabled()) { + Log.debug("Received SSH_FXP_NAME with {} files", files.length); + } if (files.length != 1) { close(); throw new SshException( @@ -1333,11 +1334,7 @@ public SftpFile getSingleFileResponse(SftpMessage bar, String messageName) throw return files[0]; } else if (bar.getType() == SSH_FXP_STATUS) { - int status = (int) bar.readInt(); - if (version >= 3) { - String desc = bar.readString(); - throw new SftpStatusException(status, desc); - } + int status = processStatusResponse(bar); throw new SftpStatusException(status); } else { close(); @@ -1916,7 +1913,7 @@ protected SftpFileAttributes getAttributes(String path, int messageId) SftpMessage bar = getResponse(requestId); try { - return extractAttributes(bar); + return extractAttributes(bar, path); } finally { bar.release(); } @@ -1927,19 +1924,16 @@ protected SftpFileAttributes getAttributes(String path, int messageId) } } - SftpFileAttributes extractAttributes(SftpMessage bar) + SftpFileAttributes extractAttributes(SftpMessage bar, String filename) throws SftpStatusException, SshException { try { if (bar.getType() == SSH_FXP_ATTRS) { + if(Log.isDebugEnabled()) { + Log.debug("Received SSH_FXP_ATTRS for {}", filename); + } return SftpFileAttributesBuilder.of(bar, getVersion(), getCharsetEncoding()).build(); } else if (bar.getType() == SSH_FXP_STATUS) { - int status = (int) bar.readInt(); - - // Only read the message string if the version is >= 3 - if (version >= 3) { - String msg = bar.readString(); - throw new SftpStatusException(status, msg); - } + int status = processStatusResponse(bar); throw new SftpStatusException(status); } else { close(); @@ -1954,6 +1948,26 @@ SftpFileAttributes extractAttributes(SftpMessage bar) } } + int processStatusResponse(SftpMessage bar) throws SftpStatusException, IOException { + + int status = (int) bar.readInt(); + + if (version >= 3) { + String desc = bar.readString(); + if(Log.isDebugEnabled()) { + Log.debug("Received {} with message {}", + SftpStatusException.getStatusMessage(status), desc); + } + throw new SftpStatusException(status, desc); + } + + if(Log.isDebugEnabled()) { + Log.debug("Received {}", SftpStatusException.getStatusMessage(status)); + } + + return status; + } + /** * Get the attributes of a file. * @@ -2028,14 +2042,12 @@ public SftpMessage getExtendedReply(UnsignedInteger32 requestId) throws SftpStat public SftpMessage getExtendedReply(SftpMessage bar) throws SftpStatusException, SshException { try { if (bar.getType() == SSH_FXP_EXTENDED_REPLY) { + if(Log.isDebugEnabled()) { + Log.debug("Received SSH_FX_EXTENDED_REPLY"); + } return bar; } else if (bar.getType() == SSH_FXP_STATUS) { - int status = (int) bar.readInt(); - - if (version >= 3) { - String msg = bar.readString(); - throw new SftpStatusException(status, msg); - } + int status = processStatusResponse(bar); throw new SftpStatusException(status); } else { close(); @@ -2065,14 +2077,12 @@ public byte[] getHandleResponse(SftpMessage bar) try { if (bar.getType() == SSH_FXP_HANDLE) { + if(Log.isDebugEnabled()) { + Log.debug("Received SSH_FXP_HANDLE"); + } return bar.readBinaryString(); } else if (bar.getType() == SSH_FXP_STATUS) { - int status = (int) bar.readInt(); - - if (version >= 3) { - String msg = bar.readString(); - throw new SftpStatusException(status, msg); - } + int status = processStatusResponse(bar); throw new SftpStatusException(status); } else { close(); @@ -2093,14 +2103,12 @@ SftpMessage getExtensionResponse(UnsignedInteger32 requestId) SftpMessage bar = getResponse(requestId); try { if (bar.getType() == SSH_FXP_EXTENDED_REPLY) { + if(Log.isDebugEnabled()) { + Log.debug("Received SSH_FXP_EXTENDED_REPLY"); + } return bar; } else if (bar.getType() == SSH_FXP_STATUS) { - int status = (int) bar.readInt(); - - if (version >= 3) { - String msg = bar.readString(); - throw new SftpStatusException(status, msg); - } + int status = processStatusResponse(bar); throw new SftpStatusException(status); } else { close(); diff --git a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpClient.java b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpClient.java index c1f760bd..3e7df00a 100644 --- a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpClient.java +++ b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpClient.java @@ -3911,11 +3911,7 @@ public StatVfs statVFS(String path) throws SshException, SftpStatusException { UnsignedInteger32 requestId = channel.sendExtensionMessage("statvfs@openssh.com", msg.toByteArray()); SftpMessage response = channel.getResponse(requestId); if (response.getType() == SftpChannel.SSH_FXP_STATUS) { - int status = (int) response.readInt(); - if (sftp.version >= 3) { - String errmsg = response.readString(); - throw new SftpStatusException(status, errmsg); - } + int status = sftp.processStatusResponse(response); throw new SftpStatusException(status); } else { return new StatVfs(response); diff --git a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpFileInputStream.java b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpFileInputStream.java index aaf1634f..109748bb 100644 --- a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpFileInputStream.java +++ b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpFileInputStream.java @@ -26,10 +26,12 @@ import java.io.InputStream; import java.util.Vector; +import com.sshtools.common.logger.Log; import com.sshtools.common.sftp.SftpStatusException; import com.sshtools.common.ssh.SshException; import com.sshtools.common.ssh.SshIOException; import com.sshtools.common.util.UnsignedInteger32; +import com.sshtools.common.util.UnsignedInteger64; /** * An InputStream to read the contents of a remote file. @@ -44,6 +46,7 @@ public class SftpFileInputStream extends InputStream { private int currentMessageRemaining; private boolean isEOF = false; private boolean error = false; + private UnsignedInteger64 length; /** * @@ -77,6 +80,7 @@ public SftpFileInputStream(SftpFile file, long position) throws SftpStatusExcept this.sftp = file.getSFTPChannel(); this.handle = file.openFile(SftpChannel.OPEN_READ); this.position = position; + this.length = handle.getAttributes().size(); } /** @@ -99,10 +103,11 @@ public SftpFileInputStream(SftpFile file, long position) throws SftpStatusExcept * @throws SftpStatusException * @throws SshException */ - SftpFileInputStream(SftpHandle handle, long position) { + SftpFileInputStream(SftpHandle handle, long position) throws SftpStatusException, SshException { this.handle = handle; this.position = position; this.sftp = handle.getSFTPChannel(); + this.length = handle.getAttributes().size(); } /* @@ -174,19 +179,36 @@ private void bufferNextMessage() throws SshException, IOException, currentMessage = sftp.getResponse(requestid); if (currentMessage.getType() == SftpChannel.SSH_FXP_DATA) { + if(Log.isDebugEnabled()) { + Log.debug("Received SSH_FXP_DATA for {}", handle.getFile().getFilename()); + } currentMessageRemaining = (int) currentMessage.readInt(); } else if (currentMessage.getType() == SftpChannel.SSH_FXP_STATUS) { try { int status = (int) currentMessage.readInt(); if (status == SftpStatusException.SSH_FX_EOF) { + if(Log.isDebugEnabled()) { + Log.debug("Received SSH_FX_EOF for {}", handle.getFile().getFilename()); + } isEOF = true; return; } if (sftp.getVersion() >= 3) { String desc = currentMessage.readString(); + if(Log.isDebugEnabled()) { + Log.debug("Received SSH_FXP_STATUS {}/{} for {}", + status, + desc, + handle.getFile().getFilename()); + } + throw new IOException(desc); } + if(Log.isDebugEnabled()) { + Log.debug("Received SSH_FXP_STATUS {} for {}", + status, handle.getFile().getFilename()); + } throw new IOException("Unexpected status " + status); } finally { currentMessage.release(); @@ -207,7 +229,20 @@ private void bufferNextMessage() throws SshException, IOException, } private void bufferMoreData() throws SftpStatusException, SshException { - while (outstandingRequests.size() < 100) { + + /** + * Read up to length of file + */ + while (outstandingRequests.size() < 100 && length.longValue() > position) { + outstandingRequests.addElement(handle.postReadRequest(position, 32768)); + position += 32768; + } + + /** + * If there are no requests then add one to ensure we are still reading if file size + * has changed. + */ + if(outstandingRequests.isEmpty()) { outstandingRequests.addElement(handle.postReadRequest(position, 32768)); position += 32768; } diff --git a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpHandle.java b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpHandle.java index a3ad35a8..83d57cbe 100644 --- a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpHandle.java +++ b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpHandle.java @@ -317,7 +317,7 @@ public int listChildren(List children) throws SftpStatusException, Ssh SftpFile[] files = sftp.extractFiles(bar, file.getAbsolutePath()); if (Log.isDebugEnabled()) { - Log.debug("THere are {} results in this packet", files.length); + Log.debug("There are {} results in this packet", files.length); } for (int i = 0; i < files.length; i++) { @@ -417,7 +417,7 @@ public SftpFileAttributes getAttributes() throws SftpStatusException, SshExcepti SftpMessage attrMessage = sftp.getResponse(requestId); try { - return sftp.extractAttributes(attrMessage); + return sftp.extractAttributes(attrMessage, getFile().getFilename()); } finally { attrMessage.release(); } From 7de5c616d12e0ea8db662100fc747eb3c58db064 Mon Sep 17 00:00:00 2001 From: Brett Smith Date: Fri, 21 Jun 2024 17:19:16 +0100 Subject: [PATCH 39/51] Get existing attributes from file object that are already there. --- .../java/com/sshtools/client/sftp/SftpFileInputStream.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpFileInputStream.java b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpFileInputStream.java index 109748bb..f12678b2 100644 --- a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpFileInputStream.java +++ b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpFileInputStream.java @@ -78,9 +78,10 @@ public SftpFileInputStream(SftpFile file) throws SftpStatusException, @Deprecated(since = "3.1.0", forRemoval = true) public SftpFileInputStream(SftpFile file, long position) throws SftpStatusException, SshException { this.sftp = file.getSFTPChannel(); + this.length = file.getAttributes().size(); this.handle = file.openFile(SftpChannel.OPEN_READ); this.position = position; - this.length = handle.getAttributes().size(); + } /** @@ -107,7 +108,7 @@ public SftpFileInputStream(SftpFile file, long position) throws SftpStatusExcept this.handle = handle; this.position = position; this.sftp = handle.getSFTPChannel(); - this.length = handle.getAttributes().size(); + this.length = handle.getFile().getAttributes().size(); } /* From 597a214b78f1db14a4c69154f6902564d33aac0d Mon Sep 17 00:00:00 2001 From: Brett Smith Date: Fri, 21 Jun 2024 17:22:29 +0100 Subject: [PATCH 40/51] Get existing attributes from file object that are already there, not using deprecated methods. --- .../sshtools/client/sftp/SftpFileInputStream.java | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpFileInputStream.java b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpFileInputStream.java index f12678b2..d8feb7b4 100644 --- a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpFileInputStream.java +++ b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpFileInputStream.java @@ -78,22 +78,11 @@ public SftpFileInputStream(SftpFile file) throws SftpStatusException, @Deprecated(since = "3.1.0", forRemoval = true) public SftpFileInputStream(SftpFile file, long position) throws SftpStatusException, SshException { this.sftp = file.getSFTPChannel(); - this.length = file.getAttributes().size(); + this.length = file.attributes().size(); this.handle = file.openFile(SftpChannel.OPEN_READ); this.position = position; } - - /** - * - * @param handle handle - * @throws SftpStatusException - * @throws SshException - */ - SftpFileInputStream(SftpHandle handle) throws SftpStatusException, - SshException { - this(handle, 0); - } /** * Creates a new SftpFileInputStream object. @@ -108,7 +97,7 @@ public SftpFileInputStream(SftpFile file, long position) throws SftpStatusExcept this.handle = handle; this.position = position; this.sftp = handle.getSFTPChannel(); - this.length = handle.getFile().getAttributes().size(); + this.length = handle.getFile().attributes().size(); } /* From 125bd80f84a1a0c385c211843fbe4bf9e5272407 Mon Sep 17 00:00:00 2001 From: Lee Painter Date: Fri, 21 Jun 2024 22:22:43 +0100 Subject: [PATCH 41/51] Improved status handling Added more logging (still missing many requests i.e. Sending SSH_FX_ABC) --- .../com/sshtools/client/sftp/SftpChannel.java | 232 ++++++++++++------ .../com/sshtools/client/sftp/SftpClient.java | 48 ++-- .../client/sftp/SftpFileOutputStream.java | 2 +- .../com/sshtools/client/sftp/SftpHandle.java | 15 +- .../com/sshtools/client/tasks/PushTask.java | 2 +- 5 files changed, 195 insertions(+), 104 deletions(-) diff --git a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java index 6b32f52f..821da894 100644 --- a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java +++ b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java @@ -666,23 +666,14 @@ public void changePermissions(String filename, String permissions) * @throws SftpStatusException * , SshException */ - public void getOKRequestStatus(UnsignedInteger32 requestId) + public void getOKRequestStatus(UnsignedInteger32 requestId, String path) throws SftpStatusException, SshException { SftpMessage bar = getResponse(requestId); try { if (bar.getType() == SSH_FXP_STATUS) { - try { - throw new SftpStatusException(processStatusResponse(bar)); - } - catch(SftpStatusException sse) { - if(sse.getStatus() == SftpStatusException.SSH_FX_OK) { - return; - } - else { - throw sse; - } - } + processStatusResponse(bar, path); + return; } close(); throw new SshException( @@ -738,9 +729,13 @@ public void setAttributes(String path, SftpFileAttributes attrs) msg.writeString(path, CHARSET_ENCODING); msg.write(attrs.toByteArray(getVersion())); + if(Log.isDebugEnabled()) { + Log.debug("Sending SSH_FXP_SETSTAT for {}", path); + } + sendMessage(msg); - getOKRequestStatus(requestId); + getOKRequestStatus(requestId, path); } catch (SshIOException ex) { throw ex.getRealException(); } catch (IOException ex) { @@ -810,8 +805,9 @@ public UnsignedInteger32 postWriteRequest(byte[] handle, long position, public void writeFile(byte[] handle, UnsignedInteger64 offset, byte[] data, int off, int len) throws SftpStatusException, SshException { - getOKRequestStatus(getBestHandle(handle).postWriteRequest(offset.longValue(), data, - off, len)); + SftpHandle h = getBestHandle(handle); + getOKRequestStatus(h.postWriteRequest(offset.longValue(), data, off, len), + h.getFile().getAbsolutePath()); } /** @@ -1075,6 +1071,9 @@ public void lockFile(byte[] handle, long offset, long length, int lockFlags) thr "Locks are not supported by the server SFTP version " + String.valueOf(version)); } + + SftpHandle h = getBestHandle(handle); + try { UnsignedInteger32 requestId = nextRequestId(); Packet msg = createPacket(); @@ -1085,9 +1084,12 @@ public void lockFile(byte[] handle, long offset, long length, int lockFlags) thr msg.writeUINT64(length); msg.writeInt(lockFlags); + if(Log.isDebugEnabled()) { + Log.debug("Sending SSH_FXP_BLOCK for {}", h.getFile().getAbsolutePath()); + } sendMessage(msg); - getOKRequestStatus(requestId); + getOKRequestStatus(requestId, h.getFile().getAbsolutePath()); } catch (SshIOException ex) { throw ex.getRealException(); } catch (IOException ex) { @@ -1115,6 +1117,9 @@ public void unlockFile(byte[] handle, long offset, long length) throws SftpStatu "Locks are not supported by the server SFTP version " + String.valueOf(version)); } + + SftpHandle h = getBestHandle(handle); + try { UnsignedInteger32 requestId = nextRequestId(); Packet msg = createPacket(); @@ -1124,9 +1129,12 @@ public void unlockFile(byte[] handle, long offset, long length) throws SftpStatu msg.writeUINT64(offset); msg.writeUINT64(length); + if(Log.isDebugEnabled()) { + Log.debug("Sending SSH_FXP_UNBLOCK for {}", h.getFile().getAbsolutePath()); + } sendMessage(msg); - getOKRequestStatus(requestId); + getOKRequestStatus(requestId, h.getFile().getAbsolutePath()); } catch (SshIOException ex) { throw ex.getRealException(); } catch (IOException ex) { @@ -1154,6 +1162,7 @@ public void createSymbolicLink(String targetpath, String linkpath) "Symbolic links are not supported by the server SFTP version " + String.valueOf(version)); } + try { UnsignedInteger32 requestId = nextRequestId(); Packet msg = createPacket(); @@ -1164,10 +1173,16 @@ public void createSymbolicLink(String targetpath, String linkpath) if(version >= 6) { msg.writeBoolean(true); } + + if(Log.isDebugEnabled()) { + Log.debug("Sending SSH_FXP_SYMLINK to link {} to {}", + linkpath, + targetpath); + } sendMessage(msg); - getOKRequestStatus(requestId); + getOKRequestStatus(requestId, linkpath); } catch (SshIOException ex) { throw ex.getRealException(); } catch (IOException ex) { @@ -1211,9 +1226,14 @@ public void createLink(String targetpath, String linkpath, boolean symbolic) msg.writeString(targetpath, CHARSET_ENCODING); msg.writeBoolean(symbolic); + if(Log.isDebugEnabled()) { + Log.debug("Sending SSH_FXP_LINK to link {} to {}", + linkpath, + targetpath); + } sendMessage(msg); - getOKRequestStatus(requestId); + getOKRequestStatus(requestId, linkpath); } catch (SshIOException ex) { throw ex.getRealException(); } catch (IOException ex) { @@ -1249,18 +1269,23 @@ public String getSymbolicLinkTarget(String linkpath) msg.writeInt(requestId.longValue()); msg.writeString(linkpath, CHARSET_ENCODING); + if(Log.isDebugEnabled()) { + Log.debug("Sending SSH_FXP_READLINK for {}", linkpath); + } + sendMessage(msg); SftpMessage fileMsg = getResponse(requestId); if (fileMsg.getType() == SSH_FXP_STATUS) { - int status = processStatusResponse(fileMsg); - throw new SftpStatusException(status); - } - try { - SftpFile[] files = extractFiles(fileMsg, null); - return files[0].getAbsolutePath(); - } finally { - fileMsg.release(); + processStatusResponse(fileMsg, linkpath); + throw new IllegalStateException("Received unexpected SSH_FX_OK in status response!"); + } else { + try { + SftpFile[] files = extractFiles(fileMsg, null); + return files[0].getAbsolutePath(); + } finally { + fileMsg.release(); + } } } catch (SshIOException ex) { throw ex.getRealException(); @@ -1296,9 +1321,14 @@ public String getAbsolutePath(String path) throws SftpStatusException, msg.write(SSH_FXP_REALPATH); msg.writeInt(requestId.longValue()); msg.writeString(path, CHARSET_ENCODING); + + if(Log.isDebugEnabled()) { + Log.debug("Sending SSH_FXP_REALPATH for {}", path); + } + sendMessage(msg); - return getSingleFileResponse(getResponse(requestId), "SSH_FXP_REALPATH").getAbsolutePath(); + return getSingleFileResponse(getResponse(requestId), "SSH_FXP_REALPATH", path).getAbsolutePath(); } catch (SshIOException ex) { throw ex.getRealException(); @@ -1316,14 +1346,11 @@ public String getAbsolutePath(String path) throws SftpStatusException, * @throws SshException * @throws SftpStatusException */ - public SftpFile getSingleFileResponse(SftpMessage bar, String messageName) throws SshException, SftpStatusException { + public SftpFile getSingleFileResponse(SftpMessage bar, String messageName, String path) throws SshException, SftpStatusException { try { if (bar.getType() == SSH_FXP_NAME) { SftpFile[] files = extractFiles(bar, null); - if(Log.isDebugEnabled()) { - Log.debug("Received SSH_FXP_NAME with {} files", files.length); - } if (files.length != 1) { close(); throw new SshException( @@ -1332,10 +1359,13 @@ public SftpFile getSingleFileResponse(SftpMessage bar, String messageName) throw SshException.CHANNEL_FAILURE); } + if(Log.isDebugEnabled()) { + Log.debug("Received SSH_FXP_NAME with value {}", files[0].getAbsolutePath()); + } return files[0]; } else if (bar.getType() == SSH_FXP_STATUS) { - int status = processStatusResponse(bar); - throw new SftpStatusException(status); + processStatusResponse(bar, path); + throw new IllegalStateException("Received unexpected SSH_FX_OK in status response!"); } else { close(); throw new SshException( @@ -1505,14 +1535,14 @@ public SftpHandle openFile(String absolutePath, int flags) /** * Open a file. * - * @param absolutePath + * @param path * @param flags * @param attrs * @return SftpFile * @throws SftpStatusException * , SshException */ - public SftpHandle openFile(String absolutePath, int flags, + public SftpHandle openFile(String path, int flags, SftpFileAttributes attrs) throws SftpStatusException, SshException { if (version >= 5) { @@ -1582,7 +1612,7 @@ else if((flags & OPEN_CREATE)==OPEN_CREATE) { } } - return openFileVersion5(absolutePath, newFlags, accessFlags, attrs); + return openFileVersion5(path, newFlags, accessFlags, attrs); } else { if (attrs == null) { attrs = SftpFileAttributesBuilder.ofType( @@ -1595,15 +1625,19 @@ else if((flags & OPEN_CREATE)==OPEN_CREATE) { Packet msg = createPacket(); msg.write(SSH_FXP_OPEN); msg.writeInt(requestId.longValue()); - msg.writeString(absolutePath, CHARSET_ENCODING); + msg.writeString(path, CHARSET_ENCODING); msg.writeInt(flags); msg.write(attrs.toByteArray(getVersion())); + if(Log.isDebugEnabled()) { + Log.debug("Sending SSH_FXP_OPEN for {}", path); + } + sendMessage(msg); - SftpFile file = new SftpFile(absolutePath, getAttributes(absolutePath), this, null); - SftpHandle handle = file.handle(getHandleResponse(requestId)); + SftpFile file = new SftpFile(path, getAttributes(path), this, null); + SftpHandle handle = file.handle(getHandleResponse(requestId, path)); EventServiceImplementation.getInstance().fireEvent( (new Event(this, @@ -1620,7 +1654,7 @@ else if((flags & OPEN_CREATE)==OPEN_CREATE) { } } - public SftpHandle openFileVersion5(String absolutePath, int flags, + public SftpHandle openFileVersion5(String path, int flags, int accessFlags, SftpFileAttributes attrs) throws SftpStatusException, SshException { @@ -1635,15 +1669,19 @@ public SftpHandle openFileVersion5(String absolutePath, int flags, Packet msg = createPacket(); msg.write(SSH_FXP_OPEN); msg.writeInt(requestId.longValue()); - msg.writeString(absolutePath, CHARSET_ENCODING); + msg.writeString(path, CHARSET_ENCODING); msg.writeInt(accessFlags); msg.writeInt(flags); msg.write(attrs.toByteArray(getVersion())); + if(Log.isDebugEnabled()) { + Log.debug("Sending SSH_FXP_OPEN for {}", path); + } + sendMessage(msg); - SftpFile file = new SftpFile(absolutePath, getAttributes(absolutePath), this, null); - SftpHandle handle = file.handle(getHandleResponse(requestId)); + SftpFile file = new SftpFile(path, getAttributes(path), this, null); + SftpHandle handle = file.handle(getHandleResponse(requestId, path)); EventServiceImplementation.getInstance().fireEvent( (new Event(this, EventCodes.EVENT_SFTP_FILE_OPENED, @@ -1684,9 +1722,14 @@ public SftpHandle openDirectory(String path) throws SftpStatusException, msg.write(SSH_FXP_OPENDIR); msg.writeInt(requestId.longValue()); msg.writeString(absolutePath, CHARSET_ENCODING); + + if(Log.isDebugEnabled()) { + Log.debug("Sending SSH_FXP_OPENDIR for {}", path); + } + sendMessage(msg); - return new SftpFile(absolutePath, attrs, this, null).handle(getHandleResponse(requestId)); + return new SftpFile(absolutePath, attrs, this, null).handle(getHandleResponse(requestId, path)); } catch (SshIOException ex) { throw ex.getRealException(); } catch (IOException ex) { @@ -1714,7 +1757,7 @@ public void closeHandle(byte[] handle) throws SftpStatusException, SshException } } - private SftpHandle getBestHandle(byte[] handle) { + SftpHandle getBestHandle(byte[] handle) { synchronized(handles) { var h = handles.get(handle); if(h != null) { @@ -1756,9 +1799,13 @@ public void removeDirectory(String path) throws SftpStatusException, msg.writeInt(requestId.longValue()); msg.writeString(path, CHARSET_ENCODING); + if(Log.isDebugEnabled()) { + Log.debug("Sending SSH_FXP_RMDIR for {}", path); + } + sendMessage(msg); - getOKRequestStatus(requestId); + getOKRequestStatus(requestId, path); } catch (SshIOException ex) { throw ex.getRealException(); } catch (IOException ex) { @@ -1773,22 +1820,26 @@ public void removeDirectory(String path) throws SftpStatusException, /** * Remove a file. * - * @param filename + * @param path * @throws SftpStatusException * , SshException */ - public void removeFile(String filename) throws SftpStatusException, + public void removeFile(String path) throws SftpStatusException, SshException { try { UnsignedInteger32 requestId = nextRequestId(); Packet msg = createPacket(); msg.write(SSH_FXP_REMOVE); msg.writeInt(requestId.longValue()); - msg.writeString(filename, CHARSET_ENCODING); + msg.writeString(path, CHARSET_ENCODING); + if(Log.isDebugEnabled()) { + Log.debug("Sending SSH_FXP_REMOVE for {}", path); + } + sendMessage(msg); - getOKRequestStatus(requestId); + getOKRequestStatus(requestId, path); } catch (SshIOException ex) { throw ex.getRealException(); } catch (IOException ex) { @@ -1800,7 +1851,7 @@ public void removeFile(String filename) throws SftpStatusException, EventCodes.EVENT_SFTP_FILE_DELETED, true)) .addAttribute( EventCodes.ATTRIBUTE_FILE_NAME, - filename)); + path)); } /** @@ -1835,9 +1886,13 @@ public void renameFile(String oldpath, String newpath, int flags) if(version >= 5) { msg.writeInt(flags); } + + if(Log.isDebugEnabled()) { + Log.debug("Sending SSH_FXP_RENAME from {} to {}", oldpath, newpath); + } sendMessage(msg); - getOKRequestStatus(requestId); + getOKRequestStatus(requestId, newpath); } catch (SshIOException ex) { throw ex.getRealException(); } catch (IOException ex) { @@ -1865,7 +1920,7 @@ public void renameFile(String oldpath, String newpath, int flags) */ public SftpFileAttributes getAttributes(String path) throws SftpStatusException, SshException { - return getAttributes(path, SSH_FXP_STAT); + return getAttributes(path, SSH_FXP_STAT, "SSH_FXP_STAT"); } /** @@ -1879,10 +1934,10 @@ public SftpFileAttributes getAttributes(String path) */ public SftpFileAttributes getLinkAttributes(String path) throws SftpStatusException, SshException { - return getAttributes(path, SSH_FXP_LSTAT); + return getAttributes(path, SSH_FXP_LSTAT, "SSH_FXP_LSTAT"); } - protected SftpFileAttributes getAttributes(String path, int messageId) + protected SftpFileAttributes getAttributes(String path, int messageId, String messageName) throws SftpStatusException, SshException { try { UnsignedInteger32 requestId = nextRequestId(); @@ -1908,6 +1963,12 @@ protected SftpFileAttributes getAttributes(String path, int messageId) msg.writeInt(flags); } + + if(Log.isDebugEnabled()) { + Log.debug("Sending {} for {}", + messageName, + path); + } sendMessage(msg); @@ -1924,17 +1985,17 @@ protected SftpFileAttributes getAttributes(String path, int messageId) } } - SftpFileAttributes extractAttributes(SftpMessage bar, String filename) + SftpFileAttributes extractAttributes(SftpMessage bar, String path) throws SftpStatusException, SshException { try { if (bar.getType() == SSH_FXP_ATTRS) { if(Log.isDebugEnabled()) { - Log.debug("Received SSH_FXP_ATTRS for {}", filename); + Log.debug("Received SSH_FXP_ATTRS for {}", path); } return SftpFileAttributesBuilder.of(bar, getVersion(), getCharsetEncoding()).build(); } else if (bar.getType() == SSH_FXP_STATUS) { - int status = processStatusResponse(bar); - throw new SftpStatusException(status); + processStatusResponse(bar, path); + throw new IllegalStateException("Received unexpected SSH_FX_OK in status response!"); } else { close(); throw new SshException( @@ -1948,10 +2009,20 @@ SftpFileAttributes extractAttributes(SftpMessage bar, String filename) } } - int processStatusResponse(SftpMessage bar) throws SftpStatusException, IOException { + void processStatusResponse(SftpMessage bar, String path) throws SftpStatusException, IOException { int status = (int) bar.readInt(); + if(status == SftpStatusException.SSH_FX_OK) { + + if(Log.isDebugEnabled()) { + Log.debug("Received SSH_FX_OK for {}", + SftpStatusException.getStatusMessage(status), + path); + } + return; + } + if (version >= 3) { String desc = bar.readString(); if(Log.isDebugEnabled()) { @@ -1965,7 +2036,7 @@ int processStatusResponse(SftpMessage bar) throws SftpStatusException, IOExcepti Log.debug("Received {}", SftpStatusException.getStatusMessage(status)); } - return status; + throw new SftpStatusException(status); } /** @@ -2019,9 +2090,14 @@ public void makeDirectory(String path, SftpFileAttributes attrs) msg.writeString(path, CHARSET_ENCODING); msg.write(attrs.toByteArray(getVersion())); + if(Log.isDebugEnabled()) { + Log.debug("Sending SSH_FXP_MKDIR for {}", + path); + } + sendMessage(msg); - getOKRequestStatus(requestId); + getOKRequestStatus(requestId, path); } catch (SshIOException ex) { throw ex.getRealException(); } catch (IOException ex) { @@ -2031,15 +2107,15 @@ public void makeDirectory(String path, SftpFileAttributes attrs) SftpHandle getHandle(SftpMessage bar, SftpFile file) throws SftpStatusException, SshException { - var response = getHandleResponse(bar); + var response = getHandleResponse(bar, file.getAbsolutePath()); return new SftpHandle(response, this, file); } - public SftpMessage getExtendedReply(UnsignedInteger32 requestId) throws SftpStatusException, SshException { - return getExtendedReply(getResponse(requestId)); + public SftpMessage getExtendedReply(UnsignedInteger32 requestId, String path) throws SftpStatusException, SshException { + return getExtendedReply(getResponse(requestId), path); } - public SftpMessage getExtendedReply(SftpMessage bar) throws SftpStatusException, SshException { + public SftpMessage getExtendedReply(SftpMessage bar, String path) throws SftpStatusException, SshException { try { if (bar.getType() == SSH_FXP_EXTENDED_REPLY) { if(Log.isDebugEnabled()) { @@ -2047,8 +2123,8 @@ public SftpMessage getExtendedReply(SftpMessage bar) throws SftpStatusException, } return bar; } else if (bar.getType() == SSH_FXP_STATUS) { - int status = processStatusResponse(bar); - throw new SftpStatusException(status); + processStatusResponse(bar, path); + throw new IllegalStateException("Received unexpected SSH_FX_OK in status response!"); } else { close(); throw new SshException( @@ -2062,17 +2138,17 @@ public SftpMessage getExtendedReply(SftpMessage bar) throws SftpStatusException, } } - public byte[] getHandleResponse(UnsignedInteger32 requestId) + public byte[] getHandleResponse(UnsignedInteger32 requestId, String path) throws SftpStatusException, SshException { - return getHandleResponse(getResponse(requestId)); + return getHandleResponse(getResponse(requestId), path); } public SftpHandle getHandle(UnsignedInteger32 requestId, SftpFile file) throws SftpStatusException, SshException { - return new SftpHandle(getHandleResponse(getResponse(requestId)), this, file); + return new SftpHandle(getHandleResponse(getResponse(requestId), file.getAbsolutePath()), this, file); } - public byte[] getHandleResponse(SftpMessage bar) + public byte[] getHandleResponse(SftpMessage bar, String path) throws SftpStatusException, SshException { try { @@ -2082,8 +2158,8 @@ public byte[] getHandleResponse(SftpMessage bar) } return bar.readBinaryString(); } else if (bar.getType() == SSH_FXP_STATUS) { - int status = processStatusResponse(bar); - throw new SftpStatusException(status); + processStatusResponse(bar, path); + throw new IllegalStateException("Received unexpected SSH_FX_OK in status response!"); } else { close(); throw new SshException( @@ -2097,7 +2173,7 @@ public byte[] getHandleResponse(SftpMessage bar) } } - SftpMessage getExtensionResponse(UnsignedInteger32 requestId) + SftpMessage getExtensionResponse(UnsignedInteger32 requestId, String path) throws SftpStatusException, SshException { SftpMessage bar = getResponse(requestId); @@ -2108,8 +2184,8 @@ SftpMessage getExtensionResponse(UnsignedInteger32 requestId) } return bar; } else if (bar.getType() == SSH_FXP_STATUS) { - int status = processStatusResponse(bar); - throw new SftpStatusException(status); + processStatusResponse(bar, path); + throw new IllegalStateException("Received unexpected SSH_FX_OK in status response!"); } else { close(); throw new SshException( diff --git a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpClient.java b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpClient.java index 3e7df00a..e2989d71 100644 --- a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpClient.java +++ b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpClient.java @@ -1038,17 +1038,18 @@ public SftpFile[] ls(String path, String filter, boolean regexFilter, int maximu } } - private SftpHandle openDirectoryHandle(String actual, ByteArrayWriter msg) throws SshException, SftpStatusException { - SftpFile file = new SftpFile(actual, sftp.getAttributes(actual), sftp, null); + private SftpHandle openDirectoryHandle(String path, ByteArrayWriter msg) throws SshException, SftpStatusException { + SftpFile file = new SftpFile(path, sftp.getAttributes(path), sftp, null); try { return file.handle(sftp.getHandleResponse( - sftp.sendExtensionMessage("open-directory-with-filter@sshtools.com", msg.toByteArray()))); + sftp.sendExtensionMessage("open-directory-with-filter@sshtools.com", msg.toByteArray()), + path)); } catch (SftpStatusException e) { if (Boolean.getBoolean("maverick.disableLocalFiltering")) { throw new SshException("Remote server does not support server side filtering", SshException.UNSUPPORTED_OPERATION); } - return file.handle(sftp.openDirectory(actual).getHandle()); + return file.handle(sftp.openDirectory(path).getHandle()); } } @@ -2479,7 +2480,9 @@ public void rename(String oldpath, String newpath, boolean posix) msg.writeString(resolveRemotePath(oldpath)); msg.writeString(resolveRemotePath(newpath)); - sftp.getOKRequestStatus(sftp.sendExtensionMessage("posix-rename@openssh.com", msg.toByteArray())); + sftp.getOKRequestStatus( + sftp.sendExtensionMessage("posix-rename@openssh.com", msg.toByteArray()), + newpath); } finally { msg.close(); @@ -2499,7 +2502,8 @@ public void copyRemoteFile(String sourceFile, String destinationFile, boolean ov msg.writeString(resolveRemotePath(destinationFile)); msg.writeBoolean(overwriteDestination); - sftp.getOKRequestStatus(sftp.sendExtensionMessage("copy-file", msg.toByteArray())); + sftp.getOKRequestStatus(sftp.sendExtensionMessage("copy-file", msg.toByteArray()), + destinationFile); } finally { msg.close(); @@ -2866,7 +2870,9 @@ public byte[] getRemoteHash(String remoteFile, long offset, long length, byte[] msg.writeUINT64(length); msg.writeBinaryString(quickCheck); - SftpMessage resp = sftp.getExtensionResponse(sftp.sendExtensionMessage("md5-hash", msg.toByteArray())); + SftpMessage resp = sftp.getExtensionResponse( + sftp.sendExtensionMessage("md5-hash", msg.toByteArray()), + remoteFile); resp.readString(); return resp.readBinaryString(); @@ -2924,8 +2930,10 @@ protected byte[] doCheckHashHandle(byte[] handle, long offset, long length, Remo msg.writeUINT64(length); msg.writeInt(0L); + SftpHandle h = sftp.getBestHandle(handle); return processCheckFileResponse( - sftp.getExtensionResponse(sftp.sendExtensionMessage("check-file-handle", msg.toByteArray())), + sftp.getExtensionResponse(sftp.sendExtensionMessage("check-file-handle", msg.toByteArray()), + h.getFile().getAbsolutePath()), algorithm); } finally { @@ -2933,20 +2941,21 @@ protected byte[] doCheckHashHandle(byte[] handle, long offset, long length, Remo } } - protected byte[] doCheckFileHandle(String filename, long offset, long length, RemoteHash algorithm) + protected byte[] doCheckFileHandle(String path, long offset, long length, RemoteHash algorithm) throws IOException, SftpStatusException, SshException { ByteArrayWriter msg = new ByteArrayWriter(); try { - msg.writeString(filename); + msg.writeString(path); msg.writeString(algorithm.name()); msg.writeUINT64(offset); msg.writeUINT64(length); msg.writeInt(0L); return processCheckFileResponse( - sftp.getExtensionResponse(sftp.sendExtensionMessage("check-file-name", msg.toByteArray())), + sftp.getExtensionResponse(sftp.sendExtensionMessage("check-file-name", msg.toByteArray()), + path), algorithm); } finally { @@ -2999,8 +3008,11 @@ protected byte[] doMD5HashHandle(byte[] handle, long offset, long length, byte[] msg.writeUINT64(length); msg.writeBinaryString(quickCheck); + SftpHandle h = sftp.getBestHandle(handle); + SftpMessage resp = sftp - .getExtensionResponse(sftp.sendExtensionMessage("md5-hash-handle", msg.toByteArray())); + .getExtensionResponse(sftp.sendExtensionMessage("md5-hash-handle", msg.toByteArray()), + h.getFile().getAbsolutePath()); resp.readString(); return resp.readBinaryString(); @@ -3869,7 +3881,7 @@ public void hardlink(String src, String dst) throws SshException, SftpStatusExce msg.writeString(dst); SftpChannel channel = getSubsystemChannel(); UnsignedInteger32 requestId = channel.sendExtensionMessage("hardlink@openssh.com", msg.toByteArray()); - channel.getOKRequestStatus(requestId); + channel.getOKRequestStatus(requestId, dst); } catch (IOException e) { throw new SshException(e); } @@ -3881,7 +3893,7 @@ public String getHomeDirectory(String username) throws SshException, SftpStatusE msg.writeString(username); SftpChannel channel = getSubsystemChannel(); UnsignedInteger32 requestId = channel.sendExtensionMessage("home-directory", msg.toByteArray()); - return channel.getSingleFileResponse(channel.getResponse(requestId), "SSH_FXP_NAME").getAbsolutePath(); + return channel.getSingleFileResponse(channel.getResponse(requestId), "SSH_FXP_NAME", "").getAbsolutePath(); } catch (IOException e) { throw new SshException(e); } @@ -3892,7 +3904,7 @@ public String makeTemporaryFolder() throws SshException, SftpStatusException { SftpChannel channel = getSubsystemChannel(); UnsignedInteger32 requestId = channel.sendExtensionMessage("make-temp-folder", null); - return channel.getSingleFileResponse(channel.getResponse(requestId), "SSH_FXP_NAME").getAbsolutePath(); + return channel.getSingleFileResponse(channel.getResponse(requestId), "SSH_FXP_NAME", "").getAbsolutePath(); } @@ -3900,7 +3912,7 @@ public String getTemporaryFolder() throws SshException, SftpStatusException { SftpChannel channel = getSubsystemChannel(); UnsignedInteger32 requestId = channel.sendExtensionMessage("get-temp-folder", null); - return channel.getSingleFileResponse(channel.getResponse(requestId), "SSH_FXP_NAME").getAbsolutePath(); + return channel.getSingleFileResponse(channel.getResponse(requestId), "SSH_FXP_NAME", "").getAbsolutePath(); } public StatVfs statVFS(String path) throws SshException, SftpStatusException { @@ -3911,8 +3923,8 @@ public StatVfs statVFS(String path) throws SshException, SftpStatusException { UnsignedInteger32 requestId = channel.sendExtensionMessage("statvfs@openssh.com", msg.toByteArray()); SftpMessage response = channel.getResponse(requestId); if (response.getType() == SftpChannel.SSH_FXP_STATUS) { - int status = sftp.processStatusResponse(response); - throw new SftpStatusException(status); + sftp.processStatusResponse(response, path); + throw new IllegalStateException("Received unexpected SSH_FX_OK in status response!"); } else { return new StatVfs(response); } diff --git a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpFileOutputStream.java b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpFileOutputStream.java index 6e0549c1..84217d8a 100644 --- a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpFileOutputStream.java +++ b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpFileOutputStream.java @@ -131,7 +131,7 @@ private boolean processNextResponse(int numOutstandingRequests) throws SftpStatu // Maybe look for a response if (outstandingRequests.size() > numOutstandingRequests) { UnsignedInteger32 requestid = (UnsignedInteger32) outstandingRequests.elementAt(0); - sftp.getOKRequestStatus(requestid); + sftp.getOKRequestStatus(requestid, handle.getFile().getAbsolutePath()); outstandingRequests.removeElementAt(0); } diff --git a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpHandle.java b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpHandle.java index 83d57cbe..40928261 100644 --- a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpHandle.java +++ b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpHandle.java @@ -162,7 +162,9 @@ public void copyTo(SftpHandle destinationHandle, UnsignedInteger64 fromOffset, U msg.writeBinaryString(destinationHandle.getHandle()); msg.writeUINT64(toOffset); - sftp.getOKRequestStatus(sftp.sendExtensionMessage("copy-data", msg.toByteArray())); + sftp.getOKRequestStatus( + sftp.sendExtensionMessage("copy-data", msg.toByteArray()), + file.getAbsolutePath()); } } @@ -248,7 +250,7 @@ public void close() throws IOException { } sftp.sendMessage(msg); - sftp.getOKRequestStatus(requestId); + sftp.getOKRequestStatus(requestId, file.getAbsolutePath()); } catch (SshException | SshIOException | SftpStatusException ex) { throw new IOException("Failed to close handle.", ex); } @@ -378,7 +380,7 @@ public void setAttributes(SftpFileAttributes attrs) throws SftpStatusException, sftp.sendMessage(msg); - sftp.getOKRequestStatus(requestId); + sftp.getOKRequestStatus(requestId, file.getAbsolutePath()); } catch (SshIOException ex) { throw ex.getRealException(); } catch (IOException ex) { @@ -545,14 +547,14 @@ public void performOptimizedWrite(String filename, int blocksize, int maxAsyncRe if (requests.size() > maxAsyncRequests) { sftp.requestId = (UnsignedInteger32) requests.elementAt(0); requests.removeElementAt(0); - sftp.getOKRequestStatus(sftp.requestId); + sftp.getOKRequestStatus(sftp.requestId, file.getAbsolutePath()); } } while (requests.size() > 0) { - sftp.getOKRequestStatus(requests.remove(0)); + sftp.getOKRequestStatus(requests.remove(0), file.getAbsolutePath()); } } @@ -929,7 +931,8 @@ public void performOptimizedRead(long length, int blocksize, OutputStream out, baw.writeBinaryString(new byte[0]); SftpMessage reply = sftp.getExtensionResponse( - sftp.sendExtensionMessage("md5-hash-handle", baw.toByteArray())); + sftp.sendExtensionMessage("md5-hash-handle", baw.toByteArray()), + file.getAbsolutePath()); reply.readString(); byte[] remoteDigest = reply.readBinaryString(); diff --git a/maverick-synergy-client/src/main/java/com/sshtools/client/tasks/PushTask.java b/maverick-synergy-client/src/main/java/com/sshtools/client/tasks/PushTask.java index 4d18ae5f..6625dc58 100644 --- a/maverick-synergy-client/src/main/java/com/sshtools/client/tasks/PushTask.java +++ b/maverick-synergy-client/src/main/java/com/sshtools/client/tasks/PushTask.java @@ -390,7 +390,7 @@ private Collection sendChunks(AbstractFile localFile, String remoteFo requestId = primarySftpClient.getSubsystemChannel().sendExtensionMessage("create-multipart-file@sshtools.com", msg.toByteArray()); - SftpMessage ext = primarySftpClient.getSubsystemChannel().getExtendedReply(requestId); + SftpMessage ext = primarySftpClient.getSubsystemChannel().getExtendedReply(requestId, remotePath); byte[] handle = ext.readBinaryString(); var blocksize = ext.readInt(); From 0cb55b8e48100014d373ca1b21a7dc31df5dd49c Mon Sep 17 00:00:00 2001 From: Brett Smith Date: Sat, 22 Jun 2024 10:07:01 +0100 Subject: [PATCH 42/51] Guard against a handle that has no `SftpFile` associated with it, and just use `` as the filename. Being as this is only for logging purposes, that will suffice for now. Use of `getBestHandle()` is to be discouraged for this reason, the intention was always to remove it at 3.2.0. All the SFTP NIO tests are passing again except the 3 remote-to-remote tests. I am not sure if its all these recent SFTP changes that broke this those, so that will need to be checked first --- .../src/main/java/com/sshtools/client/sftp/SftpChannel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java index 821da894..e839e49a 100644 --- a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java +++ b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java @@ -807,7 +807,7 @@ public void writeFile(byte[] handle, UnsignedInteger64 offset, byte[] data, SftpHandle h = getBestHandle(handle); getOKRequestStatus(h.postWriteRequest(offset.longValue(), data, off, len), - h.getFile().getAbsolutePath()); + h.getFile() == null ? "" : h.getFile().getAbsolutePath()); } /** From 3ca40e545fa466f7ddb0200f8340114166891f7c Mon Sep 17 00:00:00 2001 From: Brett Smith Date: Sat, 22 Jun 2024 11:18:50 +0100 Subject: [PATCH 43/51] Trying to send `SSH_FXP_STAT` after a `SSH_FXP_OPEN` had been sent, but before the response had been received. --- .../src/main/java/com/sshtools/client/sftp/SftpChannel.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java index e839e49a..13c3a81f 100644 --- a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java +++ b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java @@ -1621,6 +1621,9 @@ else if((flags & OPEN_CREATE)==OPEN_CREATE) { } try { + + SftpFileAttributes attributes = getAttributes(path); + UnsignedInteger32 requestId = nextRequestId(); Packet msg = createPacket(); msg.write(SSH_FXP_OPEN); @@ -1636,7 +1639,7 @@ else if((flags & OPEN_CREATE)==OPEN_CREATE) { sendMessage(msg); - SftpFile file = new SftpFile(path, getAttributes(path), this, null); + SftpFile file = new SftpFile(path, attributes, this, null); SftpHandle handle = file.handle(getHandleResponse(requestId, path)); EventServiceImplementation.getInstance().fireEvent( From 22cfb8d81addba66769ed9e9f25c311f922ceb89 Mon Sep 17 00:00:00 2001 From: Brett Smith Date: Sat, 22 Jun 2024 11:23:41 +0100 Subject: [PATCH 44/51] Rearranged order a little. --- .../src/main/java/com/sshtools/client/sftp/SftpChannel.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java index 13c3a81f..18f58da5 100644 --- a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java +++ b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java @@ -1622,7 +1622,6 @@ else if((flags & OPEN_CREATE)==OPEN_CREATE) { try { - SftpFileAttributes attributes = getAttributes(path); UnsignedInteger32 requestId = nextRequestId(); Packet msg = createPacket(); @@ -1637,10 +1636,11 @@ else if((flags & OPEN_CREATE)==OPEN_CREATE) { } sendMessage(msg); + byte[] handleResponse = getHandleResponse(requestId, path); - + SftpFileAttributes attributes = getAttributes(path); SftpFile file = new SftpFile(path, attributes, this, null); - SftpHandle handle = file.handle(getHandleResponse(requestId, path)); + SftpHandle handle = file.handle(handleResponse); EventServiceImplementation.getInstance().fireEvent( (new Event(this, From 15ff0f271574c61fe1ae590ba9503dc98b9b8833 Mon Sep 17 00:00:00 2001 From: Brett Smith Date: Sat, 22 Jun 2024 11:45:19 +0100 Subject: [PATCH 45/51] No functional change, just makes it a little clearer that attributes are effectively being refreshed after opening of the file, which *might* have changed them. --- .../src/main/java/com/sshtools/client/sftp/SftpChannel.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java index 18f58da5..be2c61bf 100644 --- a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java +++ b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java @@ -1622,7 +1622,6 @@ else if((flags & OPEN_CREATE)==OPEN_CREATE) { try { - UnsignedInteger32 requestId = nextRequestId(); Packet msg = createPacket(); msg.write(SSH_FXP_OPEN); @@ -1638,8 +1637,9 @@ else if((flags & OPEN_CREATE)==OPEN_CREATE) { sendMessage(msg); byte[] handleResponse = getHandleResponse(requestId, path); - SftpFileAttributes attributes = getAttributes(path); - SftpFile file = new SftpFile(path, attributes, this, null); + attrs = getAttributes(path); + + SftpFile file = new SftpFile(path, attrs, this, null); SftpHandle handle = file.handle(handleResponse); EventServiceImplementation.getInstance().fireEvent( From 59032eee648f605f1ac2bd491115046857e5b294 Mon Sep 17 00:00:00 2001 From: Lee Painter Date: Mon, 15 Jul 2024 09:04:04 +0100 Subject: [PATCH 46/51] Improved logging for SFTP. --- .../com/sshtools/common/events/Event.java | 15 +++++++ .../events/EventServiceImplementation.java | 5 ++- .../com/sshtools/client/sftp/SftpChannel.java | 13 +++--- .../client/sftp/SftpFileInputStream.java | 32 +++++++++++---- .../com/sshtools/client/sftp/SftpHandle.java | 41 ++++++++++++++----- .../com/sshtools/synergy/ssh/Connection.java | 7 ++++ 6 files changed, 87 insertions(+), 26 deletions(-) diff --git a/maverick-base/src/main/java/com/sshtools/common/events/Event.java b/maverick-base/src/main/java/com/sshtools/common/events/Event.java index 96cd6e39..f6ab1554 100644 --- a/maverick-base/src/main/java/com/sshtools/common/events/Event.java +++ b/maverick-base/src/main/java/com/sshtools/common/events/Event.java @@ -107,6 +107,21 @@ public String getAllAttributes() { return buff.toString(); } + + public String logAttributes() { + StringBuffer buff = new StringBuffer(); + for (String key : eventAttributes.keySet()) { + Object value = eventAttributes.get(key); + if(buff.length() > 0) { + buff.append(" "); + } + buff.append(key); + buff.append("="); + buff.append(String.valueOf(value)); + } + + return buff.toString(); + } /** * Add an attribute to the event diff --git a/maverick-base/src/main/java/com/sshtools/common/events/EventServiceImplementation.java b/maverick-base/src/main/java/com/sshtools/common/events/EventServiceImplementation.java index 70c1d5c1..308f4bf3 100644 --- a/maverick-base/src/main/java/com/sshtools/common/events/EventServiceImplementation.java +++ b/maverick-base/src/main/java/com/sshtools/common/events/EventServiceImplementation.java @@ -141,7 +141,7 @@ public void fireEvent(final Event evt) { } if(Log.isDebugEnabled()) { - Log.debug("Firing {} success={}", getEventName(evt.getId()), evt.getState() ? "true" : "false"); + Log.debug("Firing {} success={} {}", getEventName(evt.getId()), evt.getState() ? "true" : "false", evt.logAttributes()); } Object obj = (Object) evt.getAttribute(EventCodes.ATTRIBUTE_CONNECTION); @@ -174,7 +174,7 @@ public void fireEvent(final Event evt) { } } - public void setProcessAllEventsOnEventException(boolean processAllEventsOnEventException) { + public void setProcessAllEventsOnEventException(boolean processAllEventsOnEventException) { this.processAllEventsOnEventException = processAllEventsOnEventException; } @@ -186,4 +186,5 @@ public void addListener(EventListener listener) { public void removeListener(EventListener listener) { globalListeners.remove(listener); } + } diff --git a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java index be2c61bf..9997c0b6 100644 --- a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java +++ b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java @@ -56,6 +56,7 @@ import com.sshtools.common.ssh.SshConnection; import com.sshtools.common.ssh.SshException; import com.sshtools.common.ssh.SshIOException; +import com.sshtools.common.util.Base64; import com.sshtools.common.util.ByteArrayReader; import com.sshtools.common.util.UnsignedInteger32; import com.sshtools.common.util.UnsignedInteger64; @@ -2020,7 +2021,6 @@ void processStatusResponse(SftpMessage bar, String path) throws SftpStatusExcept if(Log.isDebugEnabled()) { Log.debug("Received SSH_FX_OK for {}", - SftpStatusException.getStatusMessage(status), path); } return; @@ -2029,14 +2029,14 @@ void processStatusResponse(SftpMessage bar, String path) throws SftpStatusExcept if (version >= 3) { String desc = bar.readString(); if(Log.isDebugEnabled()) { - Log.debug("Received {} with message {}", - SftpStatusException.getStatusMessage(status), desc); + Log.debug("Received {} with message {} for {}", + SftpStatusException.getStatusMessage(status), desc, path); } throw new SftpStatusException(status, desc); } if(Log.isDebugEnabled()) { - Log.debug("Received {}", SftpStatusException.getStatusMessage(status)); + Log.debug("Received {} for {}", SftpStatusException.getStatusMessage(status), path); } throw new SftpStatusException(status); @@ -2156,10 +2156,11 @@ public byte[] getHandleResponse(SftpMessage bar, String path) try { if (bar.getType() == SSH_FXP_HANDLE) { + byte[] handle = bar.readBinaryString(); if(Log.isDebugEnabled()) { - Log.debug("Received SSH_FXP_HANDLE"); + Log.debug("Received SSH_FXP_HANDLE for {} handle={}", path, Base64.encodeBytes(handle, true)); } - return bar.readBinaryString(); + return handle; } else if (bar.getType() == SSH_FXP_STATUS) { processStatusResponse(bar, path); throw new IllegalStateException("Received unexpected SSH_FX_OK in status response!"); diff --git a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpFileInputStream.java b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpFileInputStream.java index d8feb7b4..60bff7df 100644 --- a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpFileInputStream.java +++ b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpFileInputStream.java @@ -44,6 +44,7 @@ public class SftpFileInputStream extends InputStream { private long position; private SftpMessage currentMessage; private int currentMessageRemaining; + private long readPosition = 0; private boolean isEOF = false; private boolean error = false; private UnsignedInteger64 length; @@ -134,6 +135,7 @@ public int read(byte[] buffer, int offset, int len) throws IOException { System.arraycopy(currentMessage.array(), currentMessage.getPosition(), buffer, offset, count); + readPosition += count; currentMessageRemaining -= count; currentMessage.skip(count); @@ -169,10 +171,18 @@ private void bufferNextMessage() throws SshException, IOException, currentMessage = sftp.getResponse(requestid); if (currentMessage.getType() == SftpChannel.SSH_FXP_DATA) { + + currentMessageRemaining = (int) currentMessage.readInt(); + if(Log.isDebugEnabled()) { - Log.debug("Received SSH_FXP_DATA for {}", handle.getFile().getFilename()); + Log.debug("Received SSH_FXP_DATA with {} bytes at position {} for {} requestId={}", + currentMessageRemaining, + readPosition, + handle.getFile().getFilename(), + requestid); } - currentMessageRemaining = (int) currentMessage.readInt(); + + } else if (currentMessage.getType() == SftpChannel.SSH_FXP_STATUS) { try { @@ -262,12 +272,18 @@ public void close() throws IOException { try { handle.close(); - UnsignedInteger32 requestid; - while (!error && outstandingRequests.size() > 0) { - requestid = (UnsignedInteger32) outstandingRequests - .elementAt(0); - outstandingRequests.remove(0); - sftp.getResponse(requestid).release(); + if(!error && outstandingRequests.size() > 0) { + if(Log.isWarnEnabled()) { + Log.warn("Discarding {} data messages through premature closing of InputStream for file {}", outstandingRequests.size(), handle.getFile().getFilename()); + } + UnsignedInteger32 requestid; + while (!error && outstandingRequests.size() > 0) { + + + requestid = (UnsignedInteger32) outstandingRequests.elementAt(0); + outstandingRequests.remove(0); + sftp.getResponse(requestid).release(); + } } } catch (SshException ex) { throw new SshIOException(ex); diff --git a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpHandle.java b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpHandle.java index 40928261..626a312d 100644 --- a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpHandle.java +++ b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpHandle.java @@ -43,6 +43,7 @@ import com.sshtools.common.ssh.Packet; import com.sshtools.common.ssh.SshException; import com.sshtools.common.ssh.SshIOException; +import com.sshtools.common.util.Base64; import com.sshtools.common.util.ByteArrayWriter; import com.sshtools.common.util.IOUtils; import com.sshtools.common.util.UnsignedInteger32; @@ -56,7 +57,7 @@ public final class SftpHandle implements Closeable { private volatile boolean closed; private volatile boolean performVerification = false; - + SftpHandle(byte[] handle, SftpChannel sftp, SftpFile file) { super(); this.handle = handle; @@ -246,8 +247,9 @@ public void close() throws IOException { msg.writeBinaryString(handle); if(Log.isDebugEnabled()) { - Log.debug("Sending SSH_FXP_CLOSE for {}", file.getFilename()); + Log.debug("Sending SSH_FXP_CLOSE for {} requestId={}", file.getFilename(), requestId); } + sftp.sendMessage(msg); sftp.getOKRequestStatus(requestId, file.getAbsolutePath()); @@ -304,9 +306,10 @@ public int listChildren(List children) throws SftpStatusException, Ssh sftp.sendMessage(msg); - if (Log.isDebugEnabled()) { - Log.debug("Sending list children request"); + if(Log.isDebugEnabled()) { + Log.debug("Sending SSH_FXP_READDIR for {} requestId=", file.getFilename(), requestId); } + SftpMessage bar = sftp.getResponse(requestId); try { @@ -378,6 +381,10 @@ public void setAttributes(SftpFileAttributes attrs) throws SftpStatusException, msg.writeBinaryString(handle); msg.write(attrs.toByteArray(sftp.getVersion())); + if(Log.isDebugEnabled()) { + Log.debug("Sending SSH_FXP_FSETSTAT for {} requestId=", file.getFilename(), requestId); + } + sftp.sendMessage(msg); sftp.getOKRequestStatus(requestId, file.getAbsolutePath()); @@ -415,6 +422,12 @@ public SftpFileAttributes getAttributes() throws SftpStatusException, SshExcepti | SftpFileAttributes.SSH_FILEXFER_ATTR_SUBSECOND_TIMES | SftpFileAttributes.SSH_FILEXFER_ATTR_EXTENDED); } + + + if(Log.isDebugEnabled()) { + Log.debug("Sending SSH_FXP_FSTAT for {} requestId=", file.getFilename(), requestId); + } + sftp.sendMessage(msg); SftpMessage attrMessage = sftp.getResponse(requestId); @@ -609,6 +622,10 @@ public int readFile(UnsignedInteger64 offset, byte[] output, int off, int len) msg.write(offset.toByteArray()); msg.writeInt(len); + if(Log.isDebugEnabled()) { + Log.debug("Sending SSH_FXP_READ for {} bytes at position {} for {} requestId=", len, offset.toString(), file.getFilename(), requestId); + } + sftp.sendMessage(msg); SftpMessage bar = sftp.getResponse(requestId); @@ -618,10 +635,10 @@ public int readFile(UnsignedInteger64 offset, byte[] output, int off, int len) byte[] msgdata = bar.readBinaryString(); System.arraycopy(msgdata, 0, output, off, msgdata.length); - if (Log.isDebugEnabled()) { - Log.debug("Received SSH_FXP_DATA channel={} requestId={} offset={} blocksize={}", - sftp.getSession().getLocalId(), requestId.toString(), offset.toString(), msgdata.length); + if(Log.isDebugEnabled()) { + Log.debug("Received SSH_FXP_DATA with {} bytes at position {} for {} requestId=", msgdata.length, offset.toString(), file.getFilename(), requestId); } + return msgdata.length; } else if (bar.getType() == SftpChannel.SSH_FXP_STATUS) { int status = (int) bar.readInt(); @@ -673,9 +690,8 @@ public UnsignedInteger32 postReadRequest(long offset, int len) throws SftpStatus msg.writeUINT64(offset); msg.writeInt(len); - if (Log.isDebugEnabled()) { - Log.debug("Sending SSH_FXP_READ channel={} requestId={} offset={} blocksize={}", sftp.getSession().getLocalId(), - requestId.toString(), offset, len); + if(Log.isDebugEnabled()) { + Log.debug("Sending SSH_FXP_READ for {} bytes at position {} for {} requestId=", len, offset, file.getFilename(), requestId); } sftp.sendMessage(msg); @@ -857,6 +873,7 @@ public void performOptimizedRead(long length, int blocksize, OutputStream out, } catch (IOException e) { throw new TransferCancelledException(); } + transfered += dataLen; if (progress != null) { progress.progressed(transfered); @@ -1082,6 +1099,10 @@ public UnsignedInteger32 postWriteRequest(long position, byte[] data, int off, i msg.writeUINT64(position); msg.writeBinaryString(data, off, len); + if(Log.isDebugEnabled()) { + Log.debug("Sending SSH_FXP_WRITE with {} bytes at position {} for {} requestId=", len, position, file.getFilename(), requestId); + } + sftp.sendMessage(msg); return requestId; diff --git a/maverick-synergy-common/src/main/java/com/sshtools/synergy/ssh/Connection.java b/maverick-synergy-common/src/main/java/com/sshtools/synergy/ssh/Connection.java index d9c9dfda..32de50d3 100644 --- a/maverick-synergy-common/src/main/java/com/sshtools/synergy/ssh/Connection.java +++ b/maverick-synergy-common/src/main/java/com/sshtools/synergy/ssh/Connection.java @@ -434,4 +434,11 @@ public void setRemoteAddress(InetSocketAddress remoteAddress) { this.remoteAddress = remoteAddress; } + @Override + public String toString() { + return transport.getUUID(); + } + + + } From b20d13cc9f2af11e1a4d8226fd527590772bc4bd Mon Sep 17 00:00:00 2001 From: Lee Painter Date: Tue, 16 Jul 2024 09:28:11 +0100 Subject: [PATCH 47/51] Further logging improvements. Fix potential regression with next requestId being overridden from SftpHandle. --- .../com/sshtools/client/sftp/SftpChannel.java | 52 +++++++++---------- .../com/sshtools/client/sftp/SftpClient.java | 8 +-- .../com/sshtools/client/sftp/SftpHandle.java | 24 ++++----- 3 files changed, 41 insertions(+), 43 deletions(-) diff --git a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java index 9997c0b6..4c3a0272 100644 --- a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java +++ b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java @@ -164,7 +164,7 @@ public class SftpChannel extends AbstractSubsystem { int version = MAX_VERSION; int serverVersion = -1; - UnsignedInteger32 requestId = new UnsignedInteger32(0); + UnsignedInteger32 nextRequestId = new UnsignedInteger32(0); Map responses = new ConcurrentHashMap(); SftpThreadSynchronizer sync = new SftpThreadSynchronizer(); Map extensions = new HashMap(); @@ -519,8 +519,8 @@ public byte[] getExtension(String name) { } UnsignedInteger32 nextRequestId() { - requestId = UnsignedInteger32.add(requestId, 1); - return requestId; + nextRequestId = UnsignedInteger32.add(nextRequestId, 1); + return nextRequestId; } public void close() { @@ -673,7 +673,7 @@ public void getOKRequestStatus(UnsignedInteger32 requestId, String path) SftpMessage bar = getResponse(requestId); try { if (bar.getType() == SSH_FXP_STATUS) { - processStatusResponse(bar, path); + processStatusResponse(bar, path, requestId); return; } close(); @@ -1271,14 +1271,14 @@ public String getSymbolicLinkTarget(String linkpath) msg.writeString(linkpath, CHARSET_ENCODING); if(Log.isDebugEnabled()) { - Log.debug("Sending SSH_FXP_READLINK for {}", linkpath); + Log.debug("Sending SSH_FXP_READLINK for {} requestId={}", linkpath, requestId); } sendMessage(msg); SftpMessage fileMsg = getResponse(requestId); if (fileMsg.getType() == SSH_FXP_STATUS) { - processStatusResponse(fileMsg, linkpath); + processStatusResponse(fileMsg, linkpath, requestId); throw new IllegalStateException("Received unexpected SSH_FX_OK in status response!"); } else { try { @@ -1329,7 +1329,7 @@ public String getAbsolutePath(String path) throws SftpStatusException, sendMessage(msg); - return getSingleFileResponse(getResponse(requestId), "SSH_FXP_REALPATH", path).getAbsolutePath(); + return getSingleFileResponse(getResponse(requestId), "SSH_FXP_REALPATH", path, requestId).getAbsolutePath(); } catch (SshIOException ex) { throw ex.getRealException(); @@ -1347,7 +1347,7 @@ public String getAbsolutePath(String path) throws SftpStatusException, * @throws SshException * @throws SftpStatusException */ - public SftpFile getSingleFileResponse(SftpMessage bar, String messageName, String path) throws SshException, SftpStatusException { + public SftpFile getSingleFileResponse(SftpMessage bar, String messageName, String path, UnsignedInteger32 requestId) throws SshException, SftpStatusException { try { if (bar.getType() == SSH_FXP_NAME) { SftpFile[] files = extractFiles(bar, null); @@ -1365,7 +1365,7 @@ public SftpFile getSingleFileResponse(SftpMessage bar, String messageName, Strin } return files[0]; } else if (bar.getType() == SSH_FXP_STATUS) { - processStatusResponse(bar, path); + processStatusResponse(bar, path, requestId); throw new IllegalStateException("Received unexpected SSH_FX_OK in status response!"); } else { close(); @@ -1978,7 +1978,7 @@ protected SftpFileAttributes getAttributes(String path, int messageId, String me SftpMessage bar = getResponse(requestId); try { - return extractAttributes(bar, path); + return extractAttributes(bar, path, requestId); } finally { bar.release(); } @@ -1989,7 +1989,7 @@ protected SftpFileAttributes getAttributes(String path, int messageId, String me } } - SftpFileAttributes extractAttributes(SftpMessage bar, String path) + SftpFileAttributes extractAttributes(SftpMessage bar, String path, UnsignedInteger32 requestId) throws SftpStatusException, SshException { try { if (bar.getType() == SSH_FXP_ATTRS) { @@ -1998,7 +1998,7 @@ SftpFileAttributes extractAttributes(SftpMessage bar, String path) } return SftpFileAttributesBuilder.of(bar, getVersion(), getCharsetEncoding()).build(); } else if (bar.getType() == SSH_FXP_STATUS) { - processStatusResponse(bar, path); + processStatusResponse(bar, path, requestId); throw new IllegalStateException("Received unexpected SSH_FX_OK in status response!"); } else { close(); @@ -2013,15 +2013,15 @@ SftpFileAttributes extractAttributes(SftpMessage bar, String path) } } - void processStatusResponse(SftpMessage bar, String path) throws SftpStatusException, IOException { + void processStatusResponse(SftpMessage bar, String path, UnsignedInteger32 requestId) throws SftpStatusException, IOException { int status = (int) bar.readInt(); if(status == SftpStatusException.SSH_FX_OK) { if(Log.isDebugEnabled()) { - Log.debug("Received SSH_FX_OK for {}", - path); + Log.debug("Received SSH_FX_OK for {} requestId={}", + path, requestId); } return; } @@ -2108,17 +2108,17 @@ public void makeDirectory(String path, SftpFileAttributes attrs) } } - SftpHandle getHandle(SftpMessage bar, SftpFile file) + SftpHandle getHandle(SftpMessage bar, SftpFile file, UnsignedInteger32 requestId) throws SftpStatusException, SshException { - var response = getHandleResponse(bar, file.getAbsolutePath()); + var response = getHandleResponse(bar, file.getAbsolutePath(), requestId); return new SftpHandle(response, this, file); } public SftpMessage getExtendedReply(UnsignedInteger32 requestId, String path) throws SftpStatusException, SshException { - return getExtendedReply(getResponse(requestId), path); + return getExtendedReply(getResponse(requestId), path, requestId); } - public SftpMessage getExtendedReply(SftpMessage bar, String path) throws SftpStatusException, SshException { + public SftpMessage getExtendedReply(SftpMessage bar, String path, UnsignedInteger32 requestId) throws SftpStatusException, SshException { try { if (bar.getType() == SSH_FXP_EXTENDED_REPLY) { if(Log.isDebugEnabled()) { @@ -2126,7 +2126,7 @@ public SftpMessage getExtendedReply(SftpMessage bar, String path) throws SftpSta } return bar; } else if (bar.getType() == SSH_FXP_STATUS) { - processStatusResponse(bar, path); + processStatusResponse(bar, path, requestId); throw new IllegalStateException("Received unexpected SSH_FX_OK in status response!"); } else { close(); @@ -2143,15 +2143,15 @@ public SftpMessage getExtendedReply(SftpMessage bar, String path) throws SftpSta public byte[] getHandleResponse(UnsignedInteger32 requestId, String path) throws SftpStatusException, SshException { - return getHandleResponse(getResponse(requestId), path); + return getHandleResponse(getResponse(requestId), path, requestId); } public SftpHandle getHandle(UnsignedInteger32 requestId, SftpFile file) throws SftpStatusException, SshException { - return new SftpHandle(getHandleResponse(getResponse(requestId), file.getAbsolutePath()), this, file); + return new SftpHandle(getHandleResponse(getResponse(requestId), file.getAbsolutePath(), requestId), this, file); } - public byte[] getHandleResponse(SftpMessage bar, String path) + public byte[] getHandleResponse(SftpMessage bar, String path, UnsignedInteger32 requestId) throws SftpStatusException, SshException { try { @@ -2162,7 +2162,7 @@ public byte[] getHandleResponse(SftpMessage bar, String path) } return handle; } else if (bar.getType() == SSH_FXP_STATUS) { - processStatusResponse(bar, path); + processStatusResponse(bar, path, requestId); throw new IllegalStateException("Received unexpected SSH_FX_OK in status response!"); } else { close(); @@ -2179,7 +2179,7 @@ public byte[] getHandleResponse(SftpMessage bar, String path) SftpMessage getExtensionResponse(UnsignedInteger32 requestId, String path) throws SftpStatusException, SshException { - + SftpMessage bar = getResponse(requestId); try { if (bar.getType() == SSH_FXP_EXTENDED_REPLY) { @@ -2188,7 +2188,7 @@ SftpMessage getExtensionResponse(UnsignedInteger32 requestId, String path) } return bar; } else if (bar.getType() == SSH_FXP_STATUS) { - processStatusResponse(bar, path); + processStatusResponse(bar, path, requestId); throw new IllegalStateException("Received unexpected SSH_FX_OK in status response!"); } else { close(); diff --git a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpClient.java b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpClient.java index e2989d71..9ec1ea6d 100644 --- a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpClient.java +++ b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpClient.java @@ -3893,7 +3893,7 @@ public String getHomeDirectory(String username) throws SshException, SftpStatusE msg.writeString(username); SftpChannel channel = getSubsystemChannel(); UnsignedInteger32 requestId = channel.sendExtensionMessage("home-directory", msg.toByteArray()); - return channel.getSingleFileResponse(channel.getResponse(requestId), "SSH_FXP_NAME", "").getAbsolutePath(); + return channel.getSingleFileResponse(channel.getResponse(requestId), "SSH_FXP_NAME", "", requestId).getAbsolutePath(); } catch (IOException e) { throw new SshException(e); } @@ -3904,7 +3904,7 @@ public String makeTemporaryFolder() throws SshException, SftpStatusException { SftpChannel channel = getSubsystemChannel(); UnsignedInteger32 requestId = channel.sendExtensionMessage("make-temp-folder", null); - return channel.getSingleFileResponse(channel.getResponse(requestId), "SSH_FXP_NAME", "").getAbsolutePath(); + return channel.getSingleFileResponse(channel.getResponse(requestId), "SSH_FXP_NAME", "", requestId).getAbsolutePath(); } @@ -3912,7 +3912,7 @@ public String getTemporaryFolder() throws SshException, SftpStatusException { SftpChannel channel = getSubsystemChannel(); UnsignedInteger32 requestId = channel.sendExtensionMessage("get-temp-folder", null); - return channel.getSingleFileResponse(channel.getResponse(requestId), "SSH_FXP_NAME", "").getAbsolutePath(); + return channel.getSingleFileResponse(channel.getResponse(requestId), "SSH_FXP_NAME", "", requestId).getAbsolutePath(); } public StatVfs statVFS(String path) throws SshException, SftpStatusException { @@ -3923,7 +3923,7 @@ public StatVfs statVFS(String path) throws SshException, SftpStatusException { UnsignedInteger32 requestId = channel.sendExtensionMessage("statvfs@openssh.com", msg.toByteArray()); SftpMessage response = channel.getResponse(requestId); if (response.getType() == SftpChannel.SSH_FXP_STATUS) { - sftp.processStatusResponse(response, path); + sftp.processStatusResponse(response, path, requestId); throw new IllegalStateException("Received unexpected SSH_FX_OK in status response!"); } else { return new StatVfs(response); diff --git a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpHandle.java b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpHandle.java index 626a312d..9ae66a7b 100644 --- a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpHandle.java +++ b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpHandle.java @@ -29,6 +29,7 @@ import java.security.DigestOutputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Vector; @@ -43,7 +44,6 @@ import com.sshtools.common.ssh.Packet; import com.sshtools.common.ssh.SshException; import com.sshtools.common.ssh.SshIOException; -import com.sshtools.common.util.Base64; import com.sshtools.common.util.ByteArrayWriter; import com.sshtools.common.util.IOUtils; import com.sshtools.common.util.UnsignedInteger32; @@ -307,7 +307,7 @@ public int listChildren(List children) throws SftpStatusException, Ssh sftp.sendMessage(msg); if(Log.isDebugEnabled()) { - Log.debug("Sending SSH_FXP_READDIR for {} requestId=", file.getFilename(), requestId); + Log.debug("Sending SSH_FXP_READDIR for {} requestId={}", file.getFilename(), requestId); } SftpMessage bar = sftp.getResponse(requestId); @@ -432,7 +432,7 @@ public SftpFileAttributes getAttributes() throws SftpStatusException, SshExcepti SftpMessage attrMessage = sftp.getResponse(requestId); try { - return sftp.extractAttributes(attrMessage, getFile().getFilename()); + return sftp.extractAttributes(attrMessage, getFile().getFilename(), requestId); } finally { attrMessage.release(); } @@ -533,7 +533,7 @@ public void performOptimizedWrite(String filename, int blocksize, int maxAsyncRe progress.progressed(transfered); } - Vector requests = new Vector(); + List requests = new ArrayList(); // BufferedInputStream is not in J2ME, whatever type of input stream // has been passed in can be used in conjunction with the abstract // InputStream class. @@ -545,7 +545,7 @@ public void performOptimizedWrite(String filename, int blocksize, int maxAsyncRe if (buffered == -1) break; - requests.addElement(postWriteRequest(transfered, buf, 0, buffered)); + requests.add(postWriteRequest(transfered, buf, 0, buffered)); transfered += buffered; @@ -558,10 +558,8 @@ public void performOptimizedWrite(String filename, int blocksize, int maxAsyncRe } if (requests.size() > maxAsyncRequests) { - sftp.requestId = (UnsignedInteger32) requests.elementAt(0); - requests.removeElementAt(0); - sftp.getOKRequestStatus(sftp.requestId, file.getAbsolutePath()); - + UnsignedInteger32 requestId = (UnsignedInteger32) requests.remove(0); + sftp.getOKRequestStatus(requestId, file.getAbsolutePath()); } } @@ -623,7 +621,7 @@ public int readFile(UnsignedInteger64 offset, byte[] output, int off, int len) msg.writeInt(len); if(Log.isDebugEnabled()) { - Log.debug("Sending SSH_FXP_READ for {} bytes at position {} for {} requestId=", len, offset.toString(), file.getFilename(), requestId); + Log.debug("Sending SSH_FXP_READ for {} bytes at position {} for {} requestId={}", len, offset.toString(), file.getFilename(), requestId); } sftp.sendMessage(msg); @@ -636,7 +634,7 @@ public int readFile(UnsignedInteger64 offset, byte[] output, int off, int len) System.arraycopy(msgdata, 0, output, off, msgdata.length); if(Log.isDebugEnabled()) { - Log.debug("Received SSH_FXP_DATA with {} bytes at position {} for {} requestId=", msgdata.length, offset.toString(), file.getFilename(), requestId); + Log.debug("Received SSH_FXP_DATA with {} bytes at position {} for {} requestId={}", msgdata.length, offset.toString(), file.getFilename(), requestId); } return msgdata.length; @@ -691,7 +689,7 @@ public UnsignedInteger32 postReadRequest(long offset, int len) throws SftpStatus msg.writeInt(len); if(Log.isDebugEnabled()) { - Log.debug("Sending SSH_FXP_READ for {} bytes at position {} for {} requestId=", len, offset, file.getFilename(), requestId); + Log.debug("Sending SSH_FXP_READ for {} bytes at position {} for {} requestId={}", len, offset, file.getFilename(), requestId); } sftp.sendMessage(msg); @@ -1100,7 +1098,7 @@ public UnsignedInteger32 postWriteRequest(long position, byte[] data, int off, i msg.writeBinaryString(data, off, len); if(Log.isDebugEnabled()) { - Log.debug("Sending SSH_FXP_WRITE with {} bytes at position {} for {} requestId=", len, position, file.getFilename(), requestId); + Log.debug("Sending SSH_FXP_WRITE with {} bytes at position {} for {} requestId={}", len, position, file.getFilename(), requestId); } sftp.sendMessage(msg); From eee9643183045b2787628106df41e8e532f4274a Mon Sep 17 00:00:00 2001 From: Lee Painter Date: Fri, 20 Sep 2024 15:04:04 +0100 Subject: [PATCH 48/51] Fix hanging authentication when none authentication succeeds. --- .../com/sshtools/client/AuthenticationProtocolClient.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/maverick-synergy-client/src/main/java/com/sshtools/client/AuthenticationProtocolClient.java b/maverick-synergy-client/src/main/java/com/sshtools/client/AuthenticationProtocolClient.java index 45da84c3..0482d5f8 100644 --- a/maverick-synergy-client/src/main/java/com/sshtools/client/AuthenticationProtocolClient.java +++ b/maverick-synergy-client/src/main/java/com/sshtools/client/AuthenticationProtocolClient.java @@ -151,6 +151,11 @@ public boolean processMessage(byte[] msg) throws IOException, SshException { completedAuthentications.add(currentAuthenticator.getName()); + + if(currentAuthenticator.getName().equals("none")) { + transport.getConnectFuture().connected(transport, transport.getConnection()); + } + currentAuthenticator.success(); From 53428cc56e0a67c88dfc9541cea1eba4a31d4a85 Mon Sep 17 00:00:00 2001 From: Lee Painter Date: Fri, 20 Sep 2024 15:04:20 +0100 Subject: [PATCH 49/51] NPE issues with attributes --- .../com/sshtools/client/sftp/SftpChannel.java | 40 +++++++++++-------- .../com/sshtools/client/sftp/SftpClient.java | 4 +- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java index 4c3a0272..6e16fbf4 100644 --- a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java +++ b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpChannel.java @@ -1623,6 +1623,9 @@ else if((flags & OPEN_CREATE)==OPEN_CREATE) { try { + try { + attrs = getAttributes(path); + } catch(SftpStatusException e) { } UnsignedInteger32 requestId = nextRequestId(); Packet msg = createPacket(); msg.write(SSH_FXP_OPEN); @@ -1636,12 +1639,9 @@ else if((flags & OPEN_CREATE)==OPEN_CREATE) { } sendMessage(msg); - byte[] handleResponse = getHandleResponse(requestId, path); - - attrs = getAttributes(path); SftpFile file = new SftpFile(path, attrs, this, null); - SftpHandle handle = file.handle(handleResponse); + SftpHandle handle = getHandle(requestId, file); EventServiceImplementation.getInstance().fireEvent( (new Event(this, @@ -1669,6 +1669,11 @@ public SftpHandle openFileVersion5(String path, int flags, } try { + + try { + attrs = getAttributes(path); + } catch(SftpStatusException e) { } + UnsignedInteger32 requestId = nextRequestId(); Packet msg = createPacket(); msg.write(SSH_FXP_OPEN); @@ -1684,8 +1689,8 @@ public SftpHandle openFileVersion5(String path, int flags, sendMessage(msg); - SftpFile file = new SftpFile(path, getAttributes(path), this, null); - SftpHandle handle = file.handle(getHandleResponse(requestId, path)); + SftpFile file = new SftpFile(path, attrs, this, null); + SftpHandle handle = getHandle(requestId, file); EventServiceImplementation.getInstance().fireEvent( (new Event(this, EventCodes.EVENT_SFTP_FILE_OPENED, @@ -1733,7 +1738,7 @@ public SftpHandle openDirectory(String path) throws SftpStatusException, sendMessage(msg); - return new SftpFile(absolutePath, attrs, this, null).handle(getHandleResponse(requestId, path)); + return getHandle(requestId, new SftpFile(path, attrs, this, "")); } catch (SshIOException ex) { throw ex.getRealException(); } catch (IOException ex) { @@ -2110,7 +2115,7 @@ public void makeDirectory(String path, SftpFileAttributes attrs) SftpHandle getHandle(SftpMessage bar, SftpFile file, UnsignedInteger32 requestId) throws SftpStatusException, SshException { - var response = getHandleResponse(bar, file.getAbsolutePath(), requestId); + var response = getHandleResponse(bar, file, requestId); return new SftpHandle(response, this, file); } @@ -2141,28 +2146,29 @@ public SftpMessage getExtendedReply(SftpMessage bar, String path, UnsignedIntege } } - public byte[] getHandleResponse(UnsignedInteger32 requestId, String path) - throws SftpStatusException, SshException { - return getHandleResponse(getResponse(requestId), path, requestId); - } - +// public byte[] getHandleResponse(UnsignedInteger32 requestId, String path) +// throws SftpStatusException, SshException { +// return getHandleResponse(getResponse(requestId), path, requestId); +// } +// public SftpHandle getHandle(UnsignedInteger32 requestId, SftpFile file) throws SftpStatusException, SshException { - return new SftpHandle(getHandleResponse(getResponse(requestId), file.getAbsolutePath(), requestId), this, file); + return new SftpHandle(getHandleResponse(getResponse(requestId), file, requestId), this, file); } - public byte[] getHandleResponse(SftpMessage bar, String path, UnsignedInteger32 requestId) + public byte[] getHandleResponse(SftpMessage bar, SftpFile file, UnsignedInteger32 requestId) throws SftpStatusException, SshException { try { if (bar.getType() == SSH_FXP_HANDLE) { byte[] handle = bar.readBinaryString(); if(Log.isDebugEnabled()) { - Log.debug("Received SSH_FXP_HANDLE for {} handle={}", path, Base64.encodeBytes(handle, true)); + Log.debug("Received SSH_FXP_HANDLE for {} handle={}", file.getAbsolutePath(), Base64.encodeBytes(handle, true)); } + handles.put(handle, new SftpHandle(handle, this, file)); return handle; } else if (bar.getType() == SSH_FXP_STATUS) { - processStatusResponse(bar, path, requestId); + processStatusResponse(bar, file.getAbsolutePath(), requestId); throw new IllegalStateException("Received unexpected SSH_FX_OK in status response!"); } else { close(); diff --git a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpClient.java b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpClient.java index 9ec1ea6d..526c77d1 100644 --- a/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpClient.java +++ b/maverick-synergy-client/src/main/java/com/sshtools/client/sftp/SftpClient.java @@ -1041,9 +1041,9 @@ public SftpFile[] ls(String path, String filter, boolean regexFilter, int maximu private SftpHandle openDirectoryHandle(String path, ByteArrayWriter msg) throws SshException, SftpStatusException { SftpFile file = new SftpFile(path, sftp.getAttributes(path), sftp, null); try { - return file.handle(sftp.getHandleResponse( + return sftp.getHandle( sftp.sendExtensionMessage("open-directory-with-filter@sshtools.com", msg.toByteArray()), - path)); + file); } catch (SftpStatusException e) { if (Boolean.getBoolean("maverick.disableLocalFiltering")) { throw new SshException("Remote server does not support server side filtering", From cb700be60ef075a1bbf8b5559b0b44ee9821b3cd Mon Sep 17 00:00:00 2001 From: Lee Painter Date: Fri, 20 Sep 2024 15:06:46 +0100 Subject: [PATCH 50/51] Updated change log --- maverick-synergy-assembly/notes/CHANGES | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/maverick-synergy-assembly/notes/CHANGES b/maverick-synergy-assembly/notes/CHANGES index 60d3714f..0e9f75ec 100644 --- a/maverick-synergy-assembly/notes/CHANGES +++ b/maverick-synergy-assembly/notes/CHANGES @@ -1,4 +1,4 @@ -Maverick Synergy 3.1.2 - TBC +Maverick Synergy 3.1.2 - Sep 20, 2024 Bug Fixes o Added strict mode option to SignaturePolicy for clients to require declaration of signature support in SSH_MSG_EXT_INFO when provided. @@ -10,6 +10,8 @@ Bug Fixes o Fixes regression found in callback service where `osshell` would fail to send keyboard input. o Logging indicates that an IP address will be temporarily banned even if banning has been disabled. o ScpClient and SftpClient have differing strategies for the default sandbox value. By default sandboxing is now turned off for both SFTP and SCP clients. + o Client would hang if 'none' authentication succeeded. + o Ensure attributes for SftpHandle are requested after file has been opened to maintain messaging strict sequence. ================= = HISTORY = From 22683828fe2b0b4802487655186f011c951e7bdb Mon Sep 17 00:00:00 2001 From: Lee Painter Date: Fri, 20 Sep 2024 15:07:48 +0100 Subject: [PATCH 51/51] Bump version for release. --- maverick-base-tests/pom.xml | 2 +- maverick-base/pom.xml | 2 +- maverick-bc-fips-tests/pom.xml | 2 +- maverick-bc-fips/pom.xml | 2 +- maverick-bc-tests/pom.xml | 2 +- maverick-bc/pom.xml | 2 +- maverick-logging/pom.xml | 2 +- maverick-putty/pom.xml | 2 +- maverick-sshagent-jdk16-sockets/pom.xml | 2 +- maverick-sshagent-jni-sockets/pom.xml | 2 +- maverick-sshagent-named-pipes/pom.xml | 2 +- maverick-sshagent/pom.xml | 2 +- maverick-synergy-assembly/pom.xml | 2 +- maverick-synergy-callback-client/pom.xml | 2 +- maverick-synergy-callback-server/pom.xml | 2 +- maverick-synergy-client-tests/pom.xml | 2 +- maverick-synergy-client/pom.xml | 2 +- maverick-synergy-common-tests/pom.xml | 2 +- maverick-synergy-common/pom.xml | 2 +- maverick-synergy-jdk16-client/pom.xml | 2 +- maverick-synergy-jdk16-common/pom.xml | 2 +- maverick-synergy-jdk16-server/pom.xml | 2 +- maverick-synergy-s3/pom.xml | 4 ++-- maverick-synergy-server/pom.xml | 2 +- maverick-synergy/pom.xml | 2 +- maverick-utils/pom.xml | 2 +- maverick-virtual-filesystem-tests/pom.xml | 2 +- maverick-virtual-filesystem/pom.xml | 2 +- maverick-virtual-session-tests/pom.xml | 2 +- maverick-virtual-session/pom.xml | 2 +- maverick-x509/pom.xml | 2 +- pom.xml | 2 +- 32 files changed, 33 insertions(+), 33 deletions(-) diff --git a/maverick-base-tests/pom.xml b/maverick-base-tests/pom.xml index 3d245b3a..26cda7c7 100644 --- a/maverick-base-tests/pom.xml +++ b/maverick-base-tests/pom.xml @@ -3,7 +3,7 @@ com.sshtools maverick-synergy-group - 3.1.2-SNAPSHOT + 3.1.2 jar maverick-base-tests diff --git a/maverick-base/pom.xml b/maverick-base/pom.xml index 3ffd1553..e5c3888b 100644 --- a/maverick-base/pom.xml +++ b/maverick-base/pom.xml @@ -5,7 +5,7 @@ com.sshtools maverick-synergy-group - 3.1.2-SNAPSHOT + 3.1.2 maverick-base Base API diff --git a/maverick-bc-fips-tests/pom.xml b/maverick-bc-fips-tests/pom.xml index c59f8d5d..4a39c661 100644 --- a/maverick-bc-fips-tests/pom.xml +++ b/maverick-bc-fips-tests/pom.xml @@ -3,7 +3,7 @@ com.sshtools maverick-synergy-group - 3.1.2-SNAPSHOT + 3.1.2 maverick-bc-fips-tests BouncyCastle FIPS Tests diff --git a/maverick-bc-fips/pom.xml b/maverick-bc-fips/pom.xml index 87d9ed46..bff19f97 100644 --- a/maverick-bc-fips/pom.xml +++ b/maverick-bc-fips/pom.xml @@ -6,7 +6,7 @@ com.sshtools maverick-synergy-group - 3.1.2-SNAPSHOT + 3.1.2 maverick-bc-fips BouncyCastle FIPS diff --git a/maverick-bc-tests/pom.xml b/maverick-bc-tests/pom.xml index bba147f0..2e66a580 100644 --- a/maverick-bc-tests/pom.xml +++ b/maverick-bc-tests/pom.xml @@ -3,7 +3,7 @@ com.sshtools maverick-synergy-group - 3.1.2-SNAPSHOT + 3.1.2 maverick-bc-tests BouncyCastle JCE Support Tests diff --git a/maverick-bc/pom.xml b/maverick-bc/pom.xml index 358e350e..ae5c0d8f 100644 --- a/maverick-bc/pom.xml +++ b/maverick-bc/pom.xml @@ -6,7 +6,7 @@ com.sshtools maverick-synergy-group - 3.1.2-SNAPSHOT + 3.1.2 maverick-bc BouncyCastle JCE Support diff --git a/maverick-logging/pom.xml b/maverick-logging/pom.xml index 9f00a090..b08bfaad 100644 --- a/maverick-logging/pom.xml +++ b/maverick-logging/pom.xml @@ -5,7 +5,7 @@ com.sshtools maverick-synergy-group - 3.1.2-SNAPSHOT + 3.1.2 maverick-logging Logging API diff --git a/maverick-putty/pom.xml b/maverick-putty/pom.xml index e0f6ef04..5068f42f 100644 --- a/maverick-putty/pom.xml +++ b/maverick-putty/pom.xml @@ -6,7 +6,7 @@ com.sshtools maverick-synergy-group - 3.1.2-SNAPSHOT + 3.1.2 maverick-putty PuTTY Key Support diff --git a/maverick-sshagent-jdk16-sockets/pom.xml b/maverick-sshagent-jdk16-sockets/pom.xml index eaceab1b..03270441 100644 --- a/maverick-sshagent-jdk16-sockets/pom.xml +++ b/maverick-sshagent-jdk16-sockets/pom.xml @@ -3,7 +3,7 @@ com.sshtools maverick-synergy-group - 3.1.2-SNAPSHOT + 3.1.2 maverick-sshagent-jdk16-sockets JDK16+ Unix Domain Socket Agent Provider diff --git a/maverick-sshagent-jni-sockets/pom.xml b/maverick-sshagent-jni-sockets/pom.xml index ce7e6d8c..53c1b8df 100644 --- a/maverick-sshagent-jni-sockets/pom.xml +++ b/maverick-sshagent-jni-sockets/pom.xml @@ -4,7 +4,7 @@ com.sshtools maverick-synergy-group - 3.1.2-SNAPSHOT + 3.1.2 maverick-sshagent-jni-sockets JNI Unix Domain Sockets Key Agent Provider diff --git a/maverick-sshagent-named-pipes/pom.xml b/maverick-sshagent-named-pipes/pom.xml index 398441c5..30b56471 100644 --- a/maverick-sshagent-named-pipes/pom.xml +++ b/maverick-sshagent-named-pipes/pom.xml @@ -3,7 +3,7 @@ com.sshtools maverick-synergy-group - 3.1.2-SNAPSHOT + 3.1.2 maverick-sshagent-named-pipes Named Pipes Key Agent Provider diff --git a/maverick-sshagent/pom.xml b/maverick-sshagent/pom.xml index e50617bc..a9f2ebab 100644 --- a/maverick-sshagent/pom.xml +++ b/maverick-sshagent/pom.xml @@ -4,7 +4,7 @@ com.sshtools maverick-synergy-group - 3.1.2-SNAPSHOT + 3.1.2 maverick-sshagent Key Agent diff --git a/maverick-synergy-assembly/pom.xml b/maverick-synergy-assembly/pom.xml index e92a9e55..e70c3137 100644 --- a/maverick-synergy-assembly/pom.xml +++ b/maverick-synergy-assembly/pom.xml @@ -4,7 +4,7 @@ com.sshtools maverick-synergy-group - 3.1.2-SNAPSHOT + 3.1.2 maverick-synergy-assembly diff --git a/maverick-synergy-callback-client/pom.xml b/maverick-synergy-callback-client/pom.xml index 7cb9edda..cd013efe 100644 --- a/maverick-synergy-callback-client/pom.xml +++ b/maverick-synergy-callback-client/pom.xml @@ -4,7 +4,7 @@ com.sshtools maverick-synergy-group - 3.1.2-SNAPSHOT + 3.1.2 maverick-synergy-callback-client Callback Client API diff --git a/maverick-synergy-callback-server/pom.xml b/maverick-synergy-callback-server/pom.xml index 4c6383af..58cb34cb 100644 --- a/maverick-synergy-callback-server/pom.xml +++ b/maverick-synergy-callback-server/pom.xml @@ -4,7 +4,7 @@ com.sshtools maverick-synergy-group - 3.1.2-SNAPSHOT + 3.1.2 maverick-synergy-callback-server Callback Server API diff --git a/maverick-synergy-client-tests/pom.xml b/maverick-synergy-client-tests/pom.xml index d3f69cbf..b230342a 100644 --- a/maverick-synergy-client-tests/pom.xml +++ b/maverick-synergy-client-tests/pom.xml @@ -3,7 +3,7 @@ com.sshtools maverick-synergy-group - 3.1.2-SNAPSHOT + 3.1.2 maverick-synergy-client-tests Client API Tests diff --git a/maverick-synergy-client/pom.xml b/maverick-synergy-client/pom.xml index 46f00ce6..6a29176d 100644 --- a/maverick-synergy-client/pom.xml +++ b/maverick-synergy-client/pom.xml @@ -6,7 +6,7 @@ com.sshtools maverick-synergy-group - 3.1.2-SNAPSHOT + 3.1.2 maverick-synergy-client Client API diff --git a/maverick-synergy-common-tests/pom.xml b/maverick-synergy-common-tests/pom.xml index b12cef04..e5b2514b 100644 --- a/maverick-synergy-common-tests/pom.xml +++ b/maverick-synergy-common-tests/pom.xml @@ -3,7 +3,7 @@ com.sshtools maverick-synergy-group - 3.1.2-SNAPSHOT + 3.1.2 maverick-synergy-common-tests Common API Tests diff --git a/maverick-synergy-common/pom.xml b/maverick-synergy-common/pom.xml index 8dc69257..d9ddd1e3 100644 --- a/maverick-synergy-common/pom.xml +++ b/maverick-synergy-common/pom.xml @@ -5,7 +5,7 @@ com.sshtools maverick-synergy-group - 3.1.2-SNAPSHOT + 3.1.2 maverick-synergy-common Common API diff --git a/maverick-synergy-jdk16-client/pom.xml b/maverick-synergy-jdk16-client/pom.xml index 4c9af744..e64b377c 100644 --- a/maverick-synergy-jdk16-client/pom.xml +++ b/maverick-synergy-jdk16-client/pom.xml @@ -5,7 +5,7 @@ com.sshtools maverick-synergy-group - 3.1.2-SNAPSHOT + 3.1.2 maverick-synergy-jdk16-client Client implementation Unix Domain Socket forwarding diff --git a/maverick-synergy-jdk16-common/pom.xml b/maverick-synergy-jdk16-common/pom.xml index 5df38d2c..2c939df7 100644 --- a/maverick-synergy-jdk16-common/pom.xml +++ b/maverick-synergy-jdk16-common/pom.xml @@ -5,7 +5,7 @@ com.sshtools maverick-synergy-group - 3.1.2-SNAPSHOT + 3.1.2 maverick-synergy-jdk16-common Common code for Unix Domain Socket support diff --git a/maverick-synergy-jdk16-server/pom.xml b/maverick-synergy-jdk16-server/pom.xml index 3a77389e..b16474c3 100644 --- a/maverick-synergy-jdk16-server/pom.xml +++ b/maverick-synergy-jdk16-server/pom.xml @@ -5,7 +5,7 @@ com.sshtools maverick-synergy-group - 3.1.2-SNAPSHOT + 3.1.2 maverick-synergy-jdk16-server Server implementation Unix Domain Socket forwarding diff --git a/maverick-synergy-s3/pom.xml b/maverick-synergy-s3/pom.xml index fe458f9d..2c934e1f 100644 --- a/maverick-synergy-s3/pom.xml +++ b/maverick-synergy-s3/pom.xml @@ -3,7 +3,7 @@ com.sshtools maverick-synergy-group - 3.1.2-SNAPSHOT + 3.1.2 maverick-synergy-s3 S3 File System @@ -12,7 +12,7 @@ com.sshtools maverick-base - 3.1.2-SNAPSHOT + 3.1.2 diff --git a/maverick-synergy-server/pom.xml b/maverick-synergy-server/pom.xml index b7ad43f5..f1d94643 100644 --- a/maverick-synergy-server/pom.xml +++ b/maverick-synergy-server/pom.xml @@ -4,7 +4,7 @@ com.sshtools maverick-synergy-group - 3.1.2-SNAPSHOT + 3.1.2 maverick-synergy-server Server API diff --git a/maverick-synergy/pom.xml b/maverick-synergy/pom.xml index 4161b6be..74648a73 100644 --- a/maverick-synergy/pom.xml +++ b/maverick-synergy/pom.xml @@ -6,7 +6,7 @@ com.sshtools maverick-synergy-group - 3.1.2-SNAPSHOT + 3.1.2 maverick-synergy Unified API diff --git a/maverick-utils/pom.xml b/maverick-utils/pom.xml index d14ddc5c..84a3d92e 100644 --- a/maverick-utils/pom.xml +++ b/maverick-utils/pom.xml @@ -5,7 +5,7 @@ com.sshtools maverick-synergy-group - 3.1.2-SNAPSHOT + 3.1.2 maverick-utils Utils diff --git a/maverick-virtual-filesystem-tests/pom.xml b/maverick-virtual-filesystem-tests/pom.xml index 6330a269..0cb82935 100644 --- a/maverick-virtual-filesystem-tests/pom.xml +++ b/maverick-virtual-filesystem-tests/pom.xml @@ -3,7 +3,7 @@ com.sshtools maverick-synergy-group - 3.1.2-SNAPSHOT + 3.1.2 maverick-virtual-filesystem-tests Virtual File System Tests diff --git a/maverick-virtual-filesystem/pom.xml b/maverick-virtual-filesystem/pom.xml index 69f1a32f..21a8bfeb 100644 --- a/maverick-virtual-filesystem/pom.xml +++ b/maverick-virtual-filesystem/pom.xml @@ -4,7 +4,7 @@ com.sshtools maverick-synergy-group - 3.1.2-SNAPSHOT + 3.1.2 maverick-virtual-filesystem Virtual File System diff --git a/maverick-virtual-session-tests/pom.xml b/maverick-virtual-session-tests/pom.xml index 32d75c11..932c46a0 100644 --- a/maverick-virtual-session-tests/pom.xml +++ b/maverick-virtual-session-tests/pom.xml @@ -4,7 +4,7 @@ com.sshtools maverick-synergy-group - 3.1.2-SNAPSHOT + 3.1.2 maverick-virtual-session-tests Virtual Sessions Tests diff --git a/maverick-virtual-session/pom.xml b/maverick-virtual-session/pom.xml index aca84fef..b62a5e28 100644 --- a/maverick-virtual-session/pom.xml +++ b/maverick-virtual-session/pom.xml @@ -7,7 +7,7 @@ com.sshtools maverick-synergy-group - 3.1.2-SNAPSHOT + 3.1.2 maverick-virtual-session Virtual Sessions diff --git a/maverick-x509/pom.xml b/maverick-x509/pom.xml index 720b1728..37a1156f 100644 --- a/maverick-x509/pom.xml +++ b/maverick-x509/pom.xml @@ -5,7 +5,7 @@ com.sshtools maverick-synergy-group - 3.1.2-SNAPSHOT + 3.1.2 maverick-x509 X509 Certificate Support diff --git a/pom.xml b/pom.xml index 767a6fbd..c020b3a3 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 com.sshtools maverick-synergy-group - 3.1.2-SNAPSHOT + 3.1.2 Maverick Synergy Open source Java SSH API http://www.jadaptive.com