You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by lg...@apache.org on 2019/07/18 11:23:00 UTC

[mina-sshd] 03/05: [SSHD-930] Added SessionListener#sessionPeerIdentificationReceived callback

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 57ba26d7104db555b8efa0cf23fe59f873eba38a
Author: Lyor Goldstein <lg...@apache.org>
AuthorDate: Mon Jul 8 13:33:51 2019 +0300

    [SSHD-930] Added SessionListener#sessionPeerIdentificationReceived callback
---
 CHANGES.md                                         |  3 ++
 .../sshd/client/session/AbstractClientSession.java |  8 ++--
 .../sshd/common/session/SessionListener.java       | 14 +++++++
 .../sshd/common/session/helpers/SessionHelper.java | 32 ++++++++++++++++
 .../sshd/server/session/AbstractServerSession.java |  4 +-
 .../java/org/apache/sshd/server/ServerTest.java    | 43 +++++++++++++++++-----
 6 files changed, 90 insertions(+), 14 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index 2c7a43c..60f3b07 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -8,6 +8,9 @@
 
 ## Minor code helpers
 
+* `SessionListener` supports `sessionPeerIdentificationReceived` that is invoked once successful
+peer version data is received.
+
 ## Behavioral changes and enhancements
 
 * [SSHD-930](https://issues.apache.org/jira/browse/SSHD-930) - Added configuration allowing the user to specify whether client should wait
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/session/AbstractClientSession.java b/sshd-core/src/main/java/org/apache/sshd/client/session/AbstractClientSession.java
index 5f44b19..1d865aa 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/session/AbstractClientSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/session/AbstractClientSession.java
@@ -472,7 +472,9 @@ public abstract class AbstractClientSession extends AbstractSession implements C
         return true;
     }
 
-    protected void signalExtraServerVersionInfo(String version, List<String> lines) throws IOException {
+    protected void signalExtraServerVersionInfo(String version, List<String> lines) throws Exception {
+        signalPeerIdentificationReceived(version, lines);
+
         if (GenericUtils.isEmpty(lines)) {
             return;
         }
@@ -483,8 +485,8 @@ public abstract class AbstractClientSession extends AbstractSession implements C
                 ui.serverVersionInfo(this, lines);
             }
         } catch (Error e) {
-            log.warn("signalExtraServerVersionInfo({})[{}] failed ({}) to consult interaction: {}", this, version,
-                    e.getClass().getSimpleName(), e.getMessage());
+            log.warn("signalExtraServerVersionInfo({})[{}] failed ({}) to consult interaction: {}",
+                this, version, e.getClass().getSimpleName(), e.getMessage());
             if (log.isDebugEnabled()) {
                 log.debug("signalExtraServerVersionInfo(" + this + ")[" + version
                         + "] interaction consultation failure details", e);
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/SessionListener.java b/sshd-core/src/main/java/org/apache/sshd/common/session/SessionListener.java
index 544f89c..d322f9d 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/SessionListener.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/SessionListener.java
@@ -18,6 +18,7 @@
  */
 package org.apache.sshd.common.session;
 
+import java.util.List;
 import java.util.Map;
 
 import org.apache.sshd.common.kex.KexProposalOption;
@@ -43,6 +44,19 @@ public interface SessionListener extends SshdEventListener {
     }
 
     /**
+     * The peer's identification version was received
+     *
+     * @param session The {@link Session} instance
+     * @param version The retrieved identification version
+     * @param extraLines Extra data preceding the identification
+     * @see <A HREF="https://tools.ietf.org/html/rfc4253#section-4.2">RFC 4253 - section 4.2 - Protocol Version Exchange</A>
+     */
+    default void sessionPeerIdentificationReceived(
+            Session session, String version, List<String> extraLines) {
+        // ignored
+    }
+
+    /**
      * Signals the start of the negotiation options handling
      *
      * @param session The referenced {@link Session}
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/SessionHelper.java b/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/SessionHelper.java
index 914d5d6..f58ed6e 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/SessionHelper.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/SessionHelper.java
@@ -550,6 +550,38 @@ public abstract class SessionHelper extends AbstractKexFactoryManager implements
         listener.sessionCreated(this);
     }
 
+    protected void signalPeerIdentificationReceived(String version, List<String> extraLines) throws Exception {
+        try {
+            invokeSessionSignaller(l -> {
+                signalPeerIdentificationReceived(l, version, extraLines);
+                return null;
+            });
+        } catch (Throwable err) {
+            Throwable e = GenericUtils.peelException(err);
+            if (log.isDebugEnabled()) {
+                log.debug("signalPeerIdentificationReceived({}) Failed ({}) to announce peer={}: {}",
+                    this, e.getClass().getSimpleName(), version, e.getMessage());
+            }
+            if (log.isTraceEnabled()) {
+                log.trace("signalPeerIdentificationReceived(" + this + ")[" + version + "] failure details", e);
+            }
+            if (e instanceof Exception) {
+                throw (Exception) e;
+            } else {
+                throw new RuntimeSshException(e);
+            }
+        }
+
+    }
+
+    protected void signalPeerIdentificationReceived(SessionListener listener, String version, List<String> extraLines) {
+        if (listener == null) {
+            return;
+        }
+
+        listener.sessionPeerIdentificationReceived(this, version, extraLines);
+    }
+
     /**
      * Sends a session event to all currently registered session listeners
      *
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/session/AbstractServerSession.java b/sshd-core/src/main/java/org/apache/sshd/server/session/AbstractServerSession.java
index 529b5f8..1d5eceb 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/session/AbstractServerSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/session/AbstractServerSession.java
@@ -406,7 +406,7 @@ public abstract class AbstractServerSession extends AbstractSession implements S
     }
 
     @Override
-    protected boolean readIdentification(Buffer buffer) throws IOException, GeneralSecurityException {
+    protected boolean readIdentification(Buffer buffer) throws Exception {
         ServerProxyAcceptor acceptor = getServerProxyAcceptor();
         int rpos = buffer.rpos();
         boolean debugEnabled = log.isDebugEnabled();
@@ -467,6 +467,8 @@ public abstract class AbstractServerSession extends AbstractSession implements S
             throw err;
         }
 
+        signalPeerIdentificationReceived(clientVersion, ident);
+
         kexState.set(KexState.INIT);
         sendKexInit();
         return true;
diff --git a/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java b/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java
index ae3ce35..faebaf1 100644
--- a/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java
@@ -931,11 +931,7 @@ public class ServerTest extends BaseTestSupport {
         client.addSessionListener(listener);
         client.start();
 
-        try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, sshd.getPort())
-                .verify(7L, TimeUnit.SECONDS)
-                .getSession()) {
-            session.addPasswordIdentity(getCurrentTestName());
-            session.auth().verify(9L, TimeUnit.SECONDS);
+        try (ClientSession session = createTestClientSession(sshd)) {
             assertEquals("Mismatched client identification", expClientIdent, session.getClientVersion());
             assertEquals("Mismatched server identification", expServerIdent, session.getServerVersion());
         } finally {
@@ -980,11 +976,7 @@ public class ServerTest extends BaseTestSupport {
         });
         client.start();
 
-        try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, sshd.getPort())
-                .verify(7L, TimeUnit.SECONDS)
-                .getSession()) {
-            session.addPasswordIdentity(getCurrentTestName());
-            session.auth().verify(9L, TimeUnit.SECONDS);
+        try (ClientSession session = createTestClientSession(sshd)) {
             assertTrue("No signal received in time", signal.tryAcquire(11L, TimeUnit.SECONDS));
         } finally {
             client.stop();
@@ -995,6 +987,37 @@ public class ServerTest extends BaseTestSupport {
         assertListEquals("Server information", expected, actual);
     }
 
+    @Test   // see SSHD-930
+    public void testDelayClientIdentification() throws Exception {
+        sshd.start();
+
+        PropertyResolverUtils.updateProperty(
+            client, ClientFactoryManager.SEND_IMMEDIATE_IDENTIFICATION, false);
+        AtomicReference<String> peerVersion = new AtomicReference<>();
+        client.addSessionListener(new SessionListener() {
+            @Override
+            public void sessionPeerIdentificationReceived(Session session, String version, List<String> extraLines) {
+                String clientVersion = session.getClientVersion();
+                if (GenericUtils.isNotEmpty(clientVersion)) {
+                    throw new IllegalStateException("Client version already established");
+                }
+
+                String prev = peerVersion.getAndSet(version);
+                if (GenericUtils.isNotEmpty(prev)) {
+                    throw new IllegalStateException("Peer version already signalled: " + prev);
+                }
+            }
+        });
+        client.start();
+
+        try (ClientSession session = createTestClientSession(sshd)) {
+            String version = peerVersion.getAndSet(null);
+            assertTrue("Peer version not signalled", GenericUtils.isNotEmpty(version));
+        } finally {
+            client.stop();
+        }
+    }
+
     private ClientSession createTestClientSession(SshServer server) throws Exception {
         ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, server.getPort())
                 .verify(7L, TimeUnit.SECONDS)