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 2014/01/29 12:02:15 UTC

[5/5] git commit: [SSHD-282] Abstract the SSH Service to make it more pluggable

[SSHD-282] Abstract the SSH Service to make it more pluggable

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

Branch: refs/heads/master
Commit: 03aa3979d47b92ad7b01fcfe1e2833a050b7118d
Parents: 1ea3dfe
Author: Guillaume Nodet <gn...@apache.org>
Authored: Wed Jan 29 12:01:53 2014 +0100
Committer: Guillaume Nodet <gn...@apache.org>
Committed: Wed Jan 29 12:01:53 2014 +0100

----------------------------------------------------------------------
 .../main/java/org/apache/sshd/SshClient.java    |   8 +
 .../main/java/org/apache/sshd/SshServer.java    |   8 +
 .../org/apache/sshd/agent/SshAgentFactory.java  |  11 +-
 .../sshd/agent/common/AgentForwardSupport.java  |   9 +-
 .../sshd/agent/local/AgentServerProxy.java      |   9 +-
 .../agent/local/ChannelAgentForwarding.java     |   2 +-
 .../sshd/agent/local/LocalAgentFactory.java     |   6 +-
 .../sshd/agent/local/ProxyAgentFactory.java     |  11 +-
 .../sshd/agent/unix/AgentServerProxy.java       |   9 +-
 .../sshd/agent/unix/UnixAgentFactory.java       |  11 +-
 .../apache/sshd/client/auth/UserAuthAgent.java  |   5 +-
 .../client/session/ClientConnectionService.java | 209 +++++++++
 .../sshd/client/session/ClientSessionImpl.java  | 387 ++++-------------
 .../client/session/ClientUserAuthService.java   | 201 +++++++++
 .../sshd/common/AbstractFactoryManager.java     |   8 +
 .../java/org/apache/sshd/common/Channel.java    |   3 +-
 .../org/apache/sshd/common/FactoryManager.java  |   7 +
 .../java/org/apache/sshd/common/Service.java    |  53 +++
 .../org/apache/sshd/common/ServiceFactory.java  |  58 +++
 .../java/org/apache/sshd/common/Session.java    |  27 +-
 .../sshd/common/TcpipForwarderFactory.java      |   6 +-
 .../sshd/common/channel/AbstractChannel.java    |   7 +-
 .../common/forward/DefaultTcpipForwarder.java   |  11 +-
 .../forward/DefaultTcpipForwarderFactory.java   |   5 +-
 .../sshd/common/forward/TcpipServerChannel.java |   2 +-
 .../session/AbstractConnectionService.java      | 271 ++++++++++++
 .../sshd/common/session/AbstractSession.java    | 133 ++----
 .../sshd/common/session/ConnectionService.java  |  63 +++
 .../sshd/server/channel/ChannelSession.java     |   4 +-
 .../server/session/ServerConnectionService.java | 232 ++++++++++
 .../sshd/server/session/ServerSession.java      | 419 ++-----------------
 .../server/session/ServerUserAuthService.java   | 261 ++++++++++++
 .../sshd/server/x11/X11ForwardSupport.java      |  13 +-
 33 files changed, 1618 insertions(+), 851 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/03aa3979/sshd-core/src/main/java/org/apache/sshd/SshClient.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/SshClient.java b/sshd-core/src/main/java/org/apache/sshd/SshClient.java
index 410ae51..8d4e169 100644
--- a/sshd-core/src/main/java/org/apache/sshd/SshClient.java
+++ b/sshd-core/src/main/java/org/apache/sshd/SshClient.java
@@ -51,7 +51,9 @@ import org.apache.sshd.client.kex.ECDHP256;
 import org.apache.sshd.client.kex.ECDHP384;
 import org.apache.sshd.client.kex.ECDHP521;
 import org.apache.sshd.client.keyverifier.AcceptAllServerKeyVerifier;
+import org.apache.sshd.client.session.ClientConnectionService;
 import org.apache.sshd.client.session.ClientSessionImpl;
+import org.apache.sshd.client.session.ClientUserAuthService;
 import org.apache.sshd.common.AbstractFactoryManager;
 import org.apache.sshd.common.Channel;
 import org.apache.sshd.common.Cipher;
@@ -216,6 +218,12 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
         if (getIoServiceFactory() == null) {
             setIoServiceFactory(new DefaultIoServiceFactory());
         }
+        if (getServiceFactories() == null) {
+            setServiceFactories(Arrays.asList(
+                    new ClientUserAuthService.Factory(),
+                    new ClientConnectionService.Factory()
+            ));
+        }
     }
 
     public void start() {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/03aa3979/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 eaf27ea..56e84cd 100644
--- a/sshd-core/src/main/java/org/apache/sshd/SshServer.java
+++ b/sshd-core/src/main/java/org/apache/sshd/SshServer.java
@@ -105,7 +105,9 @@ import org.apache.sshd.server.kex.ECDHP384;
 import org.apache.sshd.server.kex.ECDHP521;
 import org.apache.sshd.server.keyprovider.PEMGeneratorHostKeyProvider;
 import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
+import org.apache.sshd.server.session.ServerConnectionService;
 import org.apache.sshd.server.session.ServerSession;
+import org.apache.sshd.server.session.ServerUserAuthService;
 import org.apache.sshd.server.session.SessionFactory;
 import org.apache.sshd.server.sftp.SftpSubsystem;
 import org.apache.sshd.server.session.ServerSessionTimeoutListener;
@@ -298,6 +300,12 @@ public class SshServer extends AbstractFactoryManager implements ServerFactoryMa
         if (getIoServiceFactory() == null) {
             setIoServiceFactory(new DefaultIoServiceFactory());
         }
+        if (getServiceFactories() == null) {
+            setServiceFactories(Arrays.asList(
+                    new ServerUserAuthService.Factory(),
+                    new ServerConnectionService.Factory()
+            ));
+        }
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/03aa3979/sshd-core/src/main/java/org/apache/sshd/agent/SshAgentFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/agent/SshAgentFactory.java b/sshd-core/src/main/java/org/apache/sshd/agent/SshAgentFactory.java
index d169d47..ff50ac5 100644
--- a/sshd-core/src/main/java/org/apache/sshd/agent/SshAgentFactory.java
+++ b/sshd-core/src/main/java/org/apache/sshd/agent/SshAgentFactory.java
@@ -21,8 +21,10 @@ package org.apache.sshd.agent;
 import java.io.IOException;
 
 import org.apache.sshd.common.Channel;
+import org.apache.sshd.common.FactoryManager;
 import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.Session;
+import org.apache.sshd.common.session.ConnectionService;
 
 /**
  * The <code>SshAgentFactory</code> is used to communicate with an SshAgent.
@@ -41,19 +43,20 @@ public interface SshAgentFactory {
     /**
      * Create an SshAgent that can be used on the client side by the authentication
      * process to send possible keys.
-     * @param session
+     *
+     * @param manager
      * @return
      */
-    SshAgent createClient(Session session) throws IOException;
+    SshAgent createClient(FactoryManager manager) throws IOException;
 
     /**
      * Create the server side that will be used by other SSH clients.
      * It will usually create a channel that will forward the requests
      * to the original client.
      *
-     * @param session
+     * @param service
      * @return
      */
-    SshAgentServer createServer(Session session) throws IOException;
+    SshAgentServer createServer(ConnectionService service) throws IOException;
 
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/03aa3979/sshd-core/src/main/java/org/apache/sshd/agent/common/AgentForwardSupport.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/agent/common/AgentForwardSupport.java b/sshd-core/src/main/java/org/apache/sshd/agent/common/AgentForwardSupport.java
index 0692b7e..cd879e1 100644
--- a/sshd-core/src/main/java/org/apache/sshd/agent/common/AgentForwardSupport.java
+++ b/sshd-core/src/main/java/org/apache/sshd/agent/common/AgentForwardSupport.java
@@ -22,6 +22,7 @@ import java.io.IOException;
 
 import org.apache.sshd.agent.SshAgentServer;
 import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.session.ConnectionService;
 import org.apache.sshd.server.session.ServerSession;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -33,18 +34,18 @@ public class AgentForwardSupport {
 
     private static final Logger log = LoggerFactory.getLogger(AgentForwardSupport.class);
 
-    private final ServerSession session;
+    private final ConnectionService service;
     private String agentId;
     private SshAgentServer agentServer;
 
-    public AgentForwardSupport(ServerSession session) {
-        this.session = session;
+    public AgentForwardSupport(ConnectionService service) {
+        this.service = service;
     }
 
     public String initialize() throws IOException {
         try {
             if (agentId == null) {
-                agentServer = session.getFactoryManager().getAgentFactory().createServer(session);
+                agentServer = service.getSession().getFactoryManager().getAgentFactory().createServer(service);
                 agentId = agentServer.getId();
             }
             return agentId;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/03aa3979/sshd-core/src/main/java/org/apache/sshd/agent/local/AgentServerProxy.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/agent/local/AgentServerProxy.java b/sshd-core/src/main/java/org/apache/sshd/agent/local/AgentServerProxy.java
index 2630faf..15c23b2 100644
--- a/sshd-core/src/main/java/org/apache/sshd/agent/local/AgentServerProxy.java
+++ b/sshd-core/src/main/java/org/apache/sshd/agent/local/AgentServerProxy.java
@@ -24,6 +24,7 @@ import java.util.UUID;
 import org.apache.sshd.agent.SshAgent;
 import org.apache.sshd.agent.SshAgentServer;
 import org.apache.sshd.client.future.OpenFuture;
+import org.apache.sshd.common.session.ConnectionService;
 import org.apache.sshd.server.session.ServerSession;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -35,18 +36,18 @@ public class AgentServerProxy implements SshAgentServer {
 
     private static final Logger LOG = LoggerFactory.getLogger(AgentServerProxy.class);
 
-    private final ServerSession session;
+    private final ConnectionService service;
     private String id;
 
-    public AgentServerProxy(ServerSession session) throws IOException {
-        this.session = session;
+    public AgentServerProxy(ConnectionService service) throws IOException {
+        this.service = service;
         this.id = UUID.randomUUID().toString();
     }
 
     public SshAgent createClient() throws IOException {
         try {
             AgentForwardedChannel channel = new AgentForwardedChannel();
-            this.session.registerChannel(channel);
+            this.service.registerChannel(channel);
             OpenFuture future = channel.open().await();
             Throwable t = future.getException();
             if (t instanceof Exception) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/03aa3979/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 4903315..bdd5165 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
@@ -66,7 +66,7 @@ public class ChannelAgentForwarding extends AbstractServerChannel {
         final OpenFuture f = new DefaultOpenFuture(this);
         try {
             out = new ChannelOutputStream(this, remoteWindow, log, SshConstants.Message.SSH_MSG_CHANNEL_DATA);
-            agent = session.getFactoryManager().getAgentFactory().createClient(session);
+            agent = session.getFactoryManager().getAgentFactory().createClient(session.getFactoryManager());
             client = new AgentClient();
             f.setOpened();
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/03aa3979/sshd-core/src/main/java/org/apache/sshd/agent/local/LocalAgentFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/agent/local/LocalAgentFactory.java b/sshd-core/src/main/java/org/apache/sshd/agent/local/LocalAgentFactory.java
index 014206f..15c494a 100644
--- a/sshd-core/src/main/java/org/apache/sshd/agent/local/LocalAgentFactory.java
+++ b/sshd-core/src/main/java/org/apache/sshd/agent/local/LocalAgentFactory.java
@@ -25,8 +25,10 @@ import org.apache.sshd.agent.SshAgentFactory;
 import org.apache.sshd.agent.SshAgentServer;
 import org.apache.sshd.agent.common.AgentDelegate;
 import org.apache.sshd.common.Channel;
+import org.apache.sshd.common.FactoryManager;
 import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.Session;
+import org.apache.sshd.common.session.ConnectionService;
 
 public class LocalAgentFactory implements SshAgentFactory {
 
@@ -48,11 +50,11 @@ public class LocalAgentFactory implements SshAgentFactory {
         return new ChannelAgentForwarding.Factory();
     }
 
-    public SshAgent createClient(Session session) throws IOException {
+    public SshAgent createClient(FactoryManager manager) throws IOException {
         return new AgentDelegate(agent);
     }
 
-    public SshAgentServer createServer(Session session) throws IOException {
+    public SshAgentServer createServer(ConnectionService service) throws IOException {
         return null;
     }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/03aa3979/sshd-core/src/main/java/org/apache/sshd/agent/local/ProxyAgentFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/agent/local/ProxyAgentFactory.java b/sshd-core/src/main/java/org/apache/sshd/agent/local/ProxyAgentFactory.java
index 2ae8223..fd918c0 100644
--- a/sshd-core/src/main/java/org/apache/sshd/agent/local/ProxyAgentFactory.java
+++ b/sshd-core/src/main/java/org/apache/sshd/agent/local/ProxyAgentFactory.java
@@ -26,8 +26,10 @@ import org.apache.sshd.agent.SshAgent;
 import org.apache.sshd.agent.SshAgentFactory;
 import org.apache.sshd.agent.SshAgentServer;
 import org.apache.sshd.common.Channel;
+import org.apache.sshd.common.FactoryManager;
 import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.Session;
+import org.apache.sshd.common.session.ConnectionService;
 import org.apache.sshd.server.session.ServerSession;
 
 public class ProxyAgentFactory implements SshAgentFactory {
@@ -38,8 +40,8 @@ public class ProxyAgentFactory implements SshAgentFactory {
         return new ChannelAgentForwarding.Factory();
     }
 
-    public SshAgent createClient(Session session) throws IOException {
-        String proxyId = session.getFactoryManager().getProperties().get(SshAgent.SSH_AUTHSOCKET_ENV_NAME);
+    public SshAgent createClient(FactoryManager manager) throws IOException {
+        String proxyId = manager.getProperties().get(SshAgent.SSH_AUTHSOCKET_ENV_NAME);
         if (proxyId == null) {
             throw new IllegalStateException("No " + SshAgent.SSH_AUTHSOCKET_ENV_NAME + " environment variable set");
         }
@@ -50,11 +52,12 @@ public class ProxyAgentFactory implements SshAgentFactory {
         return proxy.createClient();
     }
 
-    public SshAgentServer createServer(Session session) throws IOException {
+    public SshAgentServer createServer(ConnectionService service) throws IOException {
+        Session session = service.getSession();
         if (!(session instanceof ServerSession)) {
             throw new IllegalStateException("The session used to create an agent server proxy must be a server session");
         }
-        final AgentServerProxy proxy = new AgentServerProxy((ServerSession) session);
+        final AgentServerProxy proxy = new AgentServerProxy(service);
         proxies.put(proxy.getId(), proxy);
         return new SshAgentServer() {
             public String getId() {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/03aa3979/sshd-core/src/main/java/org/apache/sshd/agent/unix/AgentServerProxy.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/agent/unix/AgentServerProxy.java b/sshd-core/src/main/java/org/apache/sshd/agent/unix/AgentServerProxy.java
index f773237..f358740 100644
--- a/sshd-core/src/main/java/org/apache/sshd/agent/unix/AgentServerProxy.java
+++ b/sshd-core/src/main/java/org/apache/sshd/agent/unix/AgentServerProxy.java
@@ -23,6 +23,7 @@ import java.io.IOException;
 import org.apache.sshd.agent.SshAgentServer;
 import org.apache.sshd.client.future.OpenFuture;
 import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.session.ConnectionService;
 import org.apache.sshd.server.session.ServerSession;
 import org.apache.tomcat.jni.Local;
 import org.apache.tomcat.jni.Pool;
@@ -38,15 +39,15 @@ public class AgentServerProxy implements SshAgentServer {
 
     private static final Logger LOG = LoggerFactory.getLogger(AgentServerProxy.class);
 
-    private final ServerSession session;
+    private final ConnectionService service;
     private String authSocket;
     private long pool;
     private long handle;
     private Thread thread;
     private boolean closed;
 
-    public AgentServerProxy(ServerSession session) throws IOException {
-        this.session = session;
+    public AgentServerProxy(ConnectionService service) throws IOException {
+        this.service = service;
         try {
             String authSocket = AprLibrary.createLocalSocketAddress();
             pool = Pool.create(AprLibrary.getInstance().getRootPool());
@@ -70,7 +71,7 @@ public class AgentServerProxy implements SshAgentServer {
                             }
                             Socket.timeoutSet(clientSock, 10000000);
                             AgentForwardedChannel channel = new AgentForwardedChannel(clientSock);
-                            AgentServerProxy.this.session.registerChannel(channel);
+                            AgentServerProxy.this.service.registerChannel(channel);
                             OpenFuture future = channel.open().await();
                             Throwable t = future.getException();
                             if (t instanceof Exception) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/03aa3979/sshd-core/src/main/java/org/apache/sshd/agent/unix/UnixAgentFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/agent/unix/UnixAgentFactory.java b/sshd-core/src/main/java/org/apache/sshd/agent/unix/UnixAgentFactory.java
index f82c95f..87797d3 100644
--- a/sshd-core/src/main/java/org/apache/sshd/agent/unix/UnixAgentFactory.java
+++ b/sshd-core/src/main/java/org/apache/sshd/agent/unix/UnixAgentFactory.java
@@ -24,8 +24,10 @@ import org.apache.sshd.agent.SshAgent;
 import org.apache.sshd.agent.SshAgentFactory;
 import org.apache.sshd.agent.SshAgentServer;
 import org.apache.sshd.common.Channel;
+import org.apache.sshd.common.FactoryManager;
 import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.Session;
+import org.apache.sshd.common.session.ConnectionService;
 import org.apache.sshd.server.session.ServerSession;
 
 public class UnixAgentFactory implements SshAgentFactory {
@@ -34,16 +36,17 @@ public class UnixAgentFactory implements SshAgentFactory {
         return new ChannelAgentForwarding.Factory();
     }
 
-    public SshAgent createClient(Session session) throws IOException {
-        String authSocket = session.getFactoryManager().getProperties().get(SshAgent.SSH_AUTHSOCKET_ENV_NAME);
+    public SshAgent createClient(FactoryManager manager) throws IOException {
+        String authSocket = manager.getProperties().get(SshAgent.SSH_AUTHSOCKET_ENV_NAME);
         SshAgent agent = new AgentClient(authSocket);
         return agent;
     }
 
-    public SshAgentServer createServer(Session session) throws IOException {
+    public SshAgentServer createServer(ConnectionService service) throws IOException {
+        Session session = service.getSession();
         if (!(session instanceof ServerSession)) {
             throw new IllegalStateException("The session used to create an agent server proxy must be a server session");
         }
-        return new AgentServerProxy((ServerSession) session);
+        return new AgentServerProxy(service);
     }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/03aa3979/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthAgent.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthAgent.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthAgent.java
index fa68ff9..5fe5926 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthAgent.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthAgent.java
@@ -39,7 +39,10 @@ public class UserAuthAgent extends AbstractUserAuth {
 
     public UserAuthAgent(ClientSessionImpl session, String service, String username) throws IOException {
         super(session, service, username);
-        this.agent = session.getFactoryManager().getAgentFactory().createClient(session);
+        if (session.getFactoryManager().getAgentFactory() == null) {
+            throw new IllegalStateException("No ssh agent factory has been configured");
+        }
+        this.agent = session.getFactoryManager().getAgentFactory().createClient(session.getFactoryManager());
         this.keys = agent.getIdentities().iterator();
     }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/03aa3979/sshd-core/src/main/java/org/apache/sshd/client/session/ClientConnectionService.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientConnectionService.java b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientConnectionService.java
new file mode 100644
index 0000000..bc9e316
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientConnectionService.java
@@ -0,0 +1,209 @@
+/*
+ * 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.client.session;
+
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.sshd.client.ClientFactoryManager;
+import org.apache.sshd.client.future.OpenFuture;
+import org.apache.sshd.common.Channel;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.Service;
+import org.apache.sshd.common.ServiceFactory;
+import org.apache.sshd.common.Session;
+import org.apache.sshd.common.SshConstants;
+import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.future.SshFutureListener;
+import org.apache.sshd.common.session.AbstractConnectionService;
+import org.apache.sshd.common.util.Buffer;
+import org.apache.sshd.server.channel.OpenChannelException;
+
+/**
+ * Client side <code>ssh-connection</code> service.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class ClientConnectionService extends AbstractConnectionService {
+
+    public static class Factory implements ServiceFactory {
+
+        public String getName() {
+            return "ssh-connection";
+        }
+
+        public Service create(Session session) throws IOException {
+            return new ClientConnectionService(session);
+        }
+    }
+
+    public ClientConnectionService(Session s) throws SshException {
+        super(s);
+        if (!(s instanceof ClientSessionImpl)) {
+            throw new IllegalStateException("Client side service used on server side");
+        }
+    }
+
+    @Override
+    public void start() {
+        if (!((ClientSessionImpl) session).isAuthenticated()) {
+            throw new IllegalStateException("Session is not authenticated");
+        }
+        startHeartBeat();
+    }
+
+    protected void startHeartBeat() {
+        String intervalStr = session.getFactoryManager().getProperties().get(ClientFactoryManager.HEARTBEAT_INTERVAL);
+        try {
+            int interval = intervalStr != null ? Integer.parseInt(intervalStr) : 0;
+            if (interval > 0) {
+                session.getFactoryManager().getScheduledExecutorService().scheduleAtFixedRate(new Runnable() {
+                    public void run() {
+                        sendHeartBeat();
+                    }
+                }, interval, interval, TimeUnit.MILLISECONDS);
+            }
+        } catch (NumberFormatException e) {
+            log.warn("Ignoring bad heartbeat interval: {}", intervalStr);
+        }
+    }
+
+    protected void sendHeartBeat() {
+        try {
+            Buffer buf = session.createBuffer(SshConstants.Message.SSH_MSG_GLOBAL_REQUEST, 0);
+            String request = session.getFactoryManager().getProperties().get(ClientFactoryManager.HEARTBEAT_REQUEST);
+            if (request == null) {
+                request = "keepalive@sshd.apache.org";
+            }
+            buf.putString(request);
+            buf.putBoolean(false);
+            session.writePacket(buf);
+        } catch (IOException e) {
+            log.info("Error sending keepalive message", e);
+        }
+    }
+
+    public void process(SshConstants.Message cmd, Buffer buffer) throws Exception {
+        switch (cmd) {
+            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_FAILURE:
+                channelFailure(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;
+            default:
+                throw new IllegalStateException("Unsupported command: " + cmd);
+        }
+    }
+
+    // TODO: remove from interface
+    public String initAgentForward() throws IOException {
+        throw new IllegalStateException("Server side operation");
+    }
+
+    // TODO: remove from interface
+    public String createX11Display(boolean singleConnection, String authenticationProtocol, String authenticationCookie, int screen) throws IOException {
+        throw new IllegalStateException("Server side operation");
+    }
+
+    private void channelOpen(Buffer buffer) throws Exception {
+        String type = buffer.getString();
+        final int id = buffer.getInt();
+        final int rwsize = buffer.getInt();
+        final int rmpsize = buffer.getInt();
+
+        log.info("Received SSH_MSG_CHANNEL_OPEN {}", type);
+
+        if (closing) {
+            Buffer buf = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_OPEN_FAILURE, 0);
+            buf.putInt(id);
+            buf.putInt(SshConstants.SSH_OPEN_CONNECT_FAILED);
+            buf.putString("SSH server is shutting down: " + type);
+            buf.putString("");
+            session.writePacket(buf);
+            return;
+        }
+
+        final Channel channel = NamedFactory.Utils.create(getSession().getFactoryManager().getChannelFactories(), type);
+        if (channel == null) {
+            Buffer buf = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_OPEN_FAILURE, 0);
+            buf.putInt(id);
+            buf.putInt(SshConstants.SSH_OPEN_UNKNOWN_CHANNEL_TYPE);
+            buf.putString("Unsupported channel type: " + type);
+            buf.putString("");
+            session.writePacket(buf);
+            return;
+        }
+
+        final int channelId = registerChannel(channel);
+        channel.open(id, rwsize, rmpsize, buffer).addListener(new SshFutureListener<OpenFuture>() {
+            public void operationComplete(OpenFuture future) {
+                try {
+                    if (future.isOpened()) {
+                        Buffer buf = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_OPEN_CONFIRMATION, 0);
+                        buf.putInt(id);
+                        buf.putInt(channelId);
+                        buf.putInt(channel.getLocalWindow().getSize());
+                        buf.putInt(channel.getLocalWindow().getPacketSize());
+                        session.writePacket(buf);
+                    } else if (future.getException() != null) {
+                        Buffer buf = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_OPEN_FAILURE, 0);
+                        buf.putInt(id);
+                        if (future.getException() instanceof OpenChannelException) {
+                            buf.putInt(((OpenChannelException)future.getException()).getReasonCode());
+                            buf.putString(future.getException().getMessage());
+                        } else {
+                            buf.putInt(0);
+                            buf.putString("Error opening channel: " + future.getException().getMessage());
+                        }
+                        buf.putString("");
+                        session.writePacket(buf);
+                    }
+                } catch (IOException e) {
+                    session.exceptionCaught(e);
+                }
+            }
+        });
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/03aa3979/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 2d4e68c..ce3e030 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
@@ -22,6 +22,7 @@ import java.io.IOException;
 import java.net.SocketAddress;
 import java.security.KeyPair;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.TimeUnit;
 
@@ -32,7 +33,6 @@ import org.apache.sshd.client.ScpClient;
 import org.apache.sshd.client.ServerKeyVerifier;
 import org.apache.sshd.client.SftpClient;
 import org.apache.sshd.client.UserAuth;
-import org.apache.sshd.client.UserInteraction;
 import org.apache.sshd.client.auth.UserAuthAgent;
 import org.apache.sshd.client.auth.UserAuthKeyboardInteractive;
 import org.apache.sshd.client.auth.UserAuthPassword;
@@ -43,22 +43,19 @@ import org.apache.sshd.client.channel.ChannelShell;
 import org.apache.sshd.client.channel.ChannelSubsystem;
 import org.apache.sshd.client.future.AuthFuture;
 import org.apache.sshd.client.future.DefaultAuthFuture;
-import org.apache.sshd.client.future.OpenFuture;
 import org.apache.sshd.client.scp.DefaultScpClient;
 import org.apache.sshd.client.sftp.DefaultSftpClient;
-import org.apache.sshd.common.Channel;
-import org.apache.sshd.common.KeyExchange;
 import org.apache.sshd.common.KeyPairProvider;
 import org.apache.sshd.common.NamedFactory;
+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.SshdSocketAddress;
-import org.apache.sshd.common.future.CloseFuture;
-import org.apache.sshd.common.future.SshFutureListener;
 import org.apache.sshd.common.io.IoSession;
 import org.apache.sshd.common.session.AbstractSession;
+import org.apache.sshd.common.session.ConnectionService;
 import org.apache.sshd.common.util.Buffer;
-import org.apache.sshd.server.channel.OpenChannelException;
 
 /**
  * TODO Add javadoc
@@ -67,149 +64,83 @@ import org.apache.sshd.server.channel.OpenChannelException;
  */
 public class ClientSessionImpl extends AbstractSession implements ClientSession {
 
-    private static final String AUTHENTICATION_SERVICE = "ssh-connection";
-
-    private UserAuth userAuth;
-    /**
-     * The AuthFuture that is being used by the current auth request.  This encodes the state.
-     * isSuccess -> authenticated, else if isDone -> server waiting for user auth, else authenticating.
-     */
-    private volatile AuthFuture authFuture;
-
     /**
      * For clients to store their own metadata
      */
     private Map<Object, Object> metadataMap = new HashMap<Object, Object>();
 
+    private ServiceFactory currentServiceFactory;
+
+    private Service nextService;
+    private ServiceFactory nextServiceFactory;
+
+    protected AuthFuture authFuture;
+
     public ClientSessionImpl(ClientFactoryManager client, IoSession session) throws Exception {
         super(client, session);
         log.info("Session created...");
+        // 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
+        // set the authPassword.
+        List<ServiceFactory> factories = client.getServiceFactories();
+        if (factories == null || factories.isEmpty() || factories.size() > 2) {
+            throw new IllegalArgumentException("One or two services must be configured");
+        }
+        currentServiceFactory = factories.get(0);
+        currentService = currentServiceFactory.create(this);
+        if (factories.size() > 1) {
+            nextServiceFactory = factories.get(1);
+            nextService = nextServiceFactory.create(this);
+        } else {
+            nextServiceFactory = null;
+        }
+        authFuture = new DefaultAuthFuture(lock);
+        authFuture.setAuthed(false);
         sendClientIdentification();
         sendKexInit();
-        // Maintain the current auth status in the authFuture.
-        authFuture = new DefaultAuthFuture(lock);
     }
 
     public ClientFactoryManager getClientFactoryManager() {
         return (ClientFactoryManager) factoryManager;
     }
 
-    /**
-     * return true if/when ready for auth; false if never ready.
-     * @return server is ready and waiting for auth
-     */
-    private boolean readyForAuth() {
-        // isDone indicates that the last auth finished and a new one can commence.
-        while (!this.authFuture.isDone()) {
-            log.debug("waiting to send authentication");
-            try {
-                this.authFuture.await();
-            } catch (InterruptedException e) {
-                log.debug("Unexpected interrupt", e);
-                throw new RuntimeException(e);
-            }
-        }
-        if (this.authFuture.isSuccess()) {
-            log.debug("already authenticated");
-            throw new IllegalStateException("Already authenticated");
-        }
-        if (this.authFuture.getException() != null) {
-            log.debug("probably closed", this.authFuture.getException());
-            return false;
-        }
-        if (!this.authFuture.isFailure()) {
-            log.debug("unexpected state");
-            throw new IllegalStateException("Unexpected authentication state");
-        }
-        if (this.userAuth != null) {
-            log.debug("authentication already in progress");
-            throw new IllegalStateException("Authentication already in progress?");
-        }
-        log.debug("ready to try authentication with new lock");
-        // The new future !isDone() - i.e., in progress blocking out other waits.
-        this.authFuture = new DefaultAuthFuture(lock);
-        return true;
-    }
-
-    /**
-     * execute one step in user authentication.
-     * @param buffer
-     * @throws IOException
-     */
-    private void processUserAuth(Buffer buffer) throws IOException {
-        log.debug("processing {}", userAuth);
-        switch (userAuth.next(buffer)) {
-            case Success:
-                log.debug("succeeded with {}", userAuth);
-                this.authed = true;
-                this.username = userAuth.getUsername();
-                setState(State.Running);
-                // Will wake up anyone sitting in waitFor
-                authFuture.setAuthed(true);
-                startHeartBeat();
-                break;
-            case Failure:
-                log.debug("failed with {}", userAuth);
-                this.userAuth = null;
-                setState(State.WaitForAuth);
-                // Will wake up anyone sitting in waitFor
-                this.authFuture.setAuthed(false);
-                break;
-            case Continued:
-                // Will wake up anyone sitting in waitFor
-                setState(State.UserAuth);
-                log.debug("continuing with {}", userAuth);
-                break;
-        }
-    }
-
     public AuthFuture authAgent(String user) throws IOException {
-        log.debug("Trying agent authentication");
-        if (getFactoryManager().getAgentFactory() == null) {
-            throw new IllegalStateException("No ssh agent factory has been configured");
-        }
-        synchronized (lock) {
-            if (readyForAuth()) {
-                userAuth = new UserAuthAgent(this, AUTHENTICATION_SERVICE, user);
-                processUserAuth(null);
-            }
-            return authFuture;
-        }
+        return tryAuth(new UserAuthAgent(this, nextServiceName(), user));
     }
 
     public AuthFuture authPassword(String user, String password) throws IOException {
-        log.debug("Trying password authentication");
-        synchronized (lock) {
-            if (readyForAuth()) {
-                userAuth = new UserAuthPassword(this, AUTHENTICATION_SERVICE, user, password);
-                processUserAuth(null);
-            }
-            return authFuture;
-        }
+        return tryAuth(new UserAuthPassword(this, nextServiceName(), user, password));
     }
 
     public AuthFuture authInteractive(String user, String password) throws IOException {
-        log.debug("Trying keyboard-interactive authentication");
-        synchronized (lock) {
-            if (readyForAuth()) {
-                userAuth = new UserAuthKeyboardInteractive(this, AUTHENTICATION_SERVICE, user, password);
-                processUserAuth(null);
-            }
-            return authFuture;
-        }
+        return tryAuth(new UserAuthKeyboardInteractive(this, nextServiceName(), user, password));
    }
 
     public AuthFuture authPublicKey(String user, KeyPair key) throws IOException {
-        log.debug("Trying publickey authentication");
+        return tryAuth(new UserAuthPublicKey(this, nextServiceName(), user, key));
+    }
+
+    private AuthFuture tryAuth(UserAuth auth) throws IOException {
         synchronized (lock) {
-            if (readyForAuth()) {
-                userAuth = new UserAuthPublicKey(this, AUTHENTICATION_SERVICE, user, key);
-                processUserAuth(null);
-            }
-            return authFuture;
+            return authFuture = getUserAuthService().auth(auth);
         }
     }
 
+    private String nextServiceName() {
+        return nextServiceFactory.getName();
+    }
+
+    protected void switchToNextService() throws IOException {
+        if (nextService == null) {
+            throw new IllegalStateException("No service available");
+        }
+        currentServiceFactory = nextServiceFactory;
+        currentService = nextService;
+        nextServiceFactory = null;
+        nextService = null;
+        currentService.start();
+    }
+
     public ClientChannel createChannel(String type) throws IOException {
         return createChannel(type, null);
     }
@@ -228,28 +159,46 @@ public class ClientSessionImpl extends AbstractSession implements ClientSession
 
     public ChannelShell createShellChannel() throws IOException {
         ChannelShell channel = new ChannelShell();
-        registerChannel(channel);
+        getConnectionService().registerChannel(channel);
         return channel;
     }
 
     public ChannelExec createExecChannel(String command) throws IOException {
         ChannelExec channel = new ChannelExec(command);
-        registerChannel(channel);
+        getConnectionService().registerChannel(channel);
         return channel;
     }
 
     public ChannelSubsystem createSubsystemChannel(String subsystem) throws IOException {
         ChannelSubsystem channel = new ChannelSubsystem(subsystem);
-        registerChannel(channel);
+        getConnectionService().registerChannel(channel);
         return channel;
     }
 
     public ChannelDirectTcpip createDirectTcpipChannel(SshdSocketAddress local, SshdSocketAddress remote) throws IOException {
         ChannelDirectTcpip channel = new ChannelDirectTcpip(local, remote);
-        registerChannel(channel);
+        getConnectionService().registerChannel(channel);
         return channel;
     }
 
+    private ClientUserAuthService getUserAuthService() {
+        return findService(ClientUserAuthService.class);
+    }
+
+    private ConnectionService getConnectionService() {
+        return findService(ConnectionService.class);
+    }
+
+    private <T> T findService(Class<T> clazz) {
+        if (clazz.isInstance(currentService)) {
+            return clazz.cast(currentService);
+        }
+        if (clazz.isInstance(nextService)) {
+            return clazz.cast(nextService);
+        }
+        throw new IllegalStateException("Attempted to access unknown service " + clazz.getSimpleName());
+    }
+
     public ScpClient createScpClient() {
         return new DefaultScpClient(this);
     }
@@ -259,29 +208,19 @@ public class ClientSessionImpl extends AbstractSession implements ClientSession
     }
 
     public SshdSocketAddress startLocalPortForwarding(SshdSocketAddress local, SshdSocketAddress remote) throws IOException {
-        return getTcpipForwarder().startLocalPortForwarding(local, remote);
+        return getConnectionService().getTcpipForwarder().startLocalPortForwarding(local, remote);
     }
 
     public void stopLocalPortForwarding(SshdSocketAddress local) throws IOException {
-        getTcpipForwarder().stopLocalPortForwarding(local);
+        getConnectionService().getTcpipForwarder().stopLocalPortForwarding(local);
     }
 
     public SshdSocketAddress startRemotePortForwarding(SshdSocketAddress remote, SshdSocketAddress local) throws IOException {
-        return getTcpipForwarder().startRemotePortForwarding(remote, local);
+        return getConnectionService().getTcpipForwarder().startRemotePortForwarding(remote, local);
     }
 
     public void stopRemotePortForwarding(SshdSocketAddress remote) throws IOException {
-        getTcpipForwarder().stopRemotePortForwarding(remote);
-    }
-
-    @Override
-    public CloseFuture close(boolean immediately) {
-        synchronized (lock) {
-            if (!authFuture.isDone()) {
-                authFuture.setException(new SshException("Session is closed"));
-            }
-            return super.close(immediately);
-        }
+        getConnectionService().getTcpipForwarder().stopRemotePortForwarding(remote);
     }
 
     protected void handleMessage(Buffer buffer) throws Exception {
@@ -344,37 +283,24 @@ public class ClientSessionImpl extends AbstractSession implements ClientSession
                         }
                         log.info("Received SSH_MSG_NEWKEYS");
                         receiveNewKeys(false);
-                        sendAuthRequest();
-                        setState(State.AuthRequestSent);
+                        log.info("Send SSH_MSG_SERVICE_REQUEST for {}", currentServiceFactory.getName());
+                        Buffer request = createBuffer(SshConstants.Message.SSH_MSG_SERVICE_REQUEST, 0);
+                        request.putString(currentServiceFactory.getName());
+                        writePacket(request);
+                        setState(State.ServiceRequestSent);
+                        // 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();
                         break;
-                    case AuthRequestSent:
+                    case ServiceRequestSent:
                         if (cmd != SshConstants.Message.SSH_MSG_SERVICE_ACCEPT) {
                             disconnect(SshConstants.SSH2_DISCONNECT_PROTOCOL_ERROR, "Protocol error: expected packet SSH_MSG_SERVICE_ACCEPT, got " + cmd);
                             return;
                         }
-                        authFuture.setAuthed(false);
-                        setState(State.WaitForAuth);
-                        break;
-                    case WaitForAuth:
-                        // We're waiting for the client to send an authentication request
-                        // TODO: handle unexpected incoming packets
-                        break;
-                    case UserAuth:
-                        if (userAuth == null) {
-                            throw new IllegalStateException("State is userAuth, but no user auth pending!!!");
-                        }
-                        if (cmd == SshConstants.Message.SSH_MSG_USERAUTH_BANNER) {
-                            String welcome = buffer.getString();
-                            String lang = buffer.getString();
-                            log.debug("Welcome banner: {}", welcome);
-                            UserInteraction ui = getClientFactoryManager().getUserInteraction();
-                            if (ui != null) {
-                                ui.welcome(welcome);
-                            }
-                        } else {
-                            buffer.rpos(buffer.rpos() - 1);
-                            processUserAuth(buffer);
-                        }
+                        log.info("Received SSH_MSG_SERVICE_ACCEPT");
+                        setState(State.Running);
                         break;
                     case Running:
                         switch (cmd) {
@@ -384,38 +310,9 @@ public class ClientSessionImpl extends AbstractSession implements ClientSession
                             case SSH_MSG_REQUEST_FAILURE:
                                 requestFailure(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_FAILURE:
-                                channelFailure(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;
                             default:
-                                throw new IllegalStateException("Unsupported command: " + cmd);
+                                currentService.process(cmd, buffer);
+                                break;
                         }
                         break;
                     default:
@@ -472,37 +369,6 @@ public class ClientSessionImpl extends AbstractSession implements ClientSession
         }
     }
 
-    protected void startHeartBeat() {
-        String intervalStr = getClientFactoryManager().getProperties().get(ClientFactoryManager.HEARTBEAT_INTERVAL);
-        try {
-            int interval = intervalStr != null ? Integer.parseInt(intervalStr) : 0;
-            if (interval > 0) {
-                getClientFactoryManager().getScheduledExecutorService().scheduleAtFixedRate(new Runnable() {
-                    public void run() {
-                        sendHeartBeat();
-                    }
-                }, interval, interval, TimeUnit.MILLISECONDS);
-            }
-        } catch (NumberFormatException e) {
-            log.warn("Ignoring bad heartbeat interval: {}", intervalStr);
-        }
-    }
-
-    protected void sendHeartBeat() {
-        try {
-            Buffer buf = createBuffer(SshConstants.Message.SSH_MSG_GLOBAL_REQUEST, 0);
-            String request = getClientFactoryManager().getProperties().get(ClientFactoryManager.HEARTBEAT_REQUEST);
-            if (request == null) {
-                request = "keepalive@sshd.apache.org";
-            }
-            buf.putString(request);
-            buf.putBoolean(false);
-            writePacket(buf);
-        } catch (IOException e) {
-            log.info("Error sending keepalive message", e);
-        }
-    }
-
     protected boolean readIdentification(Buffer buffer) throws IOException {
         serverVersion = doReadIdentification(buffer);
         if (serverVersion == null) {
@@ -540,75 +406,6 @@ public class ClientSessionImpl extends AbstractSession implements ClientSession
         }
     }
 
-    private void sendAuthRequest() throws Exception {
-        log.info("Send SSH_MSG_SERVICE_REQUEST for ssh-userauth");
-        Buffer buffer = createBuffer(SshConstants.Message.SSH_MSG_SERVICE_REQUEST, 0);
-        buffer.putString("ssh-userauth");
-        writePacket(buffer);
-    }
-
-    private void channelOpen(Buffer buffer) throws Exception {
-        String type = buffer.getString();
-        final int id = buffer.getInt();
-        final int rwsize = buffer.getInt();
-        final int rmpsize = buffer.getInt();
-
-        log.info("Received SSH_MSG_CHANNEL_OPEN {}", type);
-
-        if (closing) {
-            Buffer buf = createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_OPEN_FAILURE, 0);
-            buf.putInt(id);
-            buf.putInt(SshConstants.SSH_OPEN_CONNECT_FAILED);
-            buf.putString("SSH server is shutting down: " + type);
-            buf.putString("");
-            writePacket(buf);
-            return;
-        }
-
-        final Channel channel = NamedFactory.Utils.create(getFactoryManager().getChannelFactories(), type);
-        if (channel == null) {
-            Buffer buf = createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_OPEN_FAILURE, 0);
-            buf.putInt(id);
-            buf.putInt(SshConstants.SSH_OPEN_UNKNOWN_CHANNEL_TYPE);
-            buf.putString("Unsupported channel type: " + type);
-            buf.putString("");
-            writePacket(buf);
-            return;
-        }
-
-        final int channelId = getNextChannelId();
-        channels.put(channelId, channel);
-        channel.init(this, channelId);
-        channel.open(id, rwsize, rmpsize, buffer).addListener(new SshFutureListener<OpenFuture>() {
-            public void operationComplete(OpenFuture future) {
-                try {
-                    if (future.isOpened()) {
-                        Buffer buf = createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_OPEN_CONFIRMATION, 0);
-                        buf.putInt(id);
-                        buf.putInt(channelId);
-                        buf.putInt(channel.getLocalWindow().getSize());
-                        buf.putInt(channel.getLocalWindow().getPacketSize());
-                        writePacket(buf);
-                    } else if (future.getException() != null) {
-                        Buffer buf = createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_OPEN_FAILURE, 0);
-                        buf.putInt(id);
-                        if (future.getException() instanceof OpenChannelException) {
-                            buf.putInt(((OpenChannelException)future.getException()).getReasonCode());
-                            buf.putString(future.getException().getMessage());
-                        } else {
-                            buf.putInt(0);
-                            buf.putString("Error opening channel: " + future.getException().getMessage());
-                        }
-                        buf.putString("");
-                        writePacket(buf);
-                    }
-                } catch (IOException e) {
-                    exceptionCaught(e);
-                }
-            }
-        });
-    }
-
 	public Map<Object, Object> getMetadataMap() {
 		return metadataMap;
 	}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/03aa3979/sshd-core/src/main/java/org/apache/sshd/client/session/ClientUserAuthService.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientUserAuthService.java b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientUserAuthService.java
new file mode 100644
index 0000000..9527d34
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientUserAuthService.java
@@ -0,0 +1,201 @@
+/*
+ * 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.client.session;
+
+import java.io.IOException;
+
+import org.apache.sshd.client.UserAuth;
+import org.apache.sshd.client.UserInteraction;
+import org.apache.sshd.client.future.AuthFuture;
+import org.apache.sshd.client.future.DefaultAuthFuture;
+import org.apache.sshd.common.Service;
+import org.apache.sshd.common.ServiceFactory;
+import org.apache.sshd.common.Session;
+import org.apache.sshd.common.SshConstants;
+import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.future.CloseFuture;
+import org.apache.sshd.common.future.DefaultCloseFuture;
+import org.apache.sshd.common.util.Buffer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Client side <code>ssh-auth</code> service.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class ClientUserAuthService implements Service {
+
+    public static class Factory implements ServiceFactory {
+
+        public String getName() {
+            return "ssh-userauth";
+        }
+
+        public Service create(Session session) throws IOException {
+            return new ClientUserAuthService(session);
+        }
+    }
+
+    /** Our logger */
+    protected final Logger log = LoggerFactory.getLogger(getClass());
+
+    /**
+     * When !authFuture.isDone() the current authentication
+     */
+    private UserAuth userAuth;
+
+    /**
+     * The AuthFuture that is being used by the current auth request.  This encodes the state.
+     * isSuccess -> authenticated, else if isDone -> server waiting for user auth, else authenticating.
+     */
+    private volatile AuthFuture authFuture;
+
+    protected final ClientSessionImpl session;
+    protected final Object lock;
+
+    public ClientUserAuthService(Session s) {
+        if (!(s instanceof ClientSessionImpl)) {
+            throw new IllegalStateException("Client side service used on server side");
+        }
+        session = (ClientSessionImpl) s;
+        lock = session.getLock();
+        // Maintain the current auth status in the authFuture.
+        authFuture = new DefaultAuthFuture(lock);
+    }
+
+    public ClientSessionImpl getSession() {
+        return session;
+    }
+
+    public void start() {
+        synchronized (lock) {
+            log.debug("accepted");
+            // kick start the authentication process by failing the pending auth.
+            this.authFuture.setAuthed(false);
+        }
+    }
+
+    public void process(SshConstants.Message cmd, Buffer buffer) throws Exception {
+        if (this.authFuture.isSuccess()) {
+            throw new IllegalStateException("UserAuth message delivered to authenticated client");
+        } else if (this.authFuture.isDone()) {
+            log.debug("Ignoring random message");
+            // ignore for now; TODO: random packets
+        } else if (cmd == SshConstants.Message.SSH_MSG_USERAUTH_BANNER) {
+            String welcome = buffer.getString();
+            String lang = buffer.getString();
+            log.debug("Welcome banner: {}", welcome);
+            UserInteraction ui = session.getClientFactoryManager().getUserInteraction();
+            if (ui != null) {
+                ui.welcome(welcome);
+            }
+        } else {
+            buffer.rpos(buffer.rpos() - 1);
+            processUserAuth(buffer);
+        }
+    }
+
+    /**
+     * return true if/when ready for auth; false if never ready.
+     * @return server is ready and waiting for auth
+     */
+    private boolean readyForAuth(UserAuth userAuth) {
+        // isDone indicates that the last auth finished and a new one can commence.
+        while (!this.authFuture.isDone()) {
+            log.debug("waiting to send authentication");
+            try {
+                this.authFuture.await();
+            } catch (InterruptedException e) {
+                log.debug("Unexpected interrupt", e);
+                throw new RuntimeException(e);
+            }
+        }
+        if (this.authFuture.isSuccess()) {
+            log.debug("already authenticated");
+            throw new IllegalStateException("Already authenticated");
+        }
+        if (this.authFuture.getException() != null) {
+            log.debug("probably closed", this.authFuture.getException());
+            return false;
+        }
+        if (!this.authFuture.isFailure()) {
+            log.debug("unexpected state");
+            throw new IllegalStateException("Unexpected authentication state");
+        }
+        if (this.userAuth != null) {
+            log.debug("authentication already in progress");
+            throw new IllegalStateException("Authentication already in progress?");
+        }
+        // Set up the next round of authentication.  Each round gets a new lock.
+        this.userAuth = userAuth;
+        // The new future !isDone() - i.e., in progress blocking out other waits.
+        this.authFuture = new DefaultAuthFuture(lock);
+        log.debug("ready to try authentication with new lock");
+        return true;
+    }
+
+    /**
+     * execute one step in user authentication.
+     * @param buffer
+     * @throws IOException
+     */
+    private void processUserAuth(Buffer buffer) throws IOException {
+        log.debug("processing {}", userAuth);
+        switch (userAuth.next(buffer)) {
+            case Success:
+                log.debug("succeeded with {}", userAuth);
+                session.setAuthenticated(userAuth.getUsername());
+                session.switchToNextService();
+                // Will wake up anyone sitting in waitFor
+                authFuture.setAuthed(true);
+                break;
+            case Failure:
+                log.debug("failed with {}", userAuth);
+                this.userAuth = null;
+                // Will wake up anyone sitting in waitFor
+                this.authFuture.setAuthed(false);
+                break;
+            case Continued:
+                // Will wake up anyone sitting in waitFor
+                log.debug("continuing with {}", userAuth);
+                break;
+        }
+    }
+
+    public CloseFuture close(boolean immediately) {
+        if (!authFuture.isDone()) {
+            authFuture.setException(new SshException("Session is closed"));
+        }
+        CloseFuture future = new DefaultCloseFuture(lock);
+        future.setClosed();
+        return future;
+    }
+
+    public AuthFuture auth(UserAuth userAuth) throws IOException {
+        log.debug("Trying authentication with {}", userAuth);
+        synchronized (lock) {
+            if (readyForAuth(userAuth)) {
+                processUserAuth(null);
+            }
+            return authFuture;
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/03aa3979/sshd-core/src/main/java/org/apache/sshd/common/AbstractFactoryManager.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/AbstractFactoryManager.java b/sshd-core/src/main/java/org/apache/sshd/common/AbstractFactoryManager.java
index eaa1088..450dce4 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/AbstractFactoryManager.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/AbstractFactoryManager.java
@@ -59,6 +59,7 @@ public abstract class AbstractFactoryManager implements FactoryManager {
     protected TcpipForwarderFactory tcpipForwarderFactory;
     protected ForwardingFilter tcpipForwardingFilter;
     protected FileSystemFactory fileSystemFactory;
+    protected List<ServiceFactory> serviceFactories;
 
     protected AbstractFactoryManager() {
         loadVersion();
@@ -228,4 +229,11 @@ public abstract class AbstractFactoryManager implements FactoryManager {
         this.fileSystemFactory = fileSystemFactory;
     }
 
+    public List<ServiceFactory> getServiceFactories() {
+        return serviceFactories;
+    }
+
+    public void setServiceFactories(List<ServiceFactory> serviceFactories) {
+        this.serviceFactories = serviceFactories;
+    }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/03aa3979/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 6e2c030..aceaef3 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
@@ -23,6 +23,7 @@ import java.io.IOException;
 import org.apache.sshd.client.future.OpenFuture;
 import org.apache.sshd.common.channel.Window;
 import org.apache.sshd.common.future.CloseFuture;
+import org.apache.sshd.common.session.ConnectionService;
 import org.apache.sshd.common.util.Buffer;
 
 /**
@@ -58,7 +59,7 @@ public interface Channel {
 
     CloseFuture close(boolean immediately);
 
-    void init(Session session, int id) throws IOException;
+    void init(ConnectionService service, Session session, int id) throws IOException;
 
     /**
      * For a server channel, this method will actually open the channel

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/03aa3979/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java b/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java
index f5bc47c..86e0155 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java
@@ -177,4 +177,11 @@ public interface FactoryManager {
      */
     FileSystemFactory getFileSystemFactory();
 
+    /**
+     * Retrieve the list of SSH <code>Service</code> factories.
+     *
+     * @return a list of named <code>Service</code> factories, never <code>null</code>
+     */
+    List<ServiceFactory> getServiceFactories();
+
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/03aa3979/sshd-core/src/main/java/org/apache/sshd/common/Service.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/Service.java b/sshd-core/src/main/java/org/apache/sshd/common/Service.java
new file mode 100644
index 0000000..e3f60b3
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/Service.java
@@ -0,0 +1,53 @@
+/*
+ * 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.common;
+
+import java.io.IOException;
+
+import org.apache.sshd.common.future.CloseFuture;
+import org.apache.sshd.common.util.Buffer;
+
+/**
+ * See RFC 4253 [SSH-TRANS] and the SSH_MSG_SERVICE_REQUEST packet.  Examples include ssh-userauth
+ * and ssh-connection but developers are also free to implement their own custom service.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface Service {
+
+    Session getSession();
+
+    // TODO: this is specific to clients
+    void start();
+
+    /**
+     * Service the request.
+     * @param buffer
+     * @throws Exception
+     */
+    void process(SshConstants.Message cmd, Buffer buffer) throws Exception;
+
+    /**
+     * Close the service.
+     * @param immediately
+     *
+     */
+    CloseFuture close(boolean immediately);
+
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/03aa3979/sshd-core/src/main/java/org/apache/sshd/common/ServiceFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/ServiceFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/ServiceFactory.java
new file mode 100644
index 0000000..b70b963
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/ServiceFactory.java
@@ -0,0 +1,58 @@
+/*
+ * 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.common;
+
+import java.io.IOException;
+import java.util.List;
+
+public interface ServiceFactory {
+
+    /**
+     * Name of this factory
+     * @return
+     */
+    String getName();
+
+    Service create(Session session) throws IOException;
+
+    /**
+     * Utility class to help using NamedFactories
+     */
+    public static class Utils {
+
+        /**
+         * Create an instance of the specified name by looking up the needed factory
+         * in the list.
+         *
+         * @param factories list of available factories
+         * @param name the factory name to use
+         * @return a newly created object or <code>null</code> if the factory is not in the list
+         */
+        public static Service create(List<ServiceFactory> factories, String name, Session session) throws IOException {
+            if (factories != null) {
+                for (ServiceFactory f : factories) {
+                    if (f.getName().equals(name)) {
+                        return f.create(session);
+                    }
+                }
+            }
+            return null;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/03aa3979/sshd-core/src/main/java/org/apache/sshd/common/Session.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/Session.java b/sshd-core/src/main/java/org/apache/sshd/common/Session.java
index 52be5cf..cc968e3 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/Session.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/Session.java
@@ -31,7 +31,7 @@ import org.apache.sshd.common.util.Buffer;
 public interface Session {
 
     public enum State {
-        ReceiveKexInit, Kex, ReceiveNewKeys, AuthRequestSent, WaitForAuth, UserAuth, Running, Closed
+        ReceiveKexInit, Kex, ReceiveNewKeys, ServiceRequestSent, WaitForServiceRequest, Running, Closed
     }
 
     /**
@@ -135,27 +135,10 @@ public interface Session {
      * {@link org.apache.sshd.common.SshException}.
      *
      * @param t the exception to process
-     * @throws IOException
      */
     void exceptionCaught(Throwable t);
 
     /**
-     * Register a newly created channel with a new unique identifier
-     *
-     * @param channel the channel to register
-     * @return the id of this channel
-     * @throws Exception
-     */
-    int registerChannel(Channel channel) throws Exception;
-
-    /**
-     * Remove this channel from the list of managed channels
-     *
-     * @param channel the channel
-     */
-    void unregisterChannel(Channel channel);
-
-    /**
      * Add a session |listener|.
      *
      * @param listener the session listener to add
@@ -170,12 +153,6 @@ public interface Session {
     void removeListener(SessionListener listener);
 
     /**
-     * Retrieve the tcpip forwarder
-     * @return
-     */
-    TcpipForwarder getTcpipForwarder();
-
-    /**
      * Type safe key for storage within the user attributes of {@link org.apache.sshd.common.session.AbstractSession}.
      * Typically it is used as a static variable that is shared between the producer
      * and the consumer. To further restrict access the setting or getting it from
@@ -191,7 +168,7 @@ public interface Session {
      *   s.setAttribute(MY_KEY, value);
      * }
      *
-     * @param T type of value stored in the attribute.
+     * @param <T> type of value stored in the attribute.
      *
      * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
      */

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/03aa3979/sshd-core/src/main/java/org/apache/sshd/common/TcpipForwarderFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/TcpipForwarderFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/TcpipForwarderFactory.java
index c19fb5f..1bf6a45 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/TcpipForwarderFactory.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/TcpipForwarderFactory.java
@@ -18,6 +18,8 @@
  */
 package org.apache.sshd.common;
 
+import org.apache.sshd.common.session.ConnectionService;
+
 /**
  * A factory for creating TcpipForwarder objects for client Port forwarding
  */
@@ -27,9 +29,9 @@ public interface TcpipForwarderFactory {
      * Creates the TcpipForwarder to be used for TCP/IP port forwards for
      * this ClientSession.
      *
-     * @param session the Session the connections are forwarded through
+     * @param service the service the connections are forwarded through
      * @return the TcpipForwarder that will listen for connections and set up forwarding
      */
-    public TcpipForwarder create(Session session);
+    public TcpipForwarder create(ConnectionService service);
 
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/03aa3979/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 95ca72f..05b633a 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
@@ -29,6 +29,7 @@ import org.apache.sshd.common.future.CloseFuture;
 import org.apache.sshd.common.future.DefaultCloseFuture;
 import org.apache.sshd.common.future.SshFutureListener;
 import org.apache.sshd.common.io.IoWriteFuture;
+import org.apache.sshd.common.session.ConnectionService;
 import org.apache.sshd.common.util.Buffer;
 import org.apache.sshd.common.util.BufferUtils;
 import org.slf4j.Logger;
@@ -48,6 +49,7 @@ public abstract class AbstractChannel implements Channel {
     protected final Object lock = new Object();
     protected final Window localWindow = new Window(this, null, getClass().getName().contains(".client."), true);
     protected final Window remoteWindow = new Window(this, null, getClass().getName().contains(".client."), false);
+    protected ConnectionService service;
     protected Session session;
     protected int id;
     protected int recipient;
@@ -80,7 +82,8 @@ public abstract class AbstractChannel implements Channel {
         throw new IllegalStateException();
     }
 
-    public void init(Session session, int id) {
+    public void init(ConnectionService service, Session session, int id) {
+        this.service = service;
         this.session = session;
         this.id = id;
         configureWindow();
@@ -101,7 +104,7 @@ public abstract class AbstractChannel implements Channel {
                         postClose();
                         closeFuture.setClosed();
                         notifyStateChanged();
-                        session.unregisterChannel(AbstractChannel.this);
+                        service.unregisterChannel(AbstractChannel.this);
                     }
                 });
             } else {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/03aa3979/sshd-core/src/main/java/org/apache/sshd/common/forward/DefaultTcpipForwarder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/forward/DefaultTcpipForwarder.java b/sshd-core/src/main/java/org/apache/sshd/common/forward/DefaultTcpipForwarder.java
index 640e1a2..45d8612 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/forward/DefaultTcpipForwarder.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/forward/DefaultTcpipForwarder.java
@@ -38,6 +38,7 @@ import org.apache.sshd.common.future.SshFutureListener;
 import org.apache.sshd.common.io.IoAcceptor;
 import org.apache.sshd.common.io.IoHandler;
 import org.apache.sshd.common.io.IoSession;
+import org.apache.sshd.common.session.ConnectionService;
 import org.apache.sshd.common.util.Buffer;
 import org.apache.sshd.common.util.Readable;
 import org.slf4j.Logger;
@@ -52,14 +53,16 @@ public class DefaultTcpipForwarder implements TcpipForwarder, IoHandler {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(DefaultTcpipForwarder.class);
 
+    private final ConnectionService service;
     private final Session session;
     private final Map<Integer, SshdSocketAddress> localToRemote = new HashMap<Integer, SshdSocketAddress>();
     private final Map<Integer, SshdSocketAddress> remoteToLocal = new HashMap<Integer, SshdSocketAddress>();
     private final Set<SshdSocketAddress> localForwards = new HashSet<SshdSocketAddress>();
     protected IoAcceptor acceptor;
 
-    public DefaultTcpipForwarder(Session session) {
-        this.session = session;
+    public DefaultTcpipForwarder(ConnectionService service) {
+        this.service = service;
+        this.session = service.getSession();
     }
 
     //
@@ -174,12 +177,12 @@ public class DefaultTcpipForwarder implements TcpipForwarder, IoHandler {
             channel = new TcpipClientChannel(TcpipClientChannel.Type.Forwarded, session, null);
         }
         session.setAttribute(TcpipClientChannel.class, channel);
-        this.session.registerChannel(channel);
+        this.service.registerChannel(channel);
         channel.open().addListener(new SshFutureListener<OpenFuture>() {
             public void operationComplete(OpenFuture future) {
                 Throwable t = future.getException();
                 if (t != null) {
-                    DefaultTcpipForwarder.this.session.unregisterChannel(channel);
+                    DefaultTcpipForwarder.this.service.unregisterChannel(channel);
                     channel.close(false);
                 }
             }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/03aa3979/sshd-core/src/main/java/org/apache/sshd/common/forward/DefaultTcpipForwarderFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/forward/DefaultTcpipForwarderFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/forward/DefaultTcpipForwarderFactory.java
index 9de1d4a..a732d7e 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/forward/DefaultTcpipForwarderFactory.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/forward/DefaultTcpipForwarderFactory.java
@@ -21,6 +21,7 @@ package org.apache.sshd.common.forward;
 import org.apache.sshd.common.Session;
 import org.apache.sshd.common.TcpipForwarder;
 import org.apache.sshd.common.TcpipForwarderFactory;
+import org.apache.sshd.common.session.ConnectionService;
 
 /**
  * The default {link TcpipForwarderFactory} implementation.
@@ -30,8 +31,8 @@ import org.apache.sshd.common.TcpipForwarderFactory;
  */
 public class DefaultTcpipForwarderFactory implements TcpipForwarderFactory
 {
-   public TcpipForwarder create( Session session )
+   public TcpipForwarder create( ConnectionService service )
    {
-      return new DefaultTcpipForwarder( session );
+      return new DefaultTcpipForwarder( service );
    }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/03aa3979/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 6ba0e7d..2e98811 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
@@ -98,7 +98,7 @@ public class TcpipServerChannel extends AbstractServerChannel {
         SshdSocketAddress address = null;
         switch (type) {
             case Direct:    address = new SshdSocketAddress(hostToConnect, portToConnect); break;
-            case Forwarded: address = getSession().getTcpipForwarder().getForwardedPort(portToConnect); break;
+            case Forwarded: address = service.getTcpipForwarder().getForwardedPort(portToConnect); break;
         }
         final ForwardingFilter filter = getSession().getFactoryManager().getTcpipForwardingFilter();
         if (address == null || filter == null || !filter.canConnect(address, getSession())) {