You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by gn...@apache.org on 2013/12/03 22:03:00 UTC

[1/4] git commit: Minor fixes

Updated Branches:
  refs/heads/master 74946f1b6 -> e22356a77


Minor fixes

Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/20888f7a
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/20888f7a
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/20888f7a

Branch: refs/heads/master
Commit: 20888f7a7329dc09a4ac75c17004c30bfd03e5de
Parents: 74946f1
Author: Guillaume Nodet <gn...@apache.org>
Authored: Tue Dec 3 20:42:41 2013 +0100
Committer: Guillaume Nodet <gn...@apache.org>
Committed: Tue Dec 3 20:42:41 2013 +0100

----------------------------------------------------------------------
 .../java/org/apache/sshd/client/session/ClientSessionImpl.java  | 4 ++--
 .../java/org/apache/sshd/common/session/AbstractSession.java    | 5 ++---
 2 files changed, 4 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/20888f7a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
index 9d1e650..73fbb18 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
@@ -323,7 +323,7 @@ public class ClientSessionImpl extends AbstractSession implements ClientSession
                 switch (getState()) {
                     case ReceiveKexInit:
                         if (cmd != SshConstants.Message.SSH_MSG_KEXINIT) {
-                            log.error("Ignoring command " + cmd + " while waiting for " + SshConstants.Message.SSH_MSG_KEXINIT);
+                            log.error("Ignoring command {} while waiting for {}", cmd, SshConstants.Message.SSH_MSG_KEXINIT);
                             break;
                         }
                         log.info("Received SSH_MSG_KEXINIT");
@@ -370,7 +370,7 @@ public class ClientSessionImpl extends AbstractSession implements ClientSession
                         if (cmd == SshConstants.Message.SSH_MSG_USERAUTH_BANNER) {
                             String welcome = buffer.getString();
                             String lang = buffer.getString();
-                            log.debug("Welcome banner: " + welcome);
+                            log.debug("Welcome banner: {}", welcome);
                             UserInteraction ui = getClientFactoryManager().getUserInteraction();
                             if (ui != null) {
                                 ui.welcome(welcome);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/20888f7a/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java b/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
index f5e9cdf..f6784d3 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
@@ -534,7 +534,7 @@ public abstract class AbstractSession implements Session {
                     // need more data
                     break;
                 }
-            // We have received the beinning of the packet
+            // We have received the beginning of the packet
             } else if (decoderState == 1) {
                 // The read position should always be 4 at this point
                 assert decoderBuffer.rpos() == 4;
@@ -556,8 +556,7 @@ public abstract class AbstractSession implements Session {
                         inMac.doFinal(inMacResult, 0);
                         // Check the computed result with the received mac (just after the packet data)
                         if (!BufferUtils.equals(inMacResult, 0, data, decoderLength + 4, macSize)) {
-                            throw new SshException(SshConstants.SSH2_DISCONNECT_MAC_ERROR,
-                                                   "MAC Error");
+                            throw new SshException(SshConstants.SSH2_DISCONNECT_MAC_ERROR, "MAC Error");
                         }
                     }
                     // Increment incoming packet sequence number


[3/4] git commit: SSHD-254 Suppress 'Authentication with partial success' message on standard authentication

Posted by gn...@apache.org.
SSHD-254 Suppress 'Authentication with partial success' message on standard authentication

Version 0.9.0 incorrectly sends back a 'partial success true' flag when a client requests
authentication using the "none" method.


Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/28a8ae25
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/28a8ae25
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/28a8ae25

Branch: refs/heads/master
Commit: 28a8ae258b08c6b41ab64ac25f2331168dc0415a
Parents: 85d11f9
Author: Michael Heemskerk <mh...@atlassian.com>
Authored: Fri Sep 6 10:31:19 2013 +0200
Committer: Guillaume Nodet <gn...@apache.org>
Committed: Tue Dec 3 21:32:11 2013 +0100

----------------------------------------------------------------------
 .../main/java/org/apache/sshd/server/session/ServerSession.java    | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/28a8ae25/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSession.java b/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSession.java
index f3d8065..89958f5 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSession.java
@@ -499,7 +499,7 @@ public class ServerSession extends AbstractSession {
                     }
                 }
                 buffer.putString(sb.toString());
-                buffer.putByte((byte) 1);
+                buffer.putByte((byte) 0);
                 writePacket(buffer);
 
                 if (currentAuth != null) {


[4/4] git commit: [SSHD-256] Consistent handling of SSH_MSG_CHANNEL_REQUEST messages

Posted by gn...@apache.org.
[SSHD-256] Consistent handling of SSH_MSG_CHANNEL_REQUEST messages

Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/e22356a7
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/e22356a7
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/e22356a7

Branch: refs/heads/master
Commit: e22356a7761a2c7d406c18c4c68d83e1712a82de
Parents: 28a8ae2
Author: Guillaume Nodet <gn...@apache.org>
Authored: Tue Dec 3 22:02:28 2013 +0100
Committer: Guillaume Nodet <gn...@apache.org>
Committed: Tue Dec 3 22:02:28 2013 +0100

----------------------------------------------------------------------
 .../agent/local/ChannelAgentForwarding.java     |   7 +-
 .../sshd/agent/unix/ChannelAgentForwarding.java |   7 +-
 .../client/channel/AbstractClientChannel.java   |   8 +-
 .../java/org/apache/sshd/common/Channel.java    |   4 +-
 .../sshd/common/channel/AbstractChannel.java    |   2 +-
 .../sshd/common/forward/TcpipServerChannel.java |   7 +-
 .../sshd/common/session/AbstractSession.java    |  11 +-
 .../sshd/server/channel/ChannelSession.java     | 114 +------------------
 8 files changed, 30 insertions(+), 130 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e22356a7/sshd-core/src/main/java/org/apache/sshd/agent/local/ChannelAgentForwarding.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/agent/local/ChannelAgentForwarding.java b/sshd-core/src/main/java/org/apache/sshd/agent/local/ChannelAgentForwarding.java
index 002cb65..4903315 100644
--- a/sshd-core/src/main/java/org/apache/sshd/agent/local/ChannelAgentForwarding.java
+++ b/sshd-core/src/main/java/org/apache/sshd/agent/local/ChannelAgentForwarding.java
@@ -111,13 +111,10 @@ public class ChannelAgentForwarding extends AbstractServerChannel {
         throw new UnsupportedOperationException("AgentForward channel does not support extended data");
     }
 
-    public void handleRequest(Buffer buffer) throws IOException {
+    public boolean handleRequest(String type, Buffer buffer) throws IOException {
         log.info("Received SSH_MSG_CHANNEL_REQUEST on channel {}", id);
-        String type = buffer.getString();
         log.info("Received channel request: {}", type);
-        buffer = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_FAILURE, 0);
-        buffer.putInt(recipient);
-        writePacket(buffer);
+        return false;
     }
 
     protected class AgentClient extends AbstractAgentClient {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e22356a7/sshd-core/src/main/java/org/apache/sshd/agent/unix/ChannelAgentForwarding.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/agent/unix/ChannelAgentForwarding.java b/sshd-core/src/main/java/org/apache/sshd/agent/unix/ChannelAgentForwarding.java
index 0acbe04..84f32f4 100644
--- a/sshd-core/src/main/java/org/apache/sshd/agent/unix/ChannelAgentForwarding.java
+++ b/sshd-core/src/main/java/org/apache/sshd/agent/unix/ChannelAgentForwarding.java
@@ -137,13 +137,10 @@ public class ChannelAgentForwarding extends AbstractServerChannel {
         throw new UnsupportedOperationException("AgentForward channel does not support extended data");
     }
 
-    public void handleRequest(Buffer buffer) throws IOException {
+    public boolean handleRequest(String type, Buffer buffer) throws IOException {
         log.info("Received SSH_MSG_CHANNEL_REQUEST on channel {}", id);
-        String type = buffer.getString();
         log.info("Received channel request: {}", type);
-        buffer = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_FAILURE, 0);
-        buffer.putInt(recipient);
-        writePacket(buffer);
+        return false;
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e22356a7/sshd-core/src/main/java/org/apache/sshd/client/channel/AbstractClientChannel.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/channel/AbstractClientChannel.java b/sshd-core/src/main/java/org/apache/sshd/client/channel/AbstractClientChannel.java
index 6a332d4..5108cf3 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/channel/AbstractClientChannel.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/channel/AbstractClientChannel.java
@@ -250,19 +250,19 @@ public abstract class AbstractClientChannel extends AbstractChannel implements C
         localWindow.consumeAndCheck(len);
     }
 
-    public void handleRequest(Buffer buffer) throws IOException {
+    public boolean handleRequest(String req, Buffer buffer) throws IOException {
         log.info("Received SSH_MSG_CHANNEL_REQUEST on channel {}", id);
-        String req = buffer.getString();
         if ("exit-status".equals(req)) {
-            buffer.getBoolean();
             exitStatus = buffer.getInt();
             notifyStateChanged();
+            return true;
         } else if ("exit-signal".equals(req)) {
-            buffer.getBoolean();
             exitSignal = buffer.getString();
             notifyStateChanged();
+            return true;
         }
         // TODO: handle other channel requests
+        return false;
     }
 
     public Integer getExitStatus() {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e22356a7/sshd-core/src/main/java/org/apache/sshd/common/Channel.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/Channel.java b/sshd-core/src/main/java/org/apache/sshd/common/Channel.java
index 6883e8c..6e2c030 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/Channel.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/Channel.java
@@ -34,6 +34,8 @@ public interface Channel {
 
     int getId();
 
+    int getRecipient();
+
     Window getLocalWindow();
 
     Window getRemoteWindow();
@@ -44,7 +46,7 @@ public interface Channel {
 
     void handleWindowAdjust(Buffer buffer) throws IOException;
 
-    void handleRequest(Buffer buffer) throws IOException;
+    boolean handleRequest(String type, Buffer buffer) throws IOException;
 
     void handleData(Buffer buffer) throws IOException;
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e22356a7/sshd-core/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java b/sshd-core/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java
index c9ce78e..95ca72f 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java
@@ -76,7 +76,7 @@ public abstract class AbstractChannel implements Channel {
         return session;
     }
 
-    public void handleRequest(Buffer buffer) throws IOException {
+    public boolean handleRequest(String type, Buffer buffer) throws IOException {
         throw new IllegalStateException();
     }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e22356a7/sshd-core/src/main/java/org/apache/sshd/common/forward/TcpipServerChannel.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/forward/TcpipServerChannel.java b/sshd-core/src/main/java/org/apache/sshd/common/forward/TcpipServerChannel.java
index aa7b91a..6ba0e7d 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/forward/TcpipServerChannel.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/forward/TcpipServerChannel.java
@@ -190,12 +190,9 @@ public class TcpipServerChannel extends AbstractServerChannel {
         throw new UnsupportedOperationException(type + "Tcpip channel does not support extended data");
     }
 
-    public void handleRequest(Buffer buffer) throws IOException {
+    public boolean handleRequest(String type, Buffer buffer) throws IOException {
         log.info("Received SSH_MSG_CHANNEL_REQUEST on channel {}", id);
-        String type = buffer.getString();
         log.info("Received channel request: {}", type);
-        buffer = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_FAILURE, 0);
-        buffer.putInt(recipient);
-        writePacket(buffer);
+        return false;
     }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e22356a7/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java b/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
index 7b78ea8..4148e65 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
@@ -1079,7 +1079,16 @@ public abstract class AbstractSession implements Session {
      */
     protected void channelRequest(Buffer buffer) throws IOException {
         Channel channel = getChannel(buffer);
-        channel.handleRequest(buffer);
+        String type = buffer.getString();
+        boolean wantReply = buffer.getBoolean();
+        boolean success = channel.handleRequest(type, buffer);
+        if (wantReply) {
+            Buffer replyBuffer = createBuffer(
+                    success ? SshConstants.Message.SSH_MSG_CHANNEL_SUCCESS
+                            : SshConstants.Message.SSH_MSG_CHANNEL_FAILURE, 0);
+            replyBuffer.putInt(channel.getRecipient());
+            writePacket(replyBuffer);
+        }
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e22356a7/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java b/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
index a2a1128..11ca72b 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
@@ -231,17 +231,6 @@ public class ChannelSession extends AbstractServerChannel {
         IoUtils.closeQuietly(receiver);
     }
 
-    public void handleRequest(Buffer buffer) throws IOException {
-        log.debug("Received SSH_MSG_CHANNEL_REQUEST on channel {}", id);
-        String type = buffer.getString();
-        log.debug("Received channel request: {}", type);
-        if (!handleRequest(type, buffer)) {
-            buffer = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_FAILURE, 0);
-            buffer.putInt(recipient);
-            writePacket(buffer);
-        }
-    }
-
     protected void doWriteData(byte[] data, int off, int len) throws IOException {
         // If we're already closing, ignore incoming data
         if (closing.get()) {
@@ -264,7 +253,9 @@ public class ChannelSession extends AbstractServerChannel {
         throw new UnsupportedOperationException("Server channel does not support extended data");
     }
 
-    protected boolean handleRequest(String type, Buffer buffer) throws IOException {
+    public boolean handleRequest(String type, Buffer buffer) throws IOException {
+        log.debug("Received SSH_MSG_CHANNEL_REQUEST on channel {}", id);
+        log.debug("Received channel request: {}", type);
         if ("env".equals(type)) {
             return handleEnv(buffer);
         }
@@ -310,36 +301,18 @@ public class ChannelSession extends AbstractServerChannel {
         if ("x11-req".equals(type)) {
             return handleX11Forwarding(buffer);
         }
-        if (type != null && type.endsWith("@putty.projects.tartarus.org")) {
-            // Ignore but accept, more doc at
-            // http://tartarus.org/~simon/putty-snapshots/htmldoc/AppendixF.html
-            boolean wantReply = buffer.getBoolean();
-            if (wantReply) {
-                buffer = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_FAILURE, 0);
-                buffer.putInt(recipient);
-                writePacket(buffer);
-            }
-            return true;
-        }
         return false;
     }
 
     protected boolean handleEnv(Buffer buffer) throws IOException {
-        boolean wantReply = buffer.getBoolean();
         String name = buffer.getString();
         String value = buffer.getString();
         addEnvVariable(name, value);
         log.debug("env for channel {}: {} = {}", new Object[] { id, name, value });
-        if (wantReply) {
-            buffer = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_SUCCESS, 0);
-            buffer.putInt(recipient);
-            writePacket(buffer);
-        }
         return true;
     }
 
     protected boolean handlePtyReq(Buffer buffer) throws IOException {
-        boolean wantReply = buffer.getBoolean();
         String term = buffer.getString();
         int tColumns = buffer.getInt();
         int tRows = buffer.getInt();
@@ -360,16 +333,10 @@ public class ChannelSession extends AbstractServerChannel {
         addEnvVariable(Environment.ENV_TERM, term);
         addEnvVariable(Environment.ENV_COLUMNS, Integer.toString(tColumns));
         addEnvVariable(Environment.ENV_LINES, Integer.toString(tRows));
-        if (wantReply) {
-            buffer = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_SUCCESS, 0);
-            buffer.putInt(recipient);
-            writePacket(buffer);
-        }
         return true;
     }
 
     protected boolean handleWindowChange(Buffer buffer) throws IOException {
-        boolean wantReply = buffer.getBoolean();
         int tColumns = buffer.getInt();
         int tRows = buffer.getInt();
         int tWidth = buffer.getInt();
@@ -380,17 +347,10 @@ public class ChannelSession extends AbstractServerChannel {
         e.set(Environment.ENV_COLUMNS, Integer.toString(tColumns));
         e.set(Environment.ENV_LINES, Integer.toString(tRows));
         e.signal(Signal.WINCH);
-
-        if (wantReply) {
-            buffer = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_SUCCESS, 0);
-            buffer.putInt(recipient);
-            writePacket(buffer);
-        }
         return true;
     }
 
     protected boolean handleSignal(Buffer buffer) throws IOException {
-        boolean wantReply = buffer.getBoolean();
         String name = buffer.getString();
         log.debug("Signal received on channel {}: {}", id, name);
 
@@ -400,48 +360,28 @@ public class ChannelSession extends AbstractServerChannel {
         } else {
             log.warn("Unknown signal received: " + name);
         }
-
-        if (wantReply) {
-            buffer = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_SUCCESS, 0);
-            buffer.putInt(recipient);
-            writePacket(buffer);
-        }
         return true;
     }
 
     protected boolean handleBreak(Buffer buffer) throws IOException {
-        boolean wantReply = buffer.getBoolean();
         String name = buffer.getString();
         log.debug("Break received on channel {}: {}", id, name);
 
         getEnvironment().signal(Signal.INT);
-
-        if (wantReply) {
-            buffer = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_SUCCESS, 0);
-            buffer.putInt(recipient);
-            writePacket(buffer);
-        }
         return true;
     }
 
     protected boolean handleShell(Buffer buffer) throws IOException {
-        boolean wantReply = buffer.getBoolean();
         if (((ServerSession) session).getServerFactoryManager().getShellFactory() == null) {
             return false;
         }
         command = ((ServerSession) session).getServerFactoryManager().getShellFactory().create();
         prepareCommand();
-        if (wantReply) {
-            buffer = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_SUCCESS, 0);
-            buffer.putInt(recipient);
-            writePacket(buffer);
-        }
         command.start(getEnvironment());
         return true;
     }
 
     protected boolean handleExec(Buffer buffer) throws IOException {
-        boolean wantReply = buffer.getBoolean();
         String commandLine = buffer.getString();
         if (((ServerSession) session).getServerFactoryManager().getCommandFactory() == null) {
             log.warn("Unsupported command: {}", commandLine);
@@ -457,18 +397,12 @@ public class ChannelSession extends AbstractServerChannel {
             return false;
         }
         prepareCommand();
-        if (wantReply) {
-            buffer = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_SUCCESS, 0);
-            buffer.putInt(recipient);
-            writePacket(buffer);
-        }
         // Launch command
         command.start(getEnvironment());
         return true;
     }
 
     protected boolean handleSubsystem(Buffer buffer) throws IOException {
-        boolean wantReply = buffer.getBoolean();
         String subsystem = buffer.getString();
         List<NamedFactory<Command>> factories = ((ServerSession) session).getServerFactoryManager().getSubsystemFactories();
         if (factories == null) {
@@ -481,11 +415,6 @@ public class ChannelSession extends AbstractServerChannel {
             return false;
         }
         prepareCommand();
-        if (wantReply) {
-            buffer = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_SUCCESS, 0);
-            buffer.putInt(recipient);
-            writePacket(buffer);
-        }
         // Launch command
         command.start(getEnvironment());
         return true;
@@ -558,63 +487,32 @@ public class ChannelSession extends AbstractServerChannel {
     }
 
     protected boolean handleAgentForwarding(Buffer buffer) throws IOException {
-        boolean wantReply = buffer.getBoolean();
-
         final ServerSession server = (ServerSession) session;
         final ForwardingFilter filter = server.getServerFactoryManager().getTcpipForwardingFilter();
         final SshAgentFactory factory = server.getServerFactoryManager().getAgentFactory();
         if (factory == null || (filter != null && !filter.canForwardAgent(server))) {
-            if (wantReply) {
-                buffer = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_FAILURE, 0);
-                buffer.putInt(recipient);
-                writePacket(buffer);
-            }
-            return true;
+            return false;
         }
 
         String authSocket = ((ServerSession) session).initAgentForward();
         addEnvVariable(SshAgent.SSH_AUTHSOCKET_ENV_NAME, authSocket);
-
-        if (wantReply) {
-            buffer = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_SUCCESS, 0);
-            buffer.putInt(recipient);
-            writePacket(buffer);
-        }
         return true;
     }
 
     protected boolean handleX11Forwarding(Buffer buffer) throws IOException {
-        boolean wantReply = buffer.getBoolean();
-
         final ServerSession server = (ServerSession) session;
         final ForwardingFilter filter = server.getServerFactoryManager().getTcpipForwardingFilter();
         if (filter == null || !filter.canForwardX11(server)) {
-            if (wantReply) {
-                buffer = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_FAILURE, 0);
-                buffer.putInt(recipient);
-                writePacket(buffer);
-            }
-            return true;
+            return false;
         }
 
         String display = ((ServerSession) session).createX11Display(buffer.getBoolean(), buffer.getString(),
                                                                     buffer.getString(), buffer.getInt());
         if (display == null) {
-            if (wantReply) {
-                buffer = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_FAILURE, 0);
-                buffer.putInt(recipient);
-                writePacket(buffer);
-            }
-            return true;
+            return false;
         }
 
         addEnvVariable(X11ForwardSupport.ENV_DISPLAY, display);
-
-        if (wantReply) {
-            buffer = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_SUCCESS, 0);
-            buffer.putInt(recipient);
-            writePacket(buffer);
-        }
         return true;
     }
 


[2/4] git commit: SSHD-253 Improve performance on ServerSession auth and idle timeout checks.

Posted by gn...@apache.org.
SSHD-253 Improve performance on ServerSession auth and idle timeout checks.

Refactoring of the way ServerSession checks for auth and idle timeouts. The
original version was creating, scheduling and unscheduling many Runnable
instances while the ServerSession is active. This refactored version creates an
'idle ping' Runnable that checks each of the currently running ServerSessions
for timeouts. This check is run once every second.


Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/85d11f90
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/85d11f90
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/85d11f90

Branch: refs/heads/master
Commit: 85d11f90da5b05e6274cad620d86c469f3c388a2
Parents: 20888f7
Author: Michael Heemskerk <mh...@atlassian.com>
Authored: Thu Aug 29 16:54:10 2013 +0200
Committer: Guillaume Nodet <gn...@apache.org>
Committed: Tue Dec 3 21:31:47 2013 +0100

----------------------------------------------------------------------
 .../main/java/org/apache/sshd/SshServer.java    | 34 +++++++
 .../sshd/common/session/AbstractSession.java    |  2 +-
 .../sshd/server/session/ServerSession.java      | 97 ++++++--------------
 .../session/ServerSessionTimeoutListener.java   | 64 +++++++++++++
 .../java/org/apache/sshd/CompressionTest.java   | 27 +++---
 .../java/org/apache/sshd/SshServerTest.java     | 47 ++++++++++
 6 files changed, 190 insertions(+), 81 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/85d11f90/sshd-core/src/main/java/org/apache/sshd/SshServer.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/SshServer.java b/sshd-core/src/main/java/org/apache/sshd/SshServer.java
index 57d9faf..d75e201 100644
--- a/sshd-core/src/main/java/org/apache/sshd/SshServer.java
+++ b/sshd-core/src/main/java/org/apache/sshd/SshServer.java
@@ -32,6 +32,8 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
 
 import org.apache.sshd.common.AbstractFactoryManager;
 import org.apache.sshd.common.Channel;
@@ -98,6 +100,7 @@ import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
 import org.apache.sshd.server.session.ServerSession;
 import org.apache.sshd.server.session.SessionFactory;
 import org.apache.sshd.server.sftp.SftpSubsystem;
+import org.apache.sshd.server.session.ServerSessionTimeoutListener;
 import org.apache.sshd.server.shell.ProcessShellFactory;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -140,6 +143,8 @@ public class SshServer extends AbstractFactoryManager implements ServerFactoryMa
     protected PasswordAuthenticator passwordAuthenticator;
     protected PublickeyAuthenticator publickeyAuthenticator;
     protected GSSAuthenticator gssAuthenticator;
+    protected ServerSessionTimeoutListener sessionTimeoutListener;
+    protected ScheduledFuture<?> timeoutListenerFuture;
 
     public SshServer() {
     }
@@ -300,6 +305,13 @@ public class SshServer extends AbstractFactoryManager implements ServerFactoryMa
         sessionFactory.setServer(this);
         acceptor = createAcceptor();
 
+        // set up the the session timeout listener and schedule it
+        sessionTimeoutListener = createSessionTimeoutListener();
+        sessionFactory.addListener(sessionTimeoutListener);
+
+        timeoutListenerFuture = getScheduledExecutorService()
+                .scheduleAtFixedRate(sessionTimeoutListener, 1, 1, TimeUnit.SECONDS);
+
         if (host != null) {
             String[] hosts = host.split(",");
             LinkedList<InetSocketAddress> addresses = new LinkedList<InetSocketAddress>();
@@ -344,6 +356,9 @@ public class SshServer extends AbstractFactoryManager implements ServerFactoryMa
         for (AbstractSession session : sessions) {
             session.close(immediately).addListener(listener);
         }
+
+        stopSessionTimeoutListener();
+
         if (!immediately) {
             latch.await();
         }
@@ -379,6 +394,25 @@ public class SshServer extends AbstractFactoryManager implements ServerFactoryMa
         return new SessionFactory();
     }
 
+    protected ServerSessionTimeoutListener createSessionTimeoutListener() {
+        return new ServerSessionTimeoutListener();
+    }
+
+    protected void stopSessionTimeoutListener() {
+        // cancel the timeout monitoring task
+        if (timeoutListenerFuture != null) {
+            timeoutListenerFuture.cancel(true);
+            timeoutListenerFuture = null;
+        }
+
+        // remove the sessionTimeoutListener completely; should the SSH server be restarted, a new one
+        // will be created.
+        if (sessionFactory != null && sessionTimeoutListener != null) {
+            sessionFactory.removeListener(sessionTimeoutListener);
+        }
+        sessionTimeoutListener = null;
+    }
+
     public static SshServer setUpDefaultServer() {
         SshServer sshd = new SshServer();
         // DHG14 uses 2048 bits key which are not supported by the default JCE provider

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/85d11f90/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java b/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
index f6784d3..7b78ea8 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
@@ -142,7 +142,7 @@ public abstract class AbstractSession implements Session {
     protected final Map<AttributeKey<?>, Object> attributes = new ConcurrentHashMap<AttributeKey<?>, Object>();
     protected String username;
 
-    private State state = State.ReceiveKexInit;
+    private volatile State state = State.ReceiveKexInit;
 
     /**
      * Create a new session.

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/85d11f90/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSession.java b/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSession.java
index 6791be5..f3d8065 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSession.java
@@ -24,9 +24,7 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
-import java.util.concurrent.Future;
 import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
 
 import org.apache.sshd.SshServer;
 import org.apache.sshd.agent.common.AgentForwardSupport;
@@ -39,8 +37,8 @@ import org.apache.sshd.common.SshException;
 import org.apache.sshd.common.SshdSocketAddress;
 import org.apache.sshd.common.future.CloseFuture;
 import org.apache.sshd.common.future.SshFutureListener;
-import org.apache.sshd.common.io.IoWriteFuture;
 import org.apache.sshd.common.io.IoSession;
+import org.apache.sshd.common.io.IoWriteFuture;
 import org.apache.sshd.common.session.AbstractSession;
 import org.apache.sshd.common.util.Buffer;
 import org.apache.sshd.server.ServerFactoryManager;
@@ -62,12 +60,12 @@ import org.apache.sshd.server.x11.X11ForwardSupport;
  */
 public class ServerSession extends AbstractSession {
 
-    private Future authTimerFuture;
-    private Future idleTimerFuture;
+    private long authTimeoutTimestamp = 0L;
+    private long idleTimeoutTimestamp = 0L;
     private int maxAuthRequests = 20;
     private int nbAuthRequests;
-    private int authTimeout = 10 * 60 * 1000; // 10 minutes in milliseconds
-    private int idleTimeout = 10 * 60 * 1000; // 10 minutes in milliseconds
+    private int authTimeoutMs = 10 * 60 * 1000; // 10 minutes in milliseconds
+    private int idleTimeoutMs = 10 * 60 * 1000; // 10 minutes in milliseconds
     private boolean allowMoreSessions = true;
     private final AgentForwardSupport agentForward;
     private final X11ForwardSupport x11Forward;
@@ -82,8 +80,8 @@ public class ServerSession extends AbstractSession {
     public ServerSession(ServerFactoryManager server, IoSession ioSession) throws Exception {
         super(server, ioSession);
         maxAuthRequests = getIntProperty(ServerFactoryManager.MAX_AUTH_REQUESTS, maxAuthRequests);
-        authTimeout = getIntProperty(ServerFactoryManager.AUTH_TIMEOUT, authTimeout);
-        idleTimeout = getIntProperty(ServerFactoryManager.IDLE_TIMEOUT, idleTimeout);
+        authTimeoutMs = getIntProperty(ServerFactoryManager.AUTH_TIMEOUT, authTimeoutMs);
+        idleTimeoutMs = getIntProperty(ServerFactoryManager.IDLE_TIMEOUT, idleTimeoutMs);
         agentForward = new AgentForwardSupport(this);
         x11Forward = new X11ForwardSupport(this);
         log.info("Session created from {}", ioSession.getRemoteAddress());
@@ -93,8 +91,6 @@ public class ServerSession extends AbstractSession {
 
     @Override
     public CloseFuture close(boolean immediately) {
-        unscheduleAuthTimer();
-        unscheduleIdleTimer();
         agentForward.close();
         x11Forward.close();
         return super.close(immediately);
@@ -124,11 +120,11 @@ public class ServerSession extends AbstractSession {
     public IoWriteFuture writePacket(Buffer buffer) throws IOException {
         boolean rescheduleIdleTimer = getState() == State.Running;
         if (rescheduleIdleTimer) {
-            unscheduleIdleTimer();
+            resetIdleTimeout();
         }
         IoWriteFuture future = super.writePacket(buffer);
         if (rescheduleIdleTimer) {
-            scheduleIdleTimer();
+            resetIdleTimeout();
         }
         return future;
     }
@@ -187,7 +183,7 @@ public class ServerSession extends AbstractSession {
                         log.debug("Received SSH_MSG_NEWKEYS");
                         receiveNewKeys(true);
                         setState(State.WaitForAuth);
-                        scheduleAuthTimer();
+                        resetAuthTimeout();
                         break;
                     case WaitForAuth:
                         if (cmd != SshConstants.Message.SSH_MSG_SERVICE_REQUEST) {
@@ -212,9 +208,8 @@ public class ServerSession extends AbstractSession {
                         userAuth(buffer, cmd);
                         break;
                     case Running:
-                        unscheduleIdleTimer();
                         running(cmd, buffer);
-                        scheduleIdleTimer();
+                        resetIdleTimeout();
                         break;
                     default:
                         throw new IllegalStateException("Unsupported state: " + getState());
@@ -278,64 +273,31 @@ public class ServerSession extends AbstractSession {
         }
     }
 
-    private void scheduleAuthTimer() {
-        Runnable authTimerTask = new Runnable() {
-            public void run() {
-                try {
-                    processAuthTimer();
-                } catch (IOException e) {
-                    // Ignore
-                }
+    /**
+     * Checks whether the server session has timed out (both auth and idle timeouts are checked). If the session has
+     * timed out, a DISCONNECT message will be sent to the client.
+     *
+     * @throws IOException
+     */
+    protected void checkForTimeouts() throws IOException {
+        if (getState() != State.Closed) {
+            long now = System.currentTimeMillis();
+            if (!authed && authTimeoutTimestamp > 0 && now > authTimeoutTimestamp) {
+                disconnect(SshConstants.SSH2_DISCONNECT_PROTOCOL_ERROR, "User authentication has timed out after " + authTimeoutMs + " ms.");
             }
-        };
-        authTimerFuture = getScheduledExecutorService().schedule(authTimerTask, authTimeout, TimeUnit.MILLISECONDS);
-    }
-
-    private void unscheduleAuthTimer() {
-        if (authTimerFuture != null) {
-            authTimerFuture.cancel(false);
-            authTimerFuture = null;
-        }
-    }
-
-    private void scheduleIdleTimer() {
-        if (idleTimeout < 1) {
-            // A timeout less than one means there is no timeout.
-            return;
-        }
-        synchronized (this) {
-            unscheduleIdleTimer();
-            Runnable idleTimerTask = new Runnable() {
-                public void run() {
-                    try {
-                        processIdleTimer();
-                    } catch (IOException e) {
-                        // Ignore
-                    }
-                }
-            };
-            idleTimerFuture = getScheduledExecutorService().schedule(idleTimerTask, idleTimeout, TimeUnit.MILLISECONDS);
-        }
-    }
 
-    private void unscheduleIdleTimer() {
-        synchronized (this) {
-            if (idleTimerFuture != null) {
-                idleTimerFuture.cancel(false);
-                idleTimerFuture = null;
+            if (idleTimeoutTimestamp > 0 && now > idleTimeoutTimestamp) {
+                disconnect(SshConstants.SSH2_DISCONNECT_PROTOCOL_ERROR, "User session has timed out after " + idleTimeoutMs + " ms.");
             }
         }
     }
 
-    private void processAuthTimer() throws IOException {
-        if (!authed) {
-            disconnect(SshConstants.SSH2_DISCONNECT_PROTOCOL_ERROR,
-                       "User authentication has timed out");
-        }
+    private void resetAuthTimeout() {
+        this.authTimeoutTimestamp = System.currentTimeMillis() + authTimeoutMs;
     }
 
-    private void processIdleTimer() throws IOException {
-        disconnect(SshConstants.SSH2_DISCONNECT_PROTOCOL_ERROR, "User session has timed out after being idled for " + idleTimeout + "ms.");
+    private void resetIdleTimeout() {
+        this.idleTimeoutTimestamp = System.currentTimeMillis() + idleTimeoutMs;
     }
 
     private void sendServerIdentification() {
@@ -498,9 +460,8 @@ public class ServerSession extends AbstractSession {
                     buffer = createBuffer(SshConstants.Message.SSH_MSG_USERAUTH_SUCCESS, 0);
                     writePacket(buffer);
                     this.authed = true;
-                    unscheduleAuthTimer();
                     setState(State.Running);
-                    scheduleIdleTimer();
+                    resetIdleTimeout();
                     log.info("Session {}@{} authenticated", getUsername(), getIoSession().getRemoteAddress());
 
                 } else {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/85d11f90/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSessionTimeoutListener.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSessionTimeoutListener.java b/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSessionTimeoutListener.java
new file mode 100644
index 0000000..580249c
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSessionTimeoutListener.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.server.session;
+
+import org.apache.mina.util.ConcurrentHashSet;
+import org.apache.sshd.common.Session;
+import org.apache.sshd.common.SessionListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Set;
+
+/**
+ * Task that iterates over all currently open {@link ServerSession}s and checks each of them for timeouts. If 
+ * the {@link ServerSession} has timed out (either auth or idle timeout), the session will be disconnected.
+ *
+ * @see org.apache.sshd.server.session.ServerSession#checkForTimeouts()
+ */
+public class ServerSessionTimeoutListener implements SessionListener, Runnable {
+
+    private final Logger log = LoggerFactory.getLogger(ServerSessionTimeoutListener.class);
+    
+    private final Set<ServerSession> sessions = new ConcurrentHashSet<ServerSession>();
+
+    public void sessionCreated(Session session) {
+        if (session instanceof ServerSession) {
+            sessions.add((ServerSession) session);
+        }
+    }
+
+    public void sessionChanged(Session session) {
+        // ignore
+    }
+
+    public void sessionClosed(Session s) {
+        sessions.remove(s);
+    }
+
+    public void run() {
+        for (ServerSession session : sessions) {
+            try {
+                session.checkForTimeouts();
+            } catch (Exception e) {
+                log.warn("An error occurred while checking session timeouts", e);
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/85d11f90/sshd-core/src/test/java/org/apache/sshd/CompressionTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/CompressionTest.java b/sshd-core/src/test/java/org/apache/sshd/CompressionTest.java
index 73cba2b..9fd407f 100644
--- a/sshd-core/src/test/java/org/apache/sshd/CompressionTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/CompressionTest.java
@@ -121,18 +121,21 @@ public class CompressionTest {
         });
         s.connect();
         com.jcraft.jsch.Channel c = s.openChannel("shell");
-        c.connect();
-        OutputStream os = c.getOutputStream();
-        InputStream is = c.getInputStream();
-        for (int i = 0; i < 10; i++) {
-            os.write("this is my command\n".getBytes());
-            os.flush();
-            byte[] data = new byte[512];
-            int len = is.read(data);
-            String str = new String(data, 0, len);
-            assertEquals("this is my command\n", str);
+        try {
+            c.connect();
+            OutputStream os = c.getOutputStream();
+            InputStream is = c.getInputStream();
+            for (int i = 0; i < 10; i++) {
+                os.write("this is my command\n".getBytes());
+                os.flush();
+                byte[] data = new byte[512];
+                int len = is.read(data);
+                String str = new String(data, 0, len);
+                assertEquals("this is my command\n", str);
+            }
+        } finally {
+            c.disconnect();
+            s.disconnect();
         }
-        c.disconnect();
-        s.disconnect();
     }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/85d11f90/sshd-core/src/test/java/org/apache/sshd/SshServerTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/SshServerTest.java b/sshd-core/src/test/java/org/apache/sshd/SshServerTest.java
index 2b3abbe..e6461af 100644
--- a/sshd-core/src/test/java/org/apache/sshd/SshServerTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/SshServerTest.java
@@ -18,12 +18,23 @@
  */
 package org.apache.sshd;
 
+import org.apache.sshd.util.BogusPasswordAuthenticator;
+import org.apache.sshd.util.EchoShellFactory;
+import org.apache.sshd.util.Utils;
 import org.junit.Test;
 
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
 /**
  * @author Kohsuke Kawaguchi
+ * @author Michael Heemskerk
  */
 public class SshServerTest {
+
     @Test
     public void stopMethodShouldBeIdempotent() throws Exception {
         SshServer sshd = new SshServer();
@@ -31,4 +42,40 @@ public class SshServerTest {
         sshd.stop();
         sshd.stop();
     }
+
+    @Test
+    public void testExecutorShutdownFalse() throws Exception {
+        ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
+
+        SshServer sshd = createTestServer();
+        sshd.setScheduledExecutorService(executorService);
+
+        sshd.start();
+        sshd.stop();
+
+        assertFalse(executorService.isShutdown());
+    }
+
+    @Test
+    public void testExecutorShutdownTrue() throws Exception {
+        ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
+
+        SshServer sshd = createTestServer();
+        sshd.setScheduledExecutorService(executorService, true);
+
+        sshd.start();
+        sshd.stop();
+
+        assertTrue(executorService.isShutdown());
+    }
+
+
+    private SshServer createTestServer() {
+        SshServer sshd = SshServer.setUpDefaultServer();
+        sshd.setKeyPairProvider(Utils.createTestHostKeyProvider());
+        sshd.setShellFactory(new EchoShellFactory());
+        sshd.setPasswordAuthenticator(new BogusPasswordAuthenticator());
+
+        return sshd;
+    }
 }