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 2011/08/30 12:28:49 UTC

svn commit: r1163164 - in /mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd: SshServer.java common/AbstractFactoryManager.java common/FactoryManager.java server/ServerFactoryManager.java server/session/ServerSession.java

Author: gnodet
Date: Tue Aug 30 10:28:49 2011
New Revision: 1163164

URL: http://svn.apache.org/viewvc?rev=1163164&view=rev
Log:
[SSHD-140] Add configurable session idle timeout and use a single scheduler for all sessions

Modified:
    mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/SshServer.java
    mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/common/AbstractFactoryManager.java
    mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java
    mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/ServerFactoryManager.java
    mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSession.java

Modified: mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/SshServer.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/SshServer.java?rev=1163164&r1=1163163&r2=1163164&view=diff
==============================================================================
--- mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/SshServer.java (original)
+++ mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/SshServer.java Tue Aug 30 10:28:49 2011
@@ -30,11 +30,8 @@ import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
-
-import org.apache.sshd.server.FileSystemFactory;
-import org.apache.sshd.server.filesystem.NativeFileSystemFactory;
-import org.slf4j.LoggerFactory;
-import org.slf4j.Logger;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
 
 import org.apache.mina.core.service.IoAcceptor;
 import org.apache.mina.core.session.IoSession;
@@ -71,10 +68,11 @@ import org.apache.sshd.common.util.OsUti
 import org.apache.sshd.common.util.SecurityUtils;
 import org.apache.sshd.server.Command;
 import org.apache.sshd.server.CommandFactory;
+import org.apache.sshd.server.FileSystemFactory;
+import org.apache.sshd.server.ForwardingFilter;
 import org.apache.sshd.server.PasswordAuthenticator;
 import org.apache.sshd.server.PublickeyAuthenticator;
 import org.apache.sshd.server.ServerFactoryManager;
-import org.apache.sshd.server.ForwardingFilter;
 import org.apache.sshd.server.UserAuth;
 import org.apache.sshd.server.auth.UserAuthPassword;
 import org.apache.sshd.server.auth.UserAuthPublicKey;
@@ -82,6 +80,7 @@ import org.apache.sshd.server.auth.gss.G
 import org.apache.sshd.server.auth.gss.UserAuthGSS;
 import org.apache.sshd.server.channel.ChannelDirectTcpip;
 import org.apache.sshd.server.channel.ChannelSession;
+import org.apache.sshd.server.filesystem.NativeFileSystemFactory;
 import org.apache.sshd.server.kex.DHG1;
 import org.apache.sshd.server.kex.DHG14;
 import org.apache.sshd.server.keyprovider.PEMGeneratorHostKeyProvider;
@@ -89,6 +88,8 @@ import org.apache.sshd.server.keyprovide
 import org.apache.sshd.server.session.ServerSession;
 import org.apache.sshd.server.session.SessionFactory;
 import org.apache.sshd.server.shell.ProcessShellFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * The SshServer class is the main entry point for the server side of the SSH protocol.
@@ -133,6 +134,8 @@ public class SshServer extends AbstractF
     protected PublickeyAuthenticator publickeyAuthenticator;
     protected GSSAuthenticator gssAuthenticator;
     protected ForwardingFilter forwardingFilter;
+    protected ScheduledExecutorService executor;
+    protected boolean shutdownExecutor;
 
     public SshServer() {
     }
@@ -262,6 +265,19 @@ public class SshServer extends AbstractF
         this.forwardingFilter = forwardingFilter;
     }
 
+    public ScheduledExecutorService getScheduledExecutorService() {
+        return executor;
+    }
+
+    public void setScheduledExecutorService(ScheduledExecutorService executor) {
+        setScheduledExecutorService(executor, false);
+    }
+
+    public void setScheduledExecutorService(ScheduledExecutorService executor, boolean shutdownExecutor) {
+        this.executor = executor;
+        this.shutdownExecutor = shutdownExecutor;
+    }
+
     protected void checkConfig() {
         if (getPort() < 0) {
             throw new IllegalArgumentException("Bad port number: " + port);
@@ -286,6 +302,9 @@ public class SshServer extends AbstractF
                 throw new IllegalArgumentException("UserAuthFactories not set");
             }
         }
+        if (getScheduledExecutorService() == null) {
+            setScheduledExecutorService(Executors.newSingleThreadScheduledExecutor(), true);
+        }
         if (getCipherFactories() == null) {
             throw new IllegalArgumentException("CipherFactories not set");
         }
@@ -363,6 +382,10 @@ public class SshServer extends AbstractF
         }
         acceptor.dispose();
         acceptor = null;
+        if (shutdownExecutor && executor != null) {
+            executor.shutdown();
+            executor = null;
+        }
     }
 
     protected IoAcceptor createAcceptor() {

Modified: mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/common/AbstractFactoryManager.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/common/AbstractFactoryManager.java?rev=1163164&r1=1163163&r2=1163164&view=diff
==============================================================================
--- mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/common/AbstractFactoryManager.java (original)
+++ mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/common/AbstractFactoryManager.java Tue Aug 30 10:28:49 2011
@@ -23,6 +23,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Properties;
+import java.util.concurrent.ScheduledExecutorService;
 
 import org.slf4j.LoggerFactory;
 import org.slf4j.Logger;
@@ -142,4 +143,5 @@ public abstract class AbstractFactoryMan
     public void setChannelFactories(List<NamedFactory<Channel>> channelFactories) {
         this.channelFactories = channelFactories;
     }
+
 }

Modified: mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java?rev=1163164&r1=1163163&r2=1163164&view=diff
==============================================================================
--- mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java (original)
+++ mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java Tue Aug 30 10:28:49 2011
@@ -42,20 +42,6 @@ public interface FactoryManager {
     public static final String MAX_PACKET_SIZE = "packet-size";
 
     /**
-     * Key used to retrieve the value in the configuration properties map
-     * of the maximum number of failed authentication requests before the
-     * server closes the connection.
-     */
-    public static final String MAX_AUTH_REQUESTS = "max-auth-requests";
-
-    /**
-     * Key used to retrieve the value of the timeout after which
-     * the server will close the connection if the client has not been
-     * authenticated.
-     */
-    public static final String AUTH_TIMEOUT = "auth-timeout";
-
-    /**
      * A map of properties that can be used to configure the SSH server
      * or client.  This map will never be changed by either the server or
      * client and is not supposed to be changed at runtime (changes are not

Modified: mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/ServerFactoryManager.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/ServerFactoryManager.java?rev=1163164&r1=1163163&r2=1163164&view=diff
==============================================================================
--- mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/ServerFactoryManager.java (original)
+++ mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/ServerFactoryManager.java Tue Aug 30 10:28:49 2011
@@ -19,6 +19,7 @@
 package org.apache.sshd.server;
 
 import java.util.List;
+import java.util.concurrent.ScheduledExecutorService;
 
 import org.apache.sshd.common.Factory;
 import org.apache.sshd.common.FactoryManager;
@@ -35,11 +36,31 @@ public interface ServerFactoryManager ex
     /**
      * Key used to retrieve the value of the maximum concurrent open session count per username
      */
-    String MAX_CONCURRENT_SESSIONS = "max-concurrent-sessions";
+    public static final String MAX_CONCURRENT_SESSIONS = "max-concurrent-sessions";
     /**
      * Key used to retrieve the value of the server identification string if not default.
      */
-    String SERVER_IDENTIFICATION = "server-identification";
+    public static final String SERVER_IDENTIFICATION = "server-identification";
+    /**
+     * Key used to retrieve the value in the configuration properties map
+     * of the maximum number of failed authentication requests before the
+     * server closes the connection.
+     */
+    public static final String MAX_AUTH_REQUESTS = "max-auth-requests";
+
+    /**
+     * Key used to retrieve the value of the timeout after which
+     * the server will close the connection if the client has not been
+     * authenticated.
+     */
+    public static final String AUTH_TIMEOUT = "auth-timeout";
+
+    /**
+     * Key used to retrieve the value of idle timeout after which
+     * the server will close the connection.  In milliseconds.
+     */
+    public static final String IDLE_TIMEOUT = "idle-timeout";
+
 
     /**
      * Retrieve the list of named factories for <code>UserAuth<code> objects.
@@ -109,7 +130,7 @@ public interface ServerFactoryManager ex
      * @return a valid <code>FileSystemFactory</code> object or <code>null</code> if commands
      *         are not supported on this server
      */
-     FileSystemFactory getFileSystemFactory();
+    FileSystemFactory getFileSystemFactory();
 
     /**
      * Retrieve the list of named factories for <code>CommandFactory.Command</code> to
@@ -120,4 +141,12 @@ public interface ServerFactoryManager ex
      */
     List<NamedFactory<Command>> getSubsystemFactories();
 
+
+    /**
+     * Retrieve the <code>ScheduledExecutorService</code> to be used.
+     *
+     * @return the <code>ScheduledExecutorService</code>, never <code>null</code>
+     */
+    ScheduledExecutorService getScheduledExecutorService();
+
 }

Modified: mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSession.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSession.java?rev=1163164&r1=1163163&r2=1163164&view=diff
==============================================================================
--- mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSession.java (original)
+++ mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSession.java Tue Aug 30 10:28:49 2011
@@ -22,8 +22,9 @@ import java.io.IOException;
 import java.security.KeyPair;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Timer;
-import java.util.TimerTask;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
 
 import org.apache.mina.core.session.IoSession;
 import org.apache.sshd.agent.AgentForwardSupport;
@@ -52,20 +53,19 @@ import org.apache.sshd.server.x11.X11For
  *
  * TODO: better use of SSH_MSG_DISCONNECT and disconnect error codes
  *
- * TODO: use a single Timer for on the server for all sessions
- *
  * TODO Add javadoc
  *
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
 public class ServerSession extends AbstractSession {
 
-    private Timer timer;
-    private TimerTask authTimerTask;
+    private Future authTimerFuture;
+    private Future idleTimerFuture;
     private State state = State.ReceiveKexInit;
     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 boolean allowMoreSessions = true;
     private final TcpipForwardSupport tcpipForward;
     private final AgentForwardSupport agentForward;
@@ -81,8 +81,9 @@ public class ServerSession extends Abstr
 
     public ServerSession(FactoryManager server, IoSession ioSession) throws Exception {
         super(server, ioSession);
-        maxAuthRequests = getIntProperty(FactoryManager.MAX_AUTH_REQUESTS, maxAuthRequests);
-        authTimeout = getIntProperty(FactoryManager.AUTH_TIMEOUT, authTimeout);
+        maxAuthRequests = getIntProperty(ServerFactoryManager.MAX_AUTH_REQUESTS, maxAuthRequests);
+        authTimeout = getIntProperty(ServerFactoryManager.AUTH_TIMEOUT, authTimeout);
+        idleTimeout = getIntProperty(ServerFactoryManager.IDLE_TIMEOUT, idleTimeout);
         tcpipForward = new TcpipForwardSupport(this);
         agentForward = new AgentForwardSupport(this);
         x11Forward = new X11ForwardSupport(this);
@@ -94,6 +95,7 @@ public class ServerSession extends Abstr
     @Override
     public CloseFuture close(boolean immediately) {
         unscheduleAuthTimer();
+        unscheduleIdleTimer();
         tcpipForward.close();
         agentForward.close();
         x11Forward.close();
@@ -116,6 +118,10 @@ public class ServerSession extends Abstr
         return (ServerFactoryManager) factoryManager;
     }
 
+    protected ScheduledExecutorService getScheduledExecutorService() {
+        return getServerFactoryManager().getScheduledExecutorService();
+    }
+
     protected void handleMessage(Buffer buffer) throws Exception {
         SshConstants.Message cmd = buffer.getCommand();
         log.debug("Received packet {}", cmd);
@@ -195,59 +201,9 @@ public class ServerSession extends Abstr
                         userAuth(buffer, cmd);
                         break;
                     case Running:
-                        switch (cmd) {
-                            case SSH_MSG_SERVICE_REQUEST:
-                                serviceRequest(buffer);
-                                break;
-                            case SSH_MSG_CHANNEL_OPEN:
-                                channelOpen(buffer);
-                                break;
-                            case SSH_MSG_CHANNEL_OPEN_CONFIRMATION:
-                                channelOpenConfirmation(buffer);
-                                break;
-                            case SSH_MSG_CHANNEL_OPEN_FAILURE:
-                                channelOpenFailure(buffer);
-                                break;
-                            case SSH_MSG_CHANNEL_REQUEST:
-                                channelRequest(buffer);
-                                break;
-                            case SSH_MSG_CHANNEL_DATA:
-                                channelData(buffer);
-                                break;
-                            case SSH_MSG_CHANNEL_EXTENDED_DATA:
-                                channelExtendedData(buffer);
-                                break;
-                            case SSH_MSG_CHANNEL_WINDOW_ADJUST:
-                                channelWindowAdjust(buffer);
-                                break;
-                            case SSH_MSG_CHANNEL_EOF:
-                                channelEof(buffer);
-                                break;
-                            case SSH_MSG_CHANNEL_CLOSE:
-                                channelClose(buffer);
-                                break;
-                            case SSH_MSG_GLOBAL_REQUEST:
-                                globalRequest(buffer);
-                                break;
-                            case SSH_MSG_KEXINIT:
-                                receiveKexInit(buffer);
-                                sendKexInit();
-                                negociate();
-                                kex = NamedFactory.Utils.create(factoryManager.getKeyExchangeFactories(), negociated[SshConstants.PROPOSAL_KEX_ALGS]);
-                                kex.init(this, serverVersion.getBytes(), clientVersion.getBytes(), I_S, I_C);
-                                break;
-                            case SSH_MSG_KEXDH_INIT:
-                                buffer.rpos(buffer.rpos() - 1);
-                                if (kex.next(buffer)) {
-                                    sendNewKeys();
-                                }
-                                break;
-                            case SSH_MSG_NEWKEYS:
-                                receiveNewKeys(true);
-                                break;
-                            default:
-                                throw new IllegalStateException("Unsupported command: " + cmd);
-                        }
+                        unscheduleIdleTimer();
+                        running(cmd, buffer);
+                        scheduleIdleTimer();
                         break;
                     default:
                         throw new IllegalStateException("Unsupported state: " + state);
@@ -255,8 +211,64 @@ public class ServerSession extends Abstr
         }
     }
 
+    private void running(SshConstants.Message cmd, Buffer buffer) throws Exception {
+        switch (cmd) {
+            case SSH_MSG_SERVICE_REQUEST:
+                serviceRequest(buffer);
+                break;
+            case SSH_MSG_CHANNEL_OPEN:
+                channelOpen(buffer);
+                break;
+            case SSH_MSG_CHANNEL_OPEN_CONFIRMATION:
+                channelOpenConfirmation(buffer);
+                break;
+            case SSH_MSG_CHANNEL_OPEN_FAILURE:
+                channelOpenFailure(buffer);
+                break;
+            case SSH_MSG_CHANNEL_REQUEST:
+                channelRequest(buffer);
+                break;
+            case SSH_MSG_CHANNEL_DATA:
+                channelData(buffer);
+                break;
+            case SSH_MSG_CHANNEL_EXTENDED_DATA:
+                channelExtendedData(buffer);
+                break;
+            case SSH_MSG_CHANNEL_WINDOW_ADJUST:
+                channelWindowAdjust(buffer);
+                break;
+            case SSH_MSG_CHANNEL_EOF:
+                channelEof(buffer);
+                break;
+            case SSH_MSG_CHANNEL_CLOSE:
+                channelClose(buffer);
+                break;
+            case SSH_MSG_GLOBAL_REQUEST:
+                globalRequest(buffer);
+                break;
+            case SSH_MSG_KEXINIT:
+                receiveKexInit(buffer);
+                sendKexInit();
+                negociate();
+                kex = NamedFactory.Utils.create(factoryManager.getKeyExchangeFactories(), negociated[SshConstants.PROPOSAL_KEX_ALGS]);
+                kex.init(this, serverVersion.getBytes(), clientVersion.getBytes(), I_S, I_C);
+                break;
+            case SSH_MSG_KEXDH_INIT:
+                buffer.rpos(buffer.rpos() - 1);
+                if (kex.next(buffer)) {
+                    sendNewKeys();
+                }
+                break;
+            case SSH_MSG_NEWKEYS:
+                receiveNewKeys(true);
+                break;
+            default:
+                throw new IllegalStateException("Unsupported command: " + cmd);
+        }
+    }
+
     private void scheduleAuthTimer() {
-        authTimerTask = new TimerTask() {
+        Runnable authTimerTask = new Runnable() {
             public void run() {
                 try {
                     processAuthTimer();
@@ -265,18 +277,33 @@ public class ServerSession extends Abstr
                 }
             }
         };
-        timer = new Timer(true);
-        timer.schedule(authTimerTask, authTimeout);
+        authTimerFuture = getScheduledExecutorService().schedule(authTimerTask, authTimeout, TimeUnit.MILLISECONDS);
     }
 
     private void unscheduleAuthTimer() {
-        if (authTimerTask != null) {
-            authTimerTask.cancel();
-            authTimerTask = null;
-        }
-        if (timer != null) {
-            timer.cancel();
-            timer = null;
+        if (authTimerFuture != null) {
+            authTimerFuture.cancel(false);
+            authTimerFuture = null;
+        }
+    }
+
+    private void scheduleIdleTimer() {
+        Runnable idleTimerTask = new Runnable() {
+            public void run() {
+                try {
+                    processIdleTimer();
+                } catch (IOException e) {
+                    // Ignore
+                }
+            }
+        };
+        idleTimerFuture = getScheduledExecutorService().schedule(idleTimerTask, idleTimeout, TimeUnit.MILLISECONDS);
+    }
+
+    private void unscheduleIdleTimer() {
+        if (idleTimerFuture != null) {
+            idleTimerFuture.cancel(false);
+            idleTimerFuture = null;
         }
     }
 
@@ -287,6 +314,10 @@ public class ServerSession extends Abstr
         }
     }
 
+    private void processIdleTimer() throws IOException {
+        disconnect(SshConstants.SSH2_DISCONNECT_PROTOCOL_ERROR, "User idle has timed out after " + idleTimeout + "ms.");
+    }
+
     private void sendServerIdentification() {
         if (getFactoryManager().getProperties() != null && getFactoryManager().getProperties().get(ServerFactoryManager.SERVER_IDENTIFICATION) != null) {
             serverVersion = "SSH-2.0-" + getFactoryManager().getProperties().get(ServerFactoryManager.SERVER_IDENTIFICATION);