This is an automated email from the ASF dual-hosted git repository.

lgoldstein pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git

commit a36319a68eca9243e30728e968b9f7a08d077ada
Author: Lyor Goldstein <[email protected]>
AuthorDate: Wed Feb 13 14:16:25 2019 +0200

    [SSHD-896] Expose KEX related information via SessionContext
---
 CHANGES.md                                         |  3 ++
 .../apache/sshd/cli/client/SftpCommandMain.java    | 33 +++++++++++++++++++++-
 .../apache/sshd/common/kex/KexProposalOption.java  |  0
 .../java/org/apache/sshd/common/kex/KexState.java  |  0
 .../apache/sshd/common/session/SessionContext.java | 33 ++++++++++++++++++++++
 .../org/apache/sshd/common/session/Session.java    | 11 --------
 .../common/session/helpers/AbstractSession.java    | 28 ++++++++++++++++--
 7 files changed, 94 insertions(+), 14 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index 0c43e89..3c76f48 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -13,6 +13,9 @@ where the code decides to disconnect the session due to 
various protocol or conf
 * `ScpFileOpener#getMatchingFilesToSend` has been modified to accept a `Path` 
as the base directory
 and also return an `Iterable<Path>`.
 
+* The SFTP command line client provides a `kex` command that displays the KEX 
parameters of the
+current sesssion - client/server proposals and what has been negotiated.
+
 ## Behavioral changes and enhancements
 
 * [SSHD-882](https://issues.apache.org/jira/browse/SSHD-882) - Provide hooks 
to allow users to register a consumer
diff --git 
a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SftpCommandMain.java 
b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SftpCommandMain.java
index 922601d..26d471d 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SftpCommandMain.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SftpCommandMain.java
@@ -103,6 +103,7 @@ public class SftpCommandMain extends SshClientCliSupport 
implements Channel {
                 new PwdCommandExecutor(),
                 new InfoCommandExecutor(),
                 new SessionCommandExecutor(),
+                new KexCommandExecutor(),
                 new ClientCommandExecutor(),
                 new VersionCommandExecutor(),
                 new CdCommandExecutor(),
@@ -418,8 +419,38 @@ public class SftpCommandMain extends SshClientCliSupport 
implements Channel {
             appendInfoValue(stdout, "Local address", 
ioSession.getLocalAddress()).println();
             appendInfoValue(stdout, "Remote address", 
ioSession.getRemoteAddress()).println();
 
+            appendInfoValue(stdout, "Client version", 
session.getClientVersion()).println();
+            appendInfoValue(stdout, "Server version", 
session.getServerVersion()).println();
+            return false;
+        }
+    }
+
+    private class KexCommandExecutor implements SftpCommandExecutor {
+        KexCommandExecutor() {
+            super();
+        }
+
+        @Override
+        public String getName() {
+            return "kex";
+        }
+
+        @Override
+        public boolean executeCommand(
+                String args, BufferedReader stdin, PrintStream stdout, 
PrintStream stderr)
+                    throws Exception {
+            ValidateUtils.checkTrue(GenericUtils.isEmpty(args), "Unexpected 
arguments: %s", args);
+            SftpClient sftp = getClient();
+            ClientSession session = sftp.getSession();
+
+            Map<KexProposalOption, String> clientProposals = 
session.getClientKexProposals();
+            Map<KexProposalOption, String> serverProposals = 
session.getServerKexProposals();
+            Map<KexProposalOption, String> negotiated = 
session.getKexNegotiationResult();
             for (KexProposalOption option : KexProposalOption.VALUES) {
-                appendInfoValue(stdout, option.getDescription(), 
session.getNegotiatedKexParameter(option)).println();
+                String description = option.getDescription();
+                appendInfoValue(stdout, description + "[client]", 
clientProposals.get(option)).println();
+                appendInfoValue(stdout, description + "[server]", 
serverProposals.get(option)).println();
+                appendInfoValue(stdout, description + "[negotiated]", 
negotiated.get(option)).println();
             }
 
             return false;
diff --git 
a/sshd-core/src/main/java/org/apache/sshd/common/kex/KexProposalOption.java 
b/sshd-common/src/main/java/org/apache/sshd/common/kex/KexProposalOption.java
similarity index 100%
rename from 
sshd-core/src/main/java/org/apache/sshd/common/kex/KexProposalOption.java
rename to 
sshd-common/src/main/java/org/apache/sshd/common/kex/KexProposalOption.java
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/kex/KexState.java 
b/sshd-common/src/main/java/org/apache/sshd/common/kex/KexState.java
similarity index 100%
rename from sshd-core/src/main/java/org/apache/sshd/common/kex/KexState.java
rename to sshd-common/src/main/java/org/apache/sshd/common/kex/KexState.java
diff --git 
a/sshd-common/src/main/java/org/apache/sshd/common/session/SessionContext.java 
b/sshd-common/src/main/java/org/apache/sshd/common/session/SessionContext.java
index c937079..d19dd9c 100644
--- 
a/sshd-common/src/main/java/org/apache/sshd/common/session/SessionContext.java
+++ 
b/sshd-common/src/main/java/org/apache/sshd/common/session/SessionContext.java
@@ -19,9 +19,13 @@
 
 package org.apache.sshd.common.session;
 
+import java.util.Map;
+
 import org.apache.sshd.common.AttributeStore;
 import org.apache.sshd.common.PropertyResolver;
 import org.apache.sshd.common.auth.UsernameHolder;
+import org.apache.sshd.common.kex.KexProposalOption;
+import org.apache.sshd.common.kex.KexState;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.net.ConnectionEndpointsIndicator;
 
@@ -73,6 +77,13 @@ public interface SessionContext
     String getClientVersion();
 
     /**
+     * @return An <U>un-modifiable</U> map of the latest KEX client proposal 
options
+     * May be empty if KEX not yet completed or re-keying in progress
+     * @see #getKexState()
+     */
+    Map<KexProposalOption, String> getClientKexProposals();
+
+    /**
      * Retrieve the server version for this session.
      *
      * @return the server version - may be {@code null}/empty if versions not 
yet exchanged
@@ -80,6 +91,28 @@ public interface SessionContext
     String getServerVersion();
 
     /**
+     * @return An <U>un-modifiable</U> map of the latest KEX client proposal 
options.
+     * May be empty if KEX not yet completed or re-keying in progress
+     * @see #getKexState()
+     */
+    Map<KexProposalOption, String> getServerKexProposals();
+
+    KexState getKexState();
+
+    Map<KexProposalOption, String> getKexNegotiationResult();
+
+    /**
+     * Retrieve one of the negotiated values during the KEX stage
+     *
+     * @param paramType The request {@link KexProposalOption} value
+     * - ignored if {@code null}
+     * @return The negotiated parameter value - {@code null} if invalid
+     * parameter or no negotiated value.
+     * @see #getKexState()
+     */
+    String getNegotiatedKexParameter(KexProposalOption paramType);
+
+    /**
      * @return {@code true} if session has successfully completed the 
authentication phase
      */
     boolean isAuthenticated();
diff --git 
a/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java 
b/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java
index 3c7f26e..7943e6a 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java
@@ -40,7 +40,6 @@ import org.apache.sshd.common.io.IoSession;
 import org.apache.sshd.common.io.IoWriteFuture;
 import org.apache.sshd.common.io.PacketWriter;
 import org.apache.sshd.common.kex.KexFactoryManager;
-import org.apache.sshd.common.kex.KexProposalOption;
 import org.apache.sshd.common.kex.KeyExchange;
 import org.apache.sshd.common.mac.MacInformation;
 import org.apache.sshd.common.util.buffer.Buffer;
@@ -77,16 +76,6 @@ public interface Session
     }
 
     /**
-     * Retrieve one of the negotiated values during the KEX stage
-     *
-     * @param paramType The request {@link KexProposalOption} value
-     * - ignored if {@code null}
-     * @return The negotiated parameter value - {@code null} if invalid
-     * parameter or no negotiated value
-     */
-    String getNegotiatedKexParameter(KexProposalOption paramType);
-
-    /**
      * Retrieves current cipher information - <B>Note:</B> may change if
      * key re-exchange executed
      *
diff --git 
a/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java
 
b/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java
index 8e1c947..ef345fe 100644
--- 
a/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java
+++ 
b/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java
@@ -132,8 +132,12 @@ public abstract class AbstractSession extends 
SessionHelper {
     protected String clientVersion;
     // if empty then means not-initialized
     protected final Map<KexProposalOption, String> serverProposal = new 
EnumMap<>(KexProposalOption.class);
+    protected final Map<KexProposalOption, String> unmodServerProposal = 
Collections.unmodifiableMap(serverProposal);
     protected final Map<KexProposalOption, String> clientProposal = new 
EnumMap<>(KexProposalOption.class);
+    protected final Map<KexProposalOption, String> unmodClientProposal = 
Collections.unmodifiableMap(clientProposal);
     protected final Map<KexProposalOption, String> negotiationResult = new 
EnumMap<>(KexProposalOption.class);
+    protected final Map<KexProposalOption, String> unmodNegotiationResult = 
Collections.unmodifiableMap(negotiationResult);
+
     protected KeyExchange kex;
     protected Boolean firstKexPacketFollows;
     protected final AtomicReference<KexState> kexState = new 
AtomicReference<>(KexState.UNKNOWN);
@@ -226,22 +230,42 @@ public abstract class AbstractSession extends 
SessionHelper {
     }
 
     @Override
+    public Map<KexProposalOption, String> getServerKexProposals() {
+        return unmodServerProposal;
+    }
+
+    @Override
     public String getClientVersion() {
         return clientVersion;
     }
 
     @Override
+    public Map<KexProposalOption, String> getClientKexProposals() {
+        return unmodClientProposal;
+    }
+
+    @Override
     public KeyExchange getKex() {
         return kex;
     }
 
     @Override
+    public KexState getKexState() {
+        return kexState.get();
+    }
+
+    @Override
     public byte[] getSessionId() {
         // return a clone to avoid anyone changing the internal value
         return NumberUtils.isEmpty(sessionId) ? sessionId : sessionId.clone();
     }
 
     @Override
+    public Map<KexProposalOption, String> getKexNegotiationResult() {
+        return unmodNegotiationResult;
+    }
+
+    @Override
     public String getNegotiatedKexParameter(KexProposalOption paramType) {
         if (paramType == null) {
             return null;
@@ -1395,8 +1419,8 @@ public abstract class AbstractSession extends 
SessionHelper {
      * @return The negotiated options {@link Map}
      */
     protected Map<KexProposalOption, String> negotiate() {
-        Map<KexProposalOption, String> c2sOptions = 
Collections.unmodifiableMap(clientProposal);
-        Map<KexProposalOption, String> s2cOptions = 
Collections.unmodifiableMap(serverProposal);
+        Map<KexProposalOption, String> c2sOptions = getClientKexProposals();
+        Map<KexProposalOption, String> s2cOptions = getServerKexProposals();
         signalNegotiationStart(c2sOptions, s2cOptions);
 
         Map<KexProposalOption, String> guess = new 
EnumMap<>(KexProposalOption.class);

Reply via email to