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 "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: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);
+ }
+}