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 2016/02/26 06:17:52 UTC

[2/3] mina-sshd git commit: Moved common client/server session code from the implementation classes to their respective abstract classes

Moved common client/server session code from the implementation classes to their respective abstract classes


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

Branch: refs/heads/master
Commit: 4c9d3aebecdcc9100148037ddf84354690f2da48
Parents: 7406188
Author: Lyor Goldstein <ly...@gmail.com>
Authored: Fri Feb 26 07:16:39 2016 +0200
Committer: Lyor Goldstein <ly...@gmail.com>
Committed: Fri Feb 26 07:16:39 2016 +0200

----------------------------------------------------------------------
 .../client/session/AbstractClientSession.java   | 139 ++++++++++++
 .../sshd/client/session/ClientSessionImpl.java  | 191 +++-------------
 .../common/session/helpers/AbstractSession.java |   7 +
 .../server/session/AbstractServerSession.java   | 216 ++++++++++++++++++
 .../sshd/server/session/ServerSessionImpl.java  | 223 +------------------
 5 files changed, 402 insertions(+), 374 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/4c9d3aeb/sshd-core/src/main/java/org/apache/sshd/client/session/AbstractClientSession.java
----------------------------------------------------------------------
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 0a8a444..9193cec 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
@@ -23,7 +23,9 @@ import java.io.IOException;
 import java.net.SocketAddress;
 import java.nio.file.FileSystem;
 import java.security.KeyPair;
+import java.util.EnumMap;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.CopyOnWriteArrayList;
 
 import org.apache.sshd.client.ClientFactoryManager;
@@ -33,6 +35,7 @@ import org.apache.sshd.client.auth.keyboard.UserInteraction;
 import org.apache.sshd.client.auth.password.PasswordIdentityProvider;
 import org.apache.sshd.client.channel.ChannelDirectTcpip;
 import org.apache.sshd.client.channel.ChannelExec;
+import org.apache.sshd.client.channel.ChannelShell;
 import org.apache.sshd.client.channel.ChannelSubsystem;
 import org.apache.sshd.client.channel.ClientChannel;
 import org.apache.sshd.client.keyverifier.ServerKeyVerifier;
@@ -46,20 +49,32 @@ import org.apache.sshd.client.subsystem.sftp.SftpVersionSelector;
 import org.apache.sshd.common.FactoryManager;
 import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.SshConstants;
+import org.apache.sshd.common.SshException;
 import org.apache.sshd.common.channel.Channel;
+import org.apache.sshd.common.cipher.BuiltinCiphers;
+import org.apache.sshd.common.cipher.CipherNone;
 import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.forward.TcpipForwarder;
+import org.apache.sshd.common.future.DefaultKeyExchangeFuture;
+import org.apache.sshd.common.future.KeyExchangeFuture;
 import org.apache.sshd.common.io.IoSession;
 import org.apache.sshd.common.io.IoWriteFuture;
+import org.apache.sshd.common.kex.KexProposalOption;
+import org.apache.sshd.common.kex.KexState;
 import org.apache.sshd.common.scp.ScpFileOpener;
 import org.apache.sshd.common.scp.ScpTransferEventListener;
 import org.apache.sshd.common.session.ConnectionService;
+import org.apache.sshd.common.session.helpers.AbstractConnectionService;
 import org.apache.sshd.common.session.helpers.AbstractSession;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.Buffer;
 import org.apache.sshd.common.util.net.SshdSocketAddress;
 
 /**
+ * Provides default implementations of {@link ClientSession} related methods
+ *
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
 public abstract class AbstractClientSession extends AbstractSession implements ClientSession {
@@ -394,4 +409,128 @@ public abstract class AbstractClientSession extends AbstractSession implements C
     public void startService(String name) throws Exception {
         throw new IllegalStateException("Starting services is not supported on the client side: " + name);
     }
+
+
+    @Override
+    public ChannelShell createShellChannel() throws IOException {
+        if ((inCipher instanceof CipherNone) || (outCipher instanceof CipherNone)) {
+            throw new IllegalStateException("Interactive channels are not supported with none cipher");
+        }
+        ChannelShell channel = new ChannelShell();
+        ConnectionService service = getConnectionService();
+        int id = service.registerChannel(channel);
+        if (log.isDebugEnabled()) {
+            log.debug("createShellChannel({}) created id={}", this, id);
+        }
+        return channel;
+    }
+
+    @Override
+    protected boolean readIdentification(Buffer buffer) throws IOException {
+        serverVersion = doReadIdentification(buffer, false);
+        if (serverVersion == null) {
+            return false;
+        }
+
+        if (log.isDebugEnabled()) {
+            log.debug("readIdentification({}) Server version string: {}", this, serverVersion);
+        }
+
+        if (!(serverVersion.startsWith(DEFAULT_SSH_VERSION_PREFIX) || serverVersion.startsWith("SSH-1.99-"))) {
+            throw new SshException(SshConstants.SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED,
+                    "Unsupported protocol version: " + serverVersion);
+        }
+
+        return true;
+    }
+
+    @Override
+    protected byte[] sendKexInit(Map<KexProposalOption, String> proposal) throws IOException {
+        mergeProposals(clientProposal, proposal);
+        return super.sendKexInit(proposal);
+    }
+
+    @Override
+    protected void setKexSeed(byte... seed) {
+        i_c = ValidateUtils.checkNotNullAndNotEmpty(seed, "No KEX seed");
+    }
+
+    @Override
+    protected void receiveKexInit(Map<KexProposalOption, String> proposal, byte[] seed) throws IOException {
+        mergeProposals(serverProposal, proposal);
+        i_s = seed;
+    }
+
+    @Override
+    protected void checkKeys() throws SshException {
+        ServerKeyVerifier serverKeyVerifier = ValidateUtils.checkNotNull(getServerKeyVerifier(), "No server key verifier");
+        SocketAddress remoteAddress = ioSession.getRemoteAddress();
+
+        if (!serverKeyVerifier.verifyServerKey(this, remoteAddress, kex.getServerKey())) {
+            throw new SshException(SshConstants.SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE, "Server key did not validate");
+        }
+    }
+
+    @Override
+    public KeyExchangeFuture switchToNoneCipher() throws IOException {
+        if (!(currentService instanceof AbstractConnectionService<?>)
+                || !GenericUtils.isEmpty(((AbstractConnectionService<?>) currentService).getChannels())) {
+            throw new IllegalStateException("The switch to the none cipher must be done immediately after authentication");
+        }
+
+        if (kexState.compareAndSet(KexState.DONE, KexState.INIT)) {
+            DefaultKeyExchangeFuture kexFuture = new DefaultKeyExchangeFuture(null);
+            DefaultKeyExchangeFuture prev = kexFutureHolder.getAndSet(kexFuture);
+            if (prev != null) {
+                synchronized (prev) {
+                    Object value = prev.getValue();
+                    if (value == null) {
+                        prev.setValue(new SshException("Switch to none cipher while previous KEX is ongoing"));
+                    }
+                }
+            }
+
+            String c2sEncServer;
+            String s2cEncServer;
+            synchronized (serverProposal) {
+                c2sEncServer = serverProposal.get(KexProposalOption.C2SENC);
+                s2cEncServer = serverProposal.get(KexProposalOption.S2CENC);
+            }
+            boolean c2sEncServerNone = BuiltinCiphers.Constants.isNoneCipherIncluded(c2sEncServer);
+            boolean s2cEncServerNone = BuiltinCiphers.Constants.isNoneCipherIncluded(s2cEncServer);
+
+            String c2sEncClient;
+            String s2cEncClient;
+            synchronized (clientProposal) {
+                c2sEncClient = clientProposal.get(KexProposalOption.C2SENC);
+                s2cEncClient = clientProposal.get(KexProposalOption.S2CENC);
+            }
+
+            boolean c2sEncClientNone = BuiltinCiphers.Constants.isNoneCipherIncluded(c2sEncClient);
+            boolean s2cEncClientNone = BuiltinCiphers.Constants.isNoneCipherIncluded(s2cEncClient);
+
+            if ((!c2sEncServerNone) || (!s2cEncServerNone)) {
+                kexFuture.setValue(new SshException("Server does not support none cipher"));
+            } else if ((!c2sEncClientNone) || (!s2cEncClientNone)) {
+                kexFuture.setValue(new SshException("Client does not support none cipher"));
+            } else {
+                log.info("switchToNoneCipher({}) switching", this);
+
+                Map<KexProposalOption, String> proposal = new EnumMap<KexProposalOption, String>(KexProposalOption.class);
+                synchronized (clientProposal) {
+                    proposal.putAll(clientProposal);
+                }
+
+                proposal.put(KexProposalOption.C2SENC, BuiltinCiphers.Constants.NONE);
+                proposal.put(KexProposalOption.S2CENC, BuiltinCiphers.Constants.NONE);
+
+                byte[] seed = sendKexInit(proposal);
+                setKexSeed(seed);
+            }
+
+            return ValidateUtils.checkNotNull(kexFutureHolder.get(), "No current KEX future");
+        } else {
+            throw new SshException("In flight key exchange");
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/4c9d3aeb/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 2bec6a3..d73a342 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
@@ -19,11 +19,9 @@
 package org.apache.sshd.client.session;
 
 import java.io.IOException;
-import java.net.SocketAddress;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.EnumMap;
 import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.List;
@@ -31,31 +29,22 @@ import java.util.Map;
 import java.util.Set;
 
 import org.apache.sshd.client.ClientFactoryManager;
-import org.apache.sshd.client.channel.ChannelShell;
 import org.apache.sshd.client.future.AuthFuture;
 import org.apache.sshd.client.future.DefaultAuthFuture;
-import org.apache.sshd.client.keyverifier.ServerKeyVerifier;
 import org.apache.sshd.common.RuntimeSshException;
 import org.apache.sshd.common.Service;
 import org.apache.sshd.common.ServiceFactory;
 import org.apache.sshd.common.SshConstants;
 import org.apache.sshd.common.SshException;
-import org.apache.sshd.common.cipher.BuiltinCiphers;
-import org.apache.sshd.common.cipher.CipherNone;
-import org.apache.sshd.common.future.DefaultKeyExchangeFuture;
-import org.apache.sshd.common.future.KeyExchangeFuture;
 import org.apache.sshd.common.io.IoSession;
-import org.apache.sshd.common.kex.KexProposalOption;
 import org.apache.sshd.common.kex.KexState;
-import org.apache.sshd.common.session.ConnectionService;
 import org.apache.sshd.common.session.SessionListener;
-import org.apache.sshd.common.session.helpers.AbstractConnectionService;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.ValidateUtils;
 import org.apache.sshd.common.util.buffer.Buffer;
 
 /**
- * TODO Add javadoc
+ * The default implementation of a {@link ClientSession}
  *
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
@@ -73,10 +62,10 @@ public class ClientSessionImpl extends AbstractClientSession {
     private Service nextService;
     private ServiceFactory nextServiceFactory;
 
-    public ClientSessionImpl(ClientFactoryManager client, IoSession session) throws Exception {
-        super(client, session);
+    public ClientSessionImpl(ClientFactoryManager client, IoSession ioSession) throws Exception {
+        super(client, ioSession);
         if (log.isDebugEnabled()) {
-            log.debug("Client session created: {}", session);
+            log.debug("Client session created: {}", ioSession);
         }
         // Need to set the initial service early as calling code likes to start trying to
         // manipulate it before the connection has even been established.  For instance, to
@@ -103,6 +92,13 @@ public class ClientSessionImpl extends AbstractClientSession {
             listener.sessionCreated(this);
         } catch (Throwable t) {
             Throwable e = GenericUtils.peelException(t);
+            if (log.isDebugEnabled()) {
+                log.debug("Failed ({}) to announce session={} created: {}",
+                          e.getClass().getSimpleName(), ioSession, e.getMessage());
+            }
+            if (log.isTraceEnabled()) {
+                log.trace("Session=" + ioSession + " creation failure details", e);
+            }
             if (e instanceof Exception) {
                 throw (Exception) e;
             } else {
@@ -191,80 +187,34 @@ public class ClientSessionImpl extends AbstractClientSession {
     }
 
     @Override
-    public KeyExchangeFuture switchToNoneCipher() throws IOException {
-        if (!(currentService instanceof AbstractConnectionService<?>)
-                || !GenericUtils.isEmpty(((AbstractConnectionService<?>) currentService).getChannels())) {
-            throw new IllegalStateException("The switch to the none cipher must be done immediately after authentication");
+    protected void sendSessionEvent(SessionListener.Event event) throws IOException {
+        if (SessionListener.Event.KeyEstablished.equals(event)) {
+            sendInitialServiceRequest();
         }
-
-        if (kexState.compareAndSet(KexState.DONE, KexState.INIT)) {
-            DefaultKeyExchangeFuture kexFuture = new DefaultKeyExchangeFuture(null);
-            DefaultKeyExchangeFuture prev = kexFutureHolder.getAndSet(kexFuture);
-            if (prev != null) {
-                synchronized (prev) {
-                    Object value = prev.getValue();
-                    if (value == null) {
-                        prev.setValue(new SshException("Switch to none cipher while previous KEX is ongoing"));
-                    }
-                }
-            }
-
-            String c2sEncServer;
-            String s2cEncServer;
-            synchronized (serverProposal) {
-                c2sEncServer = serverProposal.get(KexProposalOption.C2SENC);
-                s2cEncServer = serverProposal.get(KexProposalOption.S2CENC);
-            }
-            boolean c2sEncServerNone = BuiltinCiphers.Constants.isNoneCipherIncluded(c2sEncServer);
-            boolean s2cEncServerNone = BuiltinCiphers.Constants.isNoneCipherIncluded(s2cEncServer);
-
-            String c2sEncClient;
-            String s2cEncClient;
-            synchronized (clientProposal) {
-                c2sEncClient = clientProposal.get(KexProposalOption.C2SENC);
-                s2cEncClient = clientProposal.get(KexProposalOption.S2CENC);
-            }
-
-            boolean c2sEncClientNone = BuiltinCiphers.Constants.isNoneCipherIncluded(c2sEncClient);
-            boolean s2cEncClientNone = BuiltinCiphers.Constants.isNoneCipherIncluded(s2cEncClient);
-
-            if ((!c2sEncServerNone) || (!s2cEncServerNone)) {
-                kexFuture.setValue(new SshException("Server does not support none cipher"));
-            } else if ((!c2sEncClientNone) || (!s2cEncClientNone)) {
-                kexFuture.setValue(new SshException("Client does not support none cipher"));
-            } else {
-                log.info("switchToNoneCipher({}) switching", this);
-
-                Map<KexProposalOption, String> proposal = new EnumMap<KexProposalOption, String>(KexProposalOption.class);
-                synchronized (clientProposal) {
-                    proposal.putAll(clientProposal);
-                }
-
-                proposal.put(KexProposalOption.C2SENC, BuiltinCiphers.Constants.NONE);
-                proposal.put(KexProposalOption.S2CENC, BuiltinCiphers.Constants.NONE);
-
-                byte[] seed = sendKexInit(proposal);
-                setKexSeed(seed);
-            }
-
-            return ValidateUtils.checkNotNull(kexFutureHolder.get(), "No current KEX future");
-        } else {
-            throw new SshException("In flight key exchange");
+        synchronized (lock) {
+            lock.notifyAll();
         }
+        super.sendSessionEvent(event);
     }
 
-    @Override
-    public ChannelShell createShellChannel() throws IOException {
-        if ((inCipher instanceof CipherNone) || (outCipher instanceof CipherNone)) {
-            throw new IllegalStateException("Interactive channels are not supported with none cipher");
+    protected void sendInitialServiceRequest() throws IOException {
+        if (initialServiceRequestSent) {
+            return;
         }
-        ChannelShell channel = new ChannelShell();
-        ConnectionService service = getConnectionService();
-        int id = service.registerChannel(channel);
+        initialServiceRequestSent = true;
+        String serviceName = currentServiceFactory.getName();
         if (log.isDebugEnabled()) {
-            log.debug("createShellChannel({}) created id={}", this, id);
+            log.debug("sendInitialServiceRequest({}) Send SSH_MSG_SERVICE_REQUEST for {}", this, serviceName);
         }
-        return channel;
+
+        Buffer request = createBuffer(SshConstants.SSH_MSG_SERVICE_REQUEST, serviceName.length() + Byte.SIZE);
+        request.putString(serviceName);
+        writePacket(request);
+        // Assuming that MINA-SSHD only implements "explicit server authentication" it is permissible
+        // for the client's service to start sending data before the service-accept has been received.
+        // If "implicit authentication" were to ever be supported, then this would need to be
+        // called after service-accept comes back.  See SSH-TRANSPORT.
+        currentService.start();
     }
 
     @Override
@@ -335,83 +285,6 @@ public class ClientSessionImpl extends AbstractClientSession {
     }
 
     @Override
-    protected boolean readIdentification(Buffer buffer) throws IOException {
-        serverVersion = doReadIdentification(buffer, false);
-        if (serverVersion == null) {
-            return false;
-        }
-
-        if (log.isDebugEnabled()) {
-            log.debug("readIdentification({}) Server version string: {}", this, serverVersion);
-        }
-
-        if (!(serverVersion.startsWith(DEFAULT_SSH_VERSION_PREFIX) || serverVersion.startsWith("SSH-1.99-"))) {
-            throw new SshException(SshConstants.SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED,
-                    "Unsupported protocol version: " + serverVersion);
-        }
-
-        return true;
-    }
-
-    @Override
-    protected byte[] sendKexInit(Map<KexProposalOption, String> proposal) throws IOException {
-        mergeProposals(clientProposal, proposal);
-        return super.sendKexInit(proposal);
-    }
-
-    @Override
-    protected void setKexSeed(byte... seed) {
-        i_c = ValidateUtils.checkNotNullAndNotEmpty(seed, "No KEX seed");
-    }
-
-    @Override
-    protected void receiveKexInit(Map<KexProposalOption, String> proposal, byte[] seed) throws IOException {
-        mergeProposals(serverProposal, proposal);
-        i_s = seed;
-    }
-
-    @Override
-    protected void checkKeys() throws SshException {
-        ServerKeyVerifier serverKeyVerifier = ValidateUtils.checkNotNull(getServerKeyVerifier(), "No server key verifier");
-        SocketAddress remoteAddress = ioSession.getRemoteAddress();
-
-        if (!serverKeyVerifier.verifyServerKey(this, remoteAddress, kex.getServerKey())) {
-            throw new SshException(SshConstants.SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE, "Server key did not validate");
-        }
-    }
-
-    @Override
-    protected void sendSessionEvent(SessionListener.Event event) throws IOException {
-        if (SessionListener.Event.KeyEstablished.equals(event)) {
-            sendInitialServiceRequest();
-        }
-        synchronized (lock) {
-            lock.notifyAll();
-        }
-        super.sendSessionEvent(event);
-    }
-
-    protected void sendInitialServiceRequest() throws IOException {
-        if (initialServiceRequestSent) {
-            return;
-        }
-        initialServiceRequestSent = true;
-        String serviceName = currentServiceFactory.getName();
-        if (log.isDebugEnabled()) {
-            log.debug("sendInitialServiceRequest({}) Send SSH_MSG_SERVICE_REQUEST for {}", this, serviceName);
-        }
-
-        Buffer request = createBuffer(SshConstants.SSH_MSG_SERVICE_REQUEST, serviceName.length() + Byte.SIZE);
-        request.putString(serviceName);
-        writePacket(request);
-        // Assuming that MINA-SSHD only implements "explicit server authentication" it is permissible
-        // for the client's service to start sending data before the service-accept has been received.
-        // If "implicit authentication" were to ever be supported, then this would need to be
-        // called after service-accept comes back.  See SSH-TRANSPORT.
-        currentService.start();
-    }
-
-    @Override
     public Map<Object, Object> getMetadataMap() {
         return metadataMap;
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/4c9d3aeb/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java b/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java
index 1627cf0..4b6488f 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java
@@ -1976,6 +1976,13 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
             listener.sessionEvent(this, event);
         } catch (Throwable e) {
             Throwable t = GenericUtils.peelException(e);
+            if (log.isDebugEnabled()) {
+                log.debug("sendSessionEvent({})[{}] failed ({}) to inform listeners: {}",
+                           this, event, t.getClass().getSimpleName(), t.getMessage());
+            }
+            if (log.isTraceEnabled()) {
+                log.trace("sendSessionEvent(" + this + ")[" + event + "] listener inform details", t);
+            }
             if (t instanceof IOException) {
                 throw (IOException) t;
             } else if (t instanceof RuntimeException) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/4c9d3aeb/sshd-core/src/main/java/org/apache/sshd/server/session/AbstractServerSession.java
----------------------------------------------------------------------
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 1008ec4..137827b 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
@@ -19,12 +19,33 @@
 
 package org.apache.sshd.server.session;
 
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.security.KeyPair;
+import java.util.Collection;
 import java.util.List;
+import java.util.Map;
+import java.util.Objects;
 
+import org.apache.sshd.common.FactoryManager;
 import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.RuntimeSshException;
+import org.apache.sshd.common.ServiceFactory;
+import org.apache.sshd.common.SshConstants;
+import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.future.SshFutureListener;
+import org.apache.sshd.common.io.IoService;
 import org.apache.sshd.common.io.IoSession;
 import org.apache.sshd.common.io.IoWriteFuture;
+import org.apache.sshd.common.kex.KexProposalOption;
+import org.apache.sshd.common.kex.KexState;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
 import org.apache.sshd.common.session.helpers.AbstractSession;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
 import org.apache.sshd.server.ServerFactoryManager;
 import org.apache.sshd.server.auth.UserAuth;
 import org.apache.sshd.server.auth.gss.GSSAuthenticator;
@@ -34,6 +55,8 @@ import org.apache.sshd.server.auth.password.PasswordAuthenticator;
 import org.apache.sshd.server.auth.pubkey.PublickeyAuthenticator;
 
 /**
+ * Provides default implementations for {@link ServerSession} related methods
+ *
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
 public abstract class AbstractServerSession extends AbstractSession implements ServerSession {
@@ -122,4 +145,197 @@ public abstract class AbstractServerSession extends AbstractSession implements S
     protected void checkKeys() {
         // nothing
     }
+
+    @Override
+    public void startService(String name) throws Exception {
+        currentService = ServiceFactory.Utils.create(
+                        getFactoryManager().getServiceFactories(),
+                        ValidateUtils.checkNotNullAndNotEmpty(name, "No service name"),
+                        this);
+        /*
+         * According to RFC4253:
+         *
+         *      If the server rejects the service request, it SHOULD send an
+         *      appropriate SSH_MSG_DISCONNECT message and MUST disconnect.
+         */
+        if (currentService == null) {
+            throw new SshException(SshConstants.SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE, "Unknown service: " + name);
+        }
+    }
+
+    @Override
+    protected void serviceAccept() throws IOException {
+        // TODO: can services be initiated by the server-side ?
+        disconnect(SshConstants.SSH2_DISCONNECT_PROTOCOL_ERROR, "Unsupported packet: SSH_MSG_SERVICE_ACCEPT");
+    }
+
+    @Override
+    protected byte[] sendKexInit(Map<KexProposalOption, String> proposal) throws IOException {
+        mergeProposals(serverProposal, proposal);
+        return super.sendKexInit(proposal);
+    }
+
+    @Override
+    protected void setKexSeed(byte... seed) {
+        i_s = ValidateUtils.checkNotNullAndNotEmpty(seed, "No KEX seed");
+    }
+
+    @Override
+    protected String resolveAvailableSignaturesProposal(FactoryManager proposedManager) {
+        /*
+         * Make sure we can provide key(s) for the available signatures
+         */
+        ValidateUtils.checkTrue(proposedManager == getFactoryManager(), "Mismatched signatures proposed factory manager");
+
+        KeyPairProvider kpp = getKeyPairProvider();
+        Collection<String> supported = NamedResource.Utils.getNameList(getSignatureFactories());
+        Iterable<String> provided;
+        try {
+            provided = (kpp == null) ? null : kpp.getKeyTypes();
+        } catch (Error e) {
+            log.warn("resolveAvailableSignaturesProposal({}) failed ({}) to get key types: {}",
+                     this, e.getClass().getSimpleName(), e.getMessage());
+            if (log.isDebugEnabled()) {
+                log.debug("resolveAvailableSignaturesProposal(" + this + ") fetch key types failure details", e);
+            }
+
+            throw new RuntimeSshException(e);
+        }
+
+        if ((provided == null) || GenericUtils.isEmpty(supported)) {
+            return resolveEmptySignaturesProposal(supported, provided);
+        }
+
+        StringBuilder resolveKeys = null;
+        for (String keyType : provided) {
+            if (!supported.contains(keyType)) {
+                if (log.isDebugEnabled()) {
+                    log.debug("resolveAvailableSignaturesProposal({})[{}] {} not in suppored list: {}",
+                              this, provided, keyType, supported);
+                }
+                continue;
+            }
+
+            if (resolveKeys == null) {
+                resolveKeys = new StringBuilder(supported.size() * 16 /* ecdsa-sha2-xxxx */);
+            }
+
+            if (resolveKeys.length() > 0) {
+                resolveKeys.append(',');
+            }
+
+            resolveKeys.append(keyType);
+        }
+
+        if (GenericUtils.isEmpty(resolveKeys)) {
+            return resolveEmptySignaturesProposal(supported, provided);
+        } else {
+            return resolveKeys.toString();
+        }
+    }
+
+    /**
+     * Called by {@link #resolveAvailableSignaturesProposal(FactoryManager)}
+     * if none of the provided keys is supported - last chance for the derived
+     * implementation to do something
+     *
+     * @param supported The supported key types - may be {@code null}/empty
+     * @param provided  The available signature types - may be {@code null}/empty
+     * @return The resolved proposal - {@code null} by default
+     */
+    protected String resolveEmptySignaturesProposal(Iterable<String> supported, Iterable<String> provided) {
+        if (log.isDebugEnabled()) {
+            log.debug("resolveEmptySignaturesProposal({})[{}] none of the keys appears in supported list: {}",
+                      this, provided, supported);
+        }
+        return null;
+    }
+
+    @Override
+    protected boolean readIdentification(Buffer buffer) throws IOException {
+        clientVersion = doReadIdentification(buffer, true);
+        if (GenericUtils.isEmpty(clientVersion)) {
+            return false;
+        }
+
+        if (log.isDebugEnabled()) {
+            log.debug("readIdentification({}) client version string: {}", this, clientVersion);
+        }
+
+        if (!clientVersion.startsWith(DEFAULT_SSH_VERSION_PREFIX)) {
+            String msg = "Unsupported protocol version: " + clientVersion;
+            ioSession.write(new ByteArrayBuffer((msg + "\n").getBytes(StandardCharsets.UTF_8)))
+                     .addListener(new SshFutureListener<IoWriteFuture>() {
+                         @Override
+                         public void operationComplete(IoWriteFuture future) {
+                             close(true);
+                         }
+                     });
+            throw new SshException(msg);
+        } else {
+            kexState.set(KexState.INIT);
+            sendKexInit();
+        }
+        return true;
+    }
+
+    @Override
+    protected void receiveKexInit(Map<KexProposalOption, String> proposal, byte[] seed) throws IOException {
+        mergeProposals(clientProposal, proposal);
+        i_c = seed;
+    }
+
+    @Override
+    public KeyPair getHostKey() {
+        String keyType = getNegotiatedKexParameter(KexProposalOption.SERVERKEYS);
+        KeyPairProvider provider = ValidateUtils.checkNotNull(getKeyPairProvider(), "No host keys provider");
+        try {
+            return provider.loadKey(keyType);
+        } catch (Error e) {
+            log.warn("getHostKey({}) failed ({}) to load key of type={}: {}",
+                     this, e.getClass().getSimpleName(), keyType, e.getMessage());
+            if (log.isDebugEnabled()) {
+                log.debug("getHostKey(" + this + ") " + keyType + " key load failure details", e);
+            }
+
+            throw new RuntimeSshException(e);
+        }
+    }
+
+    @Override
+    public int getActiveSessionCountForUser(String userName) {
+        if (GenericUtils.isEmpty(userName)) {
+            return 0;
+        }
+
+        IoService service = ioSession.getService();
+        Map<?, IoSession> sessionsMap = service.getManagedSessions();
+        if (GenericUtils.isEmpty(sessionsMap)) {
+            return 0;
+        }
+
+        int totalCount = 0;
+        for (IoSession is : sessionsMap.values()) {
+            ServerSession session = (ServerSession) getSession(is, true);
+            if (session == null) {
+                continue;
+            }
+
+            String sessionUser = session.getUsername();
+            if ((!GenericUtils.isEmpty(sessionUser)) && Objects.equals(sessionUser, userName)) {
+                totalCount++;
+            }
+        }
+
+        return totalCount;
+    }
+
+    /**
+     * Returns the session id.
+     *
+     * @return The session id.
+     */
+    public long getId() {
+        return ioSession.getId();
+    }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/4c9d3aeb/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSessionImpl.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSessionImpl.java b/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSessionImpl.java
index ea8d658..2b3f54a 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSessionImpl.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSessionImpl.java
@@ -18,35 +18,14 @@
  */
 package org.apache.sshd.server.session;
 
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.security.KeyPair;
-import java.util.Collection;
-import java.util.Map;
-import java.util.Objects;
-
-import org.apache.sshd.common.FactoryManager;
-import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.RuntimeSshException;
-import org.apache.sshd.common.ServiceFactory;
-import org.apache.sshd.common.SshConstants;
-import org.apache.sshd.common.SshException;
-import org.apache.sshd.common.future.SshFutureListener;
-import org.apache.sshd.common.io.IoService;
 import org.apache.sshd.common.io.IoSession;
-import org.apache.sshd.common.io.IoWriteFuture;
-import org.apache.sshd.common.kex.KexProposalOption;
-import org.apache.sshd.common.kex.KexState;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
 import org.apache.sshd.common.session.SessionListener;
 import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.buffer.Buffer;
-import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
 import org.apache.sshd.server.ServerFactoryManager;
 
 /**
- * TODO Add javadoc
+ * The default implementation for a {@link ServerSession}
  *
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
@@ -64,6 +43,13 @@ public class ServerSessionImpl extends AbstractServerSession {
             listener.sessionCreated(this);
         } catch (Throwable t) {
             Throwable e = GenericUtils.peelException(t);
+            if (log.isDebugEnabled()) {
+                log.debug("Failed ({}) to announce session={} created: {}",
+                          e.getClass().getSimpleName(), ioSession, e.getMessage());
+            }
+            if (log.isTraceEnabled()) {
+                log.trace("Session=" + ioSession + " creation failure details", e);
+            }
             if (e instanceof Exception) {
                 throw (Exception) e;
             } else {
@@ -73,197 +59,4 @@ public class ServerSessionImpl extends AbstractServerSession {
 
         sendServerIdentification();
     }
-
-    @Override
-    public void startService(String name) throws Exception {
-        currentService = ServiceFactory.Utils.create(
-                        getFactoryManager().getServiceFactories(),
-                        ValidateUtils.checkNotNullAndNotEmpty(name, "No service name"),
-                        this);
-        /*
-         * According to RFC4253:
-         *
-         *      If the server rejects the service request, it SHOULD send an
-         *      appropriate SSH_MSG_DISCONNECT message and MUST disconnect.
-         */
-        if (currentService == null) {
-            throw new SshException(SshConstants.SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE, "Unknown service: " + name);
-        }
-    }
-
-    @Override
-    protected void serviceAccept() throws IOException {
-        // TODO: can services be initiated by the server-side ?
-        disconnect(SshConstants.SSH2_DISCONNECT_PROTOCOL_ERROR, "Unsupported packet: SSH_MSG_SERVICE_ACCEPT");
-    }
-
-    @Override
-    protected byte[] sendKexInit(Map<KexProposalOption, String> proposal) throws IOException {
-        mergeProposals(serverProposal, proposal);
-        return super.sendKexInit(proposal);
-    }
-
-    @Override
-    protected void setKexSeed(byte... seed) {
-        i_s = ValidateUtils.checkNotNullAndNotEmpty(seed, "No KEX seed");
-    }
-
-    @Override
-    protected String resolveAvailableSignaturesProposal(FactoryManager proposedManager) {
-        /*
-         * Make sure we can provide key(s) for the available signatures
-         */
-        ValidateUtils.checkTrue(proposedManager == getFactoryManager(), "Mismatched signatures proposed factory manager");
-
-        KeyPairProvider kpp = getKeyPairProvider();
-        Collection<String> supported = NamedResource.Utils.getNameList(getSignatureFactories());
-        Iterable<String> provided;
-        try {
-            provided = (kpp == null) ? null : kpp.getKeyTypes();
-        } catch (Error e) {
-            log.warn("resolveAvailableSignaturesProposal({}) failed ({}) to get key types: {}",
-                     this, e.getClass().getSimpleName(), e.getMessage());
-            if (log.isDebugEnabled()) {
-                log.debug("resolveAvailableSignaturesProposal(" + this + ") fetch key types failure details", e);
-            }
-
-            throw new RuntimeSshException(e);
-        }
-
-        if ((provided == null) || GenericUtils.isEmpty(supported)) {
-            return resolveEmptySignaturesProposal(supported, provided);
-        }
-
-        StringBuilder resolveKeys = null;
-        for (String keyType : provided) {
-            if (!supported.contains(keyType)) {
-                if (log.isDebugEnabled()) {
-                    log.debug("resolveAvailableSignaturesProposal({})[{}] {} not in suppored list: {}",
-                              this, provided, keyType, supported);
-                }
-                continue;
-            }
-
-            if (resolveKeys == null) {
-                resolveKeys = new StringBuilder(supported.size() * 16 /* ecdsa-sha2-xxxx */);
-            }
-
-            if (resolveKeys.length() > 0) {
-                resolveKeys.append(',');
-            }
-
-            resolveKeys.append(keyType);
-        }
-
-        if (GenericUtils.isEmpty(resolveKeys)) {
-            return resolveEmptySignaturesProposal(supported, provided);
-        } else {
-            return resolveKeys.toString();
-        }
-    }
-
-    /**
-     * Called by {@link #resolveAvailableSignaturesProposal(FactoryManager)}
-     * if none of the provided keys is supported - last chance for the derived
-     * implementation to do something
-     *
-     * @param supported The supported key types - may be {@code null}/empty
-     * @param provided  The available signature types - may be {@code null}/empty
-     * @return The resolved proposal - {@code null} by default
-     */
-    protected String resolveEmptySignaturesProposal(Iterable<String> supported, Iterable<String> provided) {
-        if (log.isDebugEnabled()) {
-            log.debug("resolveEmptySignaturesProposal({})[{}] none of the keys appears in supported list: {}",
-                      this, provided, supported);
-        }
-        return null;
-    }
-
-    @Override
-    protected boolean readIdentification(Buffer buffer) throws IOException {
-        clientVersion = doReadIdentification(buffer, true);
-        if (GenericUtils.isEmpty(clientVersion)) {
-            return false;
-        }
-
-        if (log.isDebugEnabled()) {
-            log.debug("readIdentification({}) client version string: {}", this, clientVersion);
-        }
-
-        if (!clientVersion.startsWith(DEFAULT_SSH_VERSION_PREFIX)) {
-            String msg = "Unsupported protocol version: " + clientVersion;
-            ioSession.write(new ByteArrayBuffer((msg + "\n").getBytes(StandardCharsets.UTF_8)))
-                     .addListener(new SshFutureListener<IoWriteFuture>() {
-                         @Override
-                         public void operationComplete(IoWriteFuture future) {
-                             close(true);
-                         }
-                     });
-            throw new SshException(msg);
-        } else {
-            kexState.set(KexState.INIT);
-            sendKexInit();
-        }
-        return true;
-    }
-
-    @Override
-    protected void receiveKexInit(Map<KexProposalOption, String> proposal, byte[] seed) throws IOException {
-        mergeProposals(clientProposal, proposal);
-        i_c = seed;
-    }
-
-    @Override
-    public KeyPair getHostKey() {
-        String keyType = getNegotiatedKexParameter(KexProposalOption.SERVERKEYS);
-        KeyPairProvider provider = ValidateUtils.checkNotNull(getKeyPairProvider(), "No host keys provider");
-        try {
-            return provider.loadKey(keyType);
-        } catch (Error e) {
-            log.warn("getHostKey({}) failed ({}) to load key of type={}: {}",
-                     this, e.getClass().getSimpleName(), keyType, e.getMessage());
-            if (log.isDebugEnabled()) {
-                log.debug("getHostKey(" + this + ") " + keyType + " key load failure details", e);
-            }
-
-            throw new RuntimeSshException(e);
-        }
-    }
-
-    @Override
-    public int getActiveSessionCountForUser(String userName) {
-        if (GenericUtils.isEmpty(userName)) {
-            return 0;
-        }
-
-        IoService service = ioSession.getService();
-        Map<?, IoSession> sessionsMap = service.getManagedSessions();
-        if (GenericUtils.isEmpty(sessionsMap)) {
-            return 0;
-        }
-
-        int totalCount = 0;
-        for (IoSession is : sessionsMap.values()) {
-            ServerSession session = (ServerSession) getSession(is, true);
-            if (session == null) {
-                continue;
-            }
-
-            String sessionUser = session.getUsername();
-            if ((!GenericUtils.isEmpty(sessionUser)) && Objects.equals(sessionUser, userName)) {
-                totalCount++;
-            }
-        }
-
-        return totalCount;
-    }
-
-    /**
-     * Returns the session id.
-     *
-     * @return The session id.
-     */
-    public long getId() {
-        return ioSession.getId();
-    }
 }