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 2017/10/13 13:53:08 UTC

mina-sshd git commit: [SSHD-777] Make the code handling channels more error tolerant

Repository: mina-sshd
Updated Branches:
  refs/heads/master 693fa5d90 -> 363cc5201


[SSHD-777] Make the code handling channels more error tolerant


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

Branch: refs/heads/master
Commit: 363cc52018046f8918127d3a169d920cbe45da93
Parents: 693fa5d
Author: Lyor Goldstein <ly...@gmail.com>
Authored: Fri Oct 13 16:26:54 2017 +0300
Committer: Lyor Goldstein <ly...@gmail.com>
Committed: Fri Oct 13 16:54:23 2017 +0300

----------------------------------------------------------------------
 README.md                                       |   7 +-
 .../client/channel/AbstractClientChannel.java   |   3 +-
 .../org/apache/sshd/common/BaseBuilder.java     |  15 ++
 .../org/apache/sshd/common/FactoryManager.java  |   2 +
 .../common/helpers/AbstractFactoryManager.java  |  17 ++
 .../apache/sshd/common/io/nio2/Nio2Session.java |  38 +++--
 .../sshd/common/session/ConnectionService.java  |   7 +-
 .../org/apache/sshd/common/session/Session.java |   1 +
 .../session/UnknownChannelReferenceHandler.java |  46 +++++
 .../UnknownChannelReferenceHandlerManager.java  |  47 ++++++
 .../helpers/AbstractConnectionService.java      | 166 ++++++++++++-------
 .../common/session/helpers/AbstractSession.java |  23 +++
 .../DefaultUnknownChannelReferenceHandler.java  | 103 ++++++++++++
 13 files changed, 392 insertions(+), 83 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/363cc520/README.md
----------------------------------------------------------------------
diff --git a/README.md b/README.md
index 2c29d77..f5783e2 100644
--- a/README.md
+++ b/README.md
@@ -930,6 +930,12 @@ Informs about session related events. One can modify the session - although the
 Informs about channel related events - as with sessions, once can influence the channel to some extent, depending on the channel's **state**. The ability to influence channels is much more limited than sessions. In this context, it is worth mentioning that one can attach to channels **arbitrary attributes** that can be retrieved by the user's code later on - same was as it is done for sessions.
 
 
+### `UnknownChannelReferenceHandler`
+
+
+Invoked whenever a message intended for an unknown channel is received. By default, the code **ignores** the vast majority of such messages and logs them at DEBUG level. For a select few types of messages the code generates an `SSH_CHANNEL_MSG_FAILURE` packet that is sent to the peer session - see `DefaultUnknownChannelReferenceHandler` implementation. The user may register handlers at any level - client/server, session and/or connection service - the one registered "closest" to connection service will be used.
+
+
 ### `SignalListener`
 
 Informs about signal requests as described in [RFC 4254 - section 6.9](https://tools.ietf.org/html/rfc4254#section-6.9), break requests (sent as SIGINT) as described in [RFC 4335](https://tools.ietf.org/html/rfc4335) and "window-change" (sent as SIGWINCH) requests as described in [RFC 4254 - section 6.7](https://tools.ietf.org/html/rfc4254#section-6.7)
@@ -991,7 +997,6 @@ Inform about SCP related events. `ScpTransferEventListener`(s) can be registered
     }
 ```
 
-
 ### Reserved messages
 
 The implementation can be used to intercept and process the [SSH_MSG_IGNORE](https://tools.ietf.org/html/rfc4253#section-11.2), [SSH_MSG_DEBUG](https://tools.ietf.org/html/rfc4253#section-11.3) and [SSH_MSG_UNIMPLEMENTED](https://tools.ietf.org/html/rfc4253#section-11.4) messages. The handler can be registered on either side - server

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/363cc520/sshd-core/src/main/java/org/apache/sshd/client/channel/AbstractClientChannel.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/channel/AbstractClientChannel.java b/sshd-core/src/main/java/org/apache/sshd/client/channel/AbstractClientChannel.java
index 4b61734..437a87a 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/channel/AbstractClientChannel.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/channel/AbstractClientChannel.java
@@ -43,6 +43,7 @@ import org.apache.sshd.common.channel.ChannelAsyncInputStream;
 import org.apache.sshd.common.channel.ChannelAsyncOutputStream;
 import org.apache.sshd.common.channel.RequestHandler;
 import org.apache.sshd.common.channel.Window;
+import org.apache.sshd.common.channel.exception.SshChannelOpenException;
 import org.apache.sshd.common.io.IoInputStream;
 import org.apache.sshd.common.io.IoOutputStream;
 import org.apache.sshd.common.session.Session;
@@ -361,7 +362,7 @@ public abstract class AbstractClientChannel extends AbstractChannel implements C
         this.openFailureReason = reason;
         this.openFailureMsg = msg;
         this.openFailureLang = lang;
-        this.openFuture.setException(new SshException(msg));
+        this.openFuture.setException(new SshChannelOpenException(getId(), reason, msg));
         this.closeFuture.setClosed();
         this.doCloseImmediately();
         notifyStateChanged("SSH_MSG_CHANNEL_OPEN_FAILURE");

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/363cc520/sshd-core/src/main/java/org/apache/sshd/common/BaseBuilder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/BaseBuilder.java b/sshd-core/src/main/java/org/apache/sshd/common/BaseBuilder.java
index d7b1732..338a25f 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/BaseBuilder.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/BaseBuilder.java
@@ -41,6 +41,8 @@ import org.apache.sshd.common.mac.Mac;
 import org.apache.sshd.common.random.Random;
 import org.apache.sshd.common.random.SingletonRandomFactory;
 import org.apache.sshd.common.session.ConnectionService;
+import org.apache.sshd.common.session.UnknownChannelReferenceHandler;
+import org.apache.sshd.common.session.helpers.DefaultUnknownChannelReferenceHandler;
 import org.apache.sshd.common.signature.BuiltinSignatures;
 import org.apache.sshd.common.signature.Signature;
 import org.apache.sshd.common.util.ObjectBuilder;
@@ -130,6 +132,9 @@ public class BaseBuilder<T extends AbstractFactoryManager, S extends BaseBuilder
             BuiltinSignatures.dsa
         ));
 
+    public static final UnknownChannelReferenceHandler DEFAULT_UNKNOWN_CHANNEL_REFERENCE_HANDLER =
+            DefaultUnknownChannelReferenceHandler.INSTANCE;
+
     protected Factory<T> factory;
     protected List<NamedFactory<KeyExchange>> keyExchangeFactories;
     protected List<NamedFactory<Cipher>> cipherFactories;
@@ -143,6 +148,7 @@ public class BaseBuilder<T extends AbstractFactoryManager, S extends BaseBuilder
     protected List<RequestHandler<ConnectionService>> globalRequestHandlers;
     protected ForwardingFilter forwardingFilter;
     protected ChannelStreamPacketWriterResolver channelStreamPacketWriterResolver;
+    protected UnknownChannelReferenceHandler unknownChannelReferenceHandler;
 
     public BaseBuilder() {
         super();
@@ -177,6 +183,10 @@ public class BaseBuilder<T extends AbstractFactoryManager, S extends BaseBuilder
             forwarderFactory = DEFAULT_FORWARDER_FACTORY;
         }
 
+        if (unknownChannelReferenceHandler == null) {
+            unknownChannelReferenceHandler = DEFAULT_UNKNOWN_CHANNEL_REFERENCE_HANDLER;
+        }
+
         return me();
     }
 
@@ -245,6 +255,11 @@ public class BaseBuilder<T extends AbstractFactoryManager, S extends BaseBuilder
         return me();
     }
 
+    public S unknownChannelReferenceHandler(UnknownChannelReferenceHandler handler) {
+        unknownChannelReferenceHandler = handler;
+        return me();
+    }
+
     public T build(boolean isFillWithDefaultValues) {
         if (isFillWithDefaultValues) {
             fillWithDefaultValues();

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/363cc520/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 3aef39b..50f4a54 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
@@ -36,6 +36,7 @@ import org.apache.sshd.common.random.Random;
 import org.apache.sshd.common.session.ConnectionService;
 import org.apache.sshd.common.session.ReservedSessionMessagesManager;
 import org.apache.sshd.common.session.SessionListenerManager;
+import org.apache.sshd.common.session.UnknownChannelReferenceHandlerManager;
 import org.apache.sshd.server.forward.AgentForwardingFilter;
 import org.apache.sshd.server.forward.ForwardingFilter;
 import org.apache.sshd.server.forward.TcpForwardingFilter;
@@ -53,6 +54,7 @@ public interface FactoryManager
                 ReservedSessionMessagesManager,
                 ChannelListenerManager,
                 ChannelStreamPacketWriterResolverManager,
+                UnknownChannelReferenceHandlerManager,
                 PortForwardingEventListenerManager,
                 AttributeStore,
                 PropertyResolver {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/363cc520/sshd-core/src/main/java/org/apache/sshd/common/helpers/AbstractFactoryManager.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/helpers/AbstractFactoryManager.java b/sshd-core/src/main/java/org/apache/sshd/common/helpers/AbstractFactoryManager.java
index 4dcd0d2..276f531 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/helpers/AbstractFactoryManager.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/helpers/AbstractFactoryManager.java
@@ -53,6 +53,7 @@ import org.apache.sshd.common.random.Random;
 import org.apache.sshd.common.session.ConnectionService;
 import org.apache.sshd.common.session.ReservedSessionMessagesHandler;
 import org.apache.sshd.common.session.SessionListener;
+import org.apache.sshd.common.session.UnknownChannelReferenceHandler;
 import org.apache.sshd.common.session.helpers.AbstractSessionFactory;
 import org.apache.sshd.common.session.helpers.SessionTimeoutListener;
 import org.apache.sshd.common.util.EventListenerUtils;
@@ -90,6 +91,7 @@ public abstract class AbstractFactoryManager extends AbstractKexFactoryManager i
     private PropertyResolver parentResolver = SyspropsMapWrapper.SYSPROPS_RESOLVER;
     private ReservedSessionMessagesHandler reservedSessionMessagesHandler;
     private ChannelStreamPacketWriterResolver channelStreamPacketWriterResolver;
+    private UnknownChannelReferenceHandler unknownChannelReferenceHandler;
 
     protected AbstractFactoryManager() {
         ClassLoader loader = getClass().getClassLoader();
@@ -284,6 +286,21 @@ public abstract class AbstractFactoryManager extends AbstractKexFactoryManager i
     }
 
     @Override
+    public UnknownChannelReferenceHandler getUnknownChannelReferenceHandler() {
+        return unknownChannelReferenceHandler;
+    }
+
+    @Override
+    public void setUnknownChannelReferenceHandler(UnknownChannelReferenceHandler unknownChannelReferenceHandler) {
+        this.unknownChannelReferenceHandler = unknownChannelReferenceHandler;
+    }
+
+    @Override
+    public UnknownChannelReferenceHandler resolveUnknownChannelReferenceHandler() {
+        return getUnknownChannelReferenceHandler();
+    }
+
+    @Override
     public void addSessionListener(SessionListener listener) {
         SessionListener.validateListener(listener);
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/363cc520/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2Session.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2Session.java b/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2Session.java
index c8bcf89..5285d7b 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2Session.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2Session.java
@@ -331,23 +331,27 @@ public class Nio2Session extends AbstractCloseable implements IoSession {
 
     protected void startWriting() {
         Nio2DefaultIoWriteFuture future = writes.peek();
-        if (future != null) {
-            if (currentWrite.compareAndSet(null, future)) {
-                try {
-                    AsynchronousSocketChannel socket = getSocket();
-                    ByteBuffer buffer = future.getBuffer();
-                    Nio2CompletionHandler<Integer, Object> handler =
-                            Objects.requireNonNull(createWriteCycleCompletionHandler(future, socket, buffer),
-                                                       "No write cycle completion handler created");
-                    doWriteCycle(buffer, handler);
-                } catch (Throwable e) {
-                    future.setWritten();
-                    if (e instanceof RuntimeException) {
-                        throw (RuntimeException) e;
-                    } else {
-                        throw new RuntimeSshException(e);
-                    }
-                }
+        if (future == null) {
+            return;
+        }
+
+        if (!currentWrite.compareAndSet(null, future)) {
+            return;
+        }
+
+        try {
+            AsynchronousSocketChannel socket = getSocket();
+            ByteBuffer buffer = future.getBuffer();
+            Nio2CompletionHandler<Integer, Object> handler =
+                    Objects.requireNonNull(createWriteCycleCompletionHandler(future, socket, buffer),
+                                               "No write cycle completion handler created");
+            doWriteCycle(buffer, handler);
+        } catch (Throwable e) {
+            future.setWritten();
+            if (e instanceof RuntimeException) {
+                throw (RuntimeException) e;
+            } else {
+                throw new RuntimeSshException(e);
             }
         }
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/363cc520/sshd-core/src/main/java/org/apache/sshd/common/session/ConnectionService.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/ConnectionService.java b/sshd-core/src/main/java/org/apache/sshd/common/session/ConnectionService.java
index 823f800..ac977fa 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/ConnectionService.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/ConnectionService.java
@@ -33,7 +33,12 @@ import org.apache.sshd.server.x11.X11ForwardSupport;
  *
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public interface ConnectionService extends Service, PortForwardingEventListenerManager, PortForwardingEventListenerManagerHolder {
+public interface ConnectionService
+        extends Service,
+        UnknownChannelReferenceHandlerManager,
+        PortForwardingEventListenerManager,
+        PortForwardingEventListenerManagerHolder {
+
     /**
      * Register a newly created channel with a new unique identifier
      *

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/363cc520/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java b/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java
index 6216188..94d5e01 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java
@@ -56,6 +56,7 @@ public interface Session
                 ChannelListenerManager,
                 ChannelStreamPacketWriterResolverManager,
                 PortForwardingEventListenerManager,
+                UnknownChannelReferenceHandlerManager,
                 FactoryManagerHolder,
                 PropertyResolver,
                 AttributeStore,

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/363cc520/sshd-core/src/main/java/org/apache/sshd/common/session/UnknownChannelReferenceHandler.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/UnknownChannelReferenceHandler.java b/sshd-core/src/main/java/org/apache/sshd/common/session/UnknownChannelReferenceHandler.java
new file mode 100644
index 0000000..7eb7fa9
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/UnknownChannelReferenceHandler.java
@@ -0,0 +1,46 @@
+/*
+ * 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.session;
+
+import java.io.IOException;
+
+import org.apache.sshd.common.channel.Channel;
+import org.apache.sshd.common.util.buffer.Buffer;
+
+/**
+ * @see <A HREF="https://tools.ietf.org/html/rfc4254">RFC 4254</A>
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface UnknownChannelReferenceHandler {
+    /**
+     * Invoked when the connection service responsible for handling channel
+     * messages receives a message intended for an unknown channel.
+     *
+     * @param service The {@link ConnectionService} instance through which the
+     * message was received
+     * @param cmd The requested command identifier
+     * @param channelId The (unknown) target channel identifier
+     * @param buffer The message {@link Buffer} containing the rest of the message
+     * @return The resolved {@link Channel} - if {@code null} then the message
+     * for the unknown channel is ignored.
+     * @throws IOException If failed to handle the request
+     */
+    Channel handleUnknownChannelCommand(ConnectionService service, byte cmd, int channelId, Buffer buffer) throws IOException;
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/363cc520/sshd-core/src/main/java/org/apache/sshd/common/session/UnknownChannelReferenceHandlerManager.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/UnknownChannelReferenceHandlerManager.java b/sshd-core/src/main/java/org/apache/sshd/common/session/UnknownChannelReferenceHandlerManager.java
new file mode 100644
index 0000000..8e34051
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/UnknownChannelReferenceHandlerManager.java
@@ -0,0 +1,47 @@
+/*
+ * 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.session;
+
+import org.apache.sshd.common.channel.exception.SshChannelNotFoundException;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface UnknownChannelReferenceHandlerManager {
+    /**
+     * @return The {@link UnknownChannelReferenceHandlerManager} to use - if
+     * {@code null} then any reference to unknown channel causes an {@link SshChannelNotFoundException}
+     */
+    UnknownChannelReferenceHandler getUnknownChannelReferenceHandler();
+
+    /**
+     * @param handler The {@link UnknownChannelReferenceHandlerManager} to use - if
+     * {@code null} then any reference to unknown channel causes an {@link SshChannelNotFoundException}
+     */
+    void setUnknownChannelReferenceHandler(UnknownChannelReferenceHandler handler);
+
+    /**
+     * Check if current manager has a specific handler set for it - if not,
+     * try and resolve one from the &quot;parent&quot; container (if any)
+     *
+     * @return The resolved handler instance
+     */
+    UnknownChannelReferenceHandler resolveUnknownChannelReferenceHandler();
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/363cc520/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractConnectionService.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractConnectionService.java b/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractConnectionService.java
index 83ae0d8..bcde65b 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractConnectionService.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractConnectionService.java
@@ -22,7 +22,6 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.concurrent.ConcurrentHashMap;
@@ -39,7 +38,6 @@ import org.apache.sshd.common.Closeable;
 import org.apache.sshd.common.FactoryManager;
 import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.SshConstants;
-import org.apache.sshd.common.SshException;
 import org.apache.sshd.common.channel.AbstractChannel;
 import org.apache.sshd.common.channel.Channel;
 import org.apache.sshd.common.channel.RequestHandler;
@@ -54,6 +52,7 @@ import org.apache.sshd.common.io.AbstractIoWriteFuture;
 import org.apache.sshd.common.io.IoWriteFuture;
 import org.apache.sshd.common.session.ConnectionService;
 import org.apache.sshd.common.session.Session;
+import org.apache.sshd.common.session.UnknownChannelReferenceHandler;
 import org.apache.sshd.common.util.EventListenerUtils;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.Int2IntFunction;
@@ -106,6 +105,7 @@ public abstract class AbstractConnectionService<S extends AbstractSession>
     private final Collection<PortForwardingEventListenerManager> managersHolder = new CopyOnWriteArraySet<>();
     private final PortForwardingEventListener listenerProxy;
     private final S sessionInstance;
+    private UnknownChannelReferenceHandler unknownChannelReferenceHandler;
 
     protected AbstractConnectionService(S session) {
         sessionInstance = Objects.requireNonNull(session, "No session");
@@ -132,6 +132,16 @@ public abstract class AbstractConnectionService<S extends AbstractSession>
     }
 
     @Override
+    public UnknownChannelReferenceHandler getUnknownChannelReferenceHandler() {
+        return unknownChannelReferenceHandler;
+    }
+
+    @Override
+    public void setUnknownChannelReferenceHandler(UnknownChannelReferenceHandler handler) {
+        unknownChannelReferenceHandler = handler;
+    }
+
+    @Override
     public Collection<PortForwardingEventListenerManager> getRegisteredManagers() {
         return managersHolder.isEmpty() ? Collections.emptyList() : new ArrayList<>(managersHolder);
     }
@@ -378,7 +388,11 @@ public abstract class AbstractConnectionService<S extends AbstractSession>
     }
 
     public void channelOpenConfirmation(Buffer buffer) throws IOException {
-        Channel channel = getChannel(buffer);
+        Channel channel = getChannel(SshConstants.SSH_MSG_CHANNEL_OPEN_CONFIRMATION, buffer);
+        if (channel == null) {
+            return; // debug breakpoint
+        }
+
         int sender = buffer.getInt();
         long rwsize = buffer.getUInt();
         long rmpsize = buffer.getUInt();
@@ -398,7 +412,12 @@ public abstract class AbstractConnectionService<S extends AbstractSession>
     }
 
     public void channelOpenFailure(Buffer buffer) throws IOException {
-        AbstractClientChannel channel = (AbstractClientChannel) getChannel(buffer);
+        AbstractClientChannel channel =
+            (AbstractClientChannel) getChannel(SshConstants.SSH_MSG_CHANNEL_OPEN_FAILURE, buffer);
+        if (channel == null) {
+            return; // debug breakpoint
+        }
+
         int id = channel.getId();
         if (log.isDebugEnabled()) {
             log.debug("channelOpenFailure({}) Received SSH_MSG_CHANNEL_OPEN_FAILURE", channel);
@@ -414,7 +433,11 @@ public abstract class AbstractConnectionService<S extends AbstractSession>
      * @throws IOException if an error occurs
      */
     public void channelData(Buffer buffer) throws IOException {
-        Channel channel = getChannel(buffer);
+        Channel channel = getChannel(SshConstants.SSH_MSG_CHANNEL_DATA, buffer);
+        if (channel == null) {
+            return; // debug breakpoint
+        }
+
         channel.handleData(buffer);
     }
 
@@ -425,7 +448,11 @@ public abstract class AbstractConnectionService<S extends AbstractSession>
      * @throws IOException if an error occurs
      */
     public void channelExtendedData(Buffer buffer) throws IOException {
-        Channel channel = getChannel(buffer);
+        Channel channel = getChannel(SshConstants.SSH_MSG_CHANNEL_EXTENDED_DATA, buffer);
+        if (channel == null) {
+            return; // debug breakpoint
+        }
+
         channel.handleExtendedData(buffer);
     }
 
@@ -436,22 +463,12 @@ public abstract class AbstractConnectionService<S extends AbstractSession>
      * @throws IOException if an error occurs
      */
     public void channelWindowAdjust(Buffer buffer) throws IOException {
-        try {
-            // Do not use getChannel to avoid the session being closed
-            // if receiving the SSH_MSG_CHANNEL_WINDOW_ADJUST on an already closed channel
-            int recipient = buffer.getInt();
-            Channel channel = channels.get(recipient);
-            if (channel != null) {
-                channel.handleWindowAdjust(buffer);
-            } else {
-                log.warn("Received SSH_MSG_CHANNEL_WINDOW_ADJUST on unknown channel " + recipient);
-            }
-
-        } catch (SshException e) {
-            if (log.isDebugEnabled()) {
-                log.debug("channelWindowAdjust {} error: {}", e.getClass().getSimpleName(), e.getMessage());
-            }
+        Channel channel = getChannel(SshConstants.SSH_MSG_CHANNEL_WINDOW_ADJUST, buffer);
+        if (channel == null) {
+            return; // debug breakpoint
         }
+
+        channel.handleWindowAdjust(buffer);
     }
 
     /**
@@ -461,15 +478,12 @@ public abstract class AbstractConnectionService<S extends AbstractSession>
      * @throws IOException if an error occurs
      */
     public void channelEof(Buffer buffer) throws IOException {
-        // Do not use getChannel to avoid the session being closed
-        // if receiving the SSH_MSG_CHANNEL_EOF on an already closed channel
-        int recipient = buffer.getInt();
-        Channel channel = channels.get(recipient);
-        if (channel != null) {
-            channel.handleEof();
-        } else {
-            log.warn("Received SSH_MSG_CHANNEL_EOF on unknown channel " + recipient);
+        Channel channel = getChannel(SshConstants.SSH_MSG_CHANNEL_EOF, buffer);
+        if (channel == null) {
+            return; // debug breakpoint
         }
+
+        channel.handleEof();
     }
 
     /**
@@ -479,15 +493,12 @@ public abstract class AbstractConnectionService<S extends AbstractSession>
      * @throws IOException if an error occurs
      */
     public void channelClose(Buffer buffer) throws IOException {
-        // Do not use getChannel to avoid the session being closed
-        // if receiving the SSH_MSG_CHANNEL_CLOSE on an already closed channel
-        int recipient = buffer.getInt();
-        Channel channel = channels.get(recipient);
-        if (channel != null) {
-            channel.handleClose();
-        } else {
-            log.warn("Received SSH_MSG_CHANNEL_CLOSE on unknown channel " + recipient);
+        Channel channel = getChannel(SshConstants.SSH_MSG_CHANNEL_CLOSE, buffer);
+        if (channel == null) {
+            return; // debug breakpoint
         }
+
+        channel.handleClose();
     }
 
     /**
@@ -497,7 +508,11 @@ public abstract class AbstractConnectionService<S extends AbstractSession>
      * @throws IOException if an error occurs
      */
     public void channelRequest(Buffer buffer) throws IOException {
-        Channel channel = getChannel(buffer);
+        Channel channel = getChannel(SshConstants.SSH_MSG_CHANNEL_REQUEST, buffer);
+        if (channel == null) {
+            return; // debug breakpoint
+        }
+
         channel.handleRequest(buffer);
     }
 
@@ -508,7 +523,11 @@ public abstract class AbstractConnectionService<S extends AbstractSession>
      * @throws IOException if an error occurs
      */
     public void channelFailure(Buffer buffer) throws IOException {
-        Channel channel = getChannel(buffer);
+        Channel channel = getChannel(SshConstants.SSH_MSG_CHANNEL_FAILURE, buffer);
+        if (channel == null) {
+            return; // debug breakpoint
+        }
+
         channel.handleFailure();
     }
 
@@ -519,40 +538,60 @@ public abstract class AbstractConnectionService<S extends AbstractSession>
      * @throws IOException if an error occurs
      */
     public void channelSuccess(Buffer buffer) throws IOException {
-        Channel channel = getChannel(buffer);
+        Channel channel = getChannel(SshConstants.SSH_MSG_CHANNEL_SUCCESS, buffer);
+        if (channel == null) {
+            return; // debug breakpoint
+        }
+
         channel.handleSuccess();
     }
 
     /**
      * Retrieve the channel designated by the given packet
      *
+     * @param cmd The command being processed for the channel
      * @param buffer the incoming packet
      * @return the target channel
      * @throws IOException if the channel does not exists
      */
-    protected Channel getChannel(Buffer buffer) throws IOException {
-        return getChannel(buffer.getInt(), buffer);
+    protected Channel getChannel(byte cmd, Buffer buffer) throws IOException {
+        return getChannel(cmd, buffer.getInt(), buffer);
     }
 
-    protected Channel getChannel(int recipient, Buffer buffer) throws IOException {
+    protected Channel getChannel(byte cmd, int recipient, Buffer buffer) throws IOException {
         Channel channel = channels.get(recipient);
-        if (channel == null) {
-            byte[] data = buffer.array();
-            int curPos = buffer.rpos();
-            int cmd = (curPos >= 5) ? (data[curPos - 5] & 0xFF) : -1;
-            // Throw a special exception - SSHD-776
+        if (channel != null) {
+            return channel;
+        }
+
+        UnknownChannelReferenceHandler handler = resolveUnknownChannelReferenceHandler();
+        if (handler == null) {
+            // Throw a special exception - SSHD-777
             throw new SshChannelNotFoundException(recipient,
-                "Received " + SshConstants.getCommandMessageName(cmd) + " on unknown channel " + recipient);
+                    "Received " + SshConstants.getCommandMessageName(cmd) + " on unknown channel " + recipient);
+
         }
 
+        channel = handler.handleUnknownChannelCommand(this, cmd, recipient, buffer);
         return channel;
     }
 
+    @Override
+    public UnknownChannelReferenceHandler resolveUnknownChannelReferenceHandler() {
+        UnknownChannelReferenceHandler handler = getUnknownChannelReferenceHandler();
+        if (handler != null) {
+            return handler;
+        }
+
+        Session s = getSession();
+        return (s == null) ? null : s.resolveUnknownChannelReferenceHandler();
+    }
+
     protected void channelOpen(Buffer buffer) throws Exception {
         String type = buffer.getString();
-        final int sender = buffer.getInt();
-        final long rwsize = buffer.getUInt();
-        final long rmpsize = buffer.getUInt();
+        int sender = buffer.getInt();
+        long rwsize = buffer.getUInt();
+        long rmpsize = buffer.getUInt();
         /*
          * NOTE: the 'sender' is the identifier assigned by the remote side - the server in this case
          */
@@ -562,27 +601,27 @@ public abstract class AbstractConnectionService<S extends AbstractSession>
         }
 
         if (isClosing()) {
-            // TODO add language tag
+            // TODO add language tag configurable control
             sendChannelOpenFailure(buffer, sender, SshConstants.SSH_OPEN_CONNECT_FAILED, "Server is shutting down while attempting to open channel type=" + type, "");
             return;
         }
 
         if (!isAllowMoreSessions()) {
-            // TODO add language tag
+            // TODO add language tag configurable control
             sendChannelOpenFailure(buffer, sender, SshConstants.SSH_OPEN_CONNECT_FAILED, "additional sessions disabled", "");
             return;
         }
 
-        final Session session = getSession();
+        Session session = getSession();
         FactoryManager manager = Objects.requireNonNull(session.getFactoryManager(), "No factory manager");
-        final Channel channel = NamedFactory.create(manager.getChannelFactories(), type);
+        Channel channel = NamedFactory.create(manager.getChannelFactories(), type);
         if (channel == null) {
-            // TODO add language tag
+            // TODO add language tag configurable control
             sendChannelOpenFailure(buffer, sender, SshConstants.SSH_OPEN_UNKNOWN_CHANNEL_TYPE, "Unsupported channel type: " + type, "");
             return;
         }
 
-        final int channelId = registerChannel(channel);
+        int channelId = registerChannel(channel);
         channel.open(sender, rwsize, rmpsize, buffer).addListener(future -> {
             try {
                 if (future.isOpened()) {
@@ -653,9 +692,8 @@ public abstract class AbstractConnectionService<S extends AbstractSession>
         }
 
         Session session = getSession();
-        FactoryManager manager =
-                Objects.requireNonNull(session.getFactoryManager(), "No factory manager");
-        List<RequestHandler<ConnectionService>> handlers = manager.getGlobalRequestHandlers();
+        FactoryManager manager = Objects.requireNonNull(session.getFactoryManager(), "No factory manager");
+        Collection<RequestHandler<ConnectionService>> handlers = manager.getGlobalRequestHandlers();
         if (GenericUtils.size(handlers) > 0) {
             for (RequestHandler<ConnectionService> handler : handlers) {
                 RequestHandler.Result result;
@@ -713,11 +751,13 @@ public abstract class AbstractConnectionService<S extends AbstractSession>
     }
 
     protected void requestSuccess(Buffer buffer) throws Exception {
-        getSession().requestSuccess(buffer);
+        S s = getSession();
+        s.requestSuccess(buffer);
     }
 
     protected void requestFailure(Buffer buffer) throws Exception {
-        getSession().requestFailure(buffer);
+        S s = getSession();
+        s.requestFailure(buffer);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/363cc520/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 9d25fa9..e5f40ce 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
@@ -79,6 +79,7 @@ import org.apache.sshd.common.session.ReservedSessionMessagesHandler;
 import org.apache.sshd.common.session.Session;
 import org.apache.sshd.common.session.SessionListener;
 import org.apache.sshd.common.session.SessionWorkBuffer;
+import org.apache.sshd.common.session.UnknownChannelReferenceHandler;
 import org.apache.sshd.common.util.EventListenerUtils;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.Invoker;
@@ -243,6 +244,7 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
     private final Map<AttributeKey<?>, Object> attributes = new ConcurrentHashMap<>();
     private ReservedSessionMessagesHandler reservedSessionMessagesHandler;
     private ChannelStreamPacketWriterResolver channelStreamPacketWriterResolver;
+    private UnknownChannelReferenceHandler unknownChannelReferenceHandler;
 
     /**
      * Create a new session.
@@ -400,6 +402,27 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
     }
 
     @Override
+    public UnknownChannelReferenceHandler getUnknownChannelReferenceHandler() {
+        return unknownChannelReferenceHandler;
+    }
+
+    @Override
+    public void setUnknownChannelReferenceHandler(UnknownChannelReferenceHandler unknownChannelReferenceHandler) {
+        this.unknownChannelReferenceHandler = unknownChannelReferenceHandler;
+    }
+
+    @Override
+    public UnknownChannelReferenceHandler resolveUnknownChannelReferenceHandler() {
+        UnknownChannelReferenceHandler handler = getUnknownChannelReferenceHandler();
+        if (handler != null) {
+            return handler;
+        }
+
+        FactoryManager mgr = getFactoryManager();
+        return (mgr == null) ? null : mgr.resolveUnknownChannelReferenceHandler();
+    }
+
+    @Override
     public String getNegotiatedKexParameter(KexProposalOption paramType) {
         if (paramType == null) {
             return null;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/363cc520/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/DefaultUnknownChannelReferenceHandler.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/DefaultUnknownChannelReferenceHandler.java b/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/DefaultUnknownChannelReferenceHandler.java
new file mode 100644
index 0000000..42c0617
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/DefaultUnknownChannelReferenceHandler.java
@@ -0,0 +1,103 @@
+/*
+ * 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.session.helpers;
+
+import java.io.IOException;
+
+import org.apache.sshd.common.SshConstants;
+import org.apache.sshd.common.channel.Channel;
+import org.apache.sshd.common.io.IoWriteFuture;
+import org.apache.sshd.common.session.ConnectionService;
+import org.apache.sshd.common.session.Session;
+import org.apache.sshd.common.session.UnknownChannelReferenceHandler;
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class DefaultUnknownChannelReferenceHandler
+        extends AbstractLoggingBean
+        implements UnknownChannelReferenceHandler {
+    public static final DefaultUnknownChannelReferenceHandler INSTANCE = new DefaultUnknownChannelReferenceHandler();
+
+    public DefaultUnknownChannelReferenceHandler() {
+        super();
+    }
+
+    @Override
+    public Channel handleUnknownChannelCommand(
+            ConnectionService service, byte cmd, int channelId, Buffer buffer)
+                    throws IOException {
+        Session session = service.getSession();
+        // Use DEBUG level to avoid log overflow due to invalid messages flood
+        if (log.isDebugEnabled()) {
+            log.debug("handleUnknownChannelCommand({}) received {} command for unknown channel: {}",
+                    session, SshConstants.getCommandMessageName(cmd), channelId);
+        }
+
+        boolean wantReply = false;
+        switch (cmd) {
+            case SshConstants.SSH_MSG_CHANNEL_REQUEST: {
+                /*
+                 * From RFC 4252 - section 5.4:
+                 *
+                 *      If the request is not recognized or is not supported for the
+                 *      channel, SSH_MSG_CHANNEL_FAILURE is returned
+                 */
+                String req = buffer.getString();
+                wantReply = buffer.getBoolean();
+                // Use DEBUG level to avoid log overflow due to invalid messages flood
+                if (log.isDebugEnabled()) {
+                    log.debug("handleUnknownChannelCommand({}) Received SSH_MSG_CHANNEL_REQUEST={} (wantReply={}) for unknown channel: {}",
+                        session, req, wantReply, channelId);
+                }
+                break;
+            }
+
+            case SshConstants.SSH_MSG_CHANNEL_DATA:
+            case SshConstants.SSH_MSG_CHANNEL_EXTENDED_DATA:
+                // Not sure if entirely compliant with RFC4254, but try to stem the flood
+                wantReply = true;
+                break;
+
+            default: // do nothing
+        }
+
+        if (wantReply) {
+            sendFailureResponse(service, cmd, channelId);
+        }
+
+        return null;
+    }
+
+    protected IoWriteFuture sendFailureResponse(ConnectionService service, byte cmd, int channelId) throws IOException {
+        Session session = service.getSession();
+        // Use DEBUG level to avoid log overflow due to invalid messages flood
+        if (log.isDebugEnabled()) {
+            log.debug("sendFailureResponse({}) send SSH_MSG_CHANNEL_FAILURE for {} command on unknown channel: {}",
+                    session, SshConstants.getCommandMessageName(cmd), channelId);
+        }
+
+        Buffer rsp = session.createBuffer(SshConstants.SSH_MSG_CHANNEL_FAILURE, Integer.BYTES);
+        rsp.putInt(channelId);
+        return session.writePacket(rsp);
+    }
+}