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 <lyor.goldst...@gmail.com> Authored: Fri Oct 13 16:26:54 2017 +0300 Committer: Lyor Goldstein <lyor.goldst...@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:d...@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:d...@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:d...@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 "parent" 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:d...@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); + } +}