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);