This is an automated email from the ASF dual-hosted git repository. lgoldstein pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/mina-sshd.git
The following commit(s) were added to refs/heads/master by this push: new 11b33de [SSHD-942] Added ChannelIdTrackingUnknownChannelReferenceHandler 11b33de is described below commit 11b33dee37b5b9c71a40a8a98a42007e3687131e Author: Lyor Goldstein <lgoldst...@apache.org> AuthorDate: Mon Sep 23 07:44:28 2019 +0300 [SSHD-942] Added ChannelIdTrackingUnknownChannelReferenceHandler --- CHANGES.md | 4 ++ docs/event-listeners.md | 4 ++ ...elIdTrackingUnknownChannelReferenceHandler.java | 83 ++++++++++++++++++++++ .../DefaultUnknownChannelReferenceHandler.java | 16 +++-- 4 files changed, 101 insertions(+), 6 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 7f77a16..1de1064 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -36,6 +36,10 @@ the standard does not specifically specify the behavior regarding symbolic links * `SessionListener` supports `sessionPeerIdentificationReceived` that is invoked once successful peer version data is received. +* `ChannelIdTrackingUnknownChannelReferenceHandler` extends the functionality of the `DefaultUnknownChannelReferenceHandler` +by tracking the initialized channels identifiers and being lenient only if command is received for a channel that was +initialized in the past. + ## Behavioral changes and enhancements * [SSHD-926](https://issues.apache.org/jira/browse/SSHD-930) - Add support for OpenSSH 'lsets...@openssh.com' SFTP protocol extension. diff --git a/docs/event-listeners.md b/docs/event-listeners.md index 476ec13..49a4a83 100644 --- a/docs/event-listeners.md +++ b/docs/event-listeners.md @@ -80,6 +80,10 @@ and logs them at DEBUG level. For a select few types of messages the code genera 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. +An **experimental** `ChannelIdTrackingUnknownChannelReferenceHandler` is available in _sshd-contrib_ package that applies the "leniency" of +the `DefaultUnknownChannelReferenceHandler` only if the unknown channel is one that has been assigned in the past - otherwise it throws an +exception. In order to use it, the handler instance needs to be registered as **both** an `UnknownChannelReferenceHandler` and a `ChannelListener`. + ### `KexExtensionHandler` Provides hooks for implementing [KEX extension negotiation](https://tools.ietf.org/html/rfc8308). diff --git a/sshd-contrib/src/main/java/org/apache/sshd/common/session/helpers/ChannelIdTrackingUnknownChannelReferenceHandler.java b/sshd-contrib/src/main/java/org/apache/sshd/common/session/helpers/ChannelIdTrackingUnknownChannelReferenceHandler.java new file mode 100644 index 0000000..bb6498b --- /dev/null +++ b/sshd-contrib/src/main/java/org/apache/sshd/common/session/helpers/ChannelIdTrackingUnknownChannelReferenceHandler.java @@ -0,0 +1,83 @@ +/* + * 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.AttributeRepository.AttributeKey; +import org.apache.sshd.common.SshConstants; +import org.apache.sshd.common.channel.Channel; +import org.apache.sshd.common.channel.ChannelListener; +import org.apache.sshd.common.channel.exception.SshChannelNotFoundException; +import org.apache.sshd.common.session.ConnectionService; +import org.apache.sshd.common.session.Session; +import org.apache.sshd.common.util.buffer.Buffer; + +/** + * Makes sure that the referenced "unknown" channel identifier + * is one that was assigned in the past. <B>Note:</B> it relies on the + * fact that the default {@code ConnectionService} implementation assigns + * channels identifiers in ascending order. + * + * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> + */ +public class ChannelIdTrackingUnknownChannelReferenceHandler + extends DefaultUnknownChannelReferenceHandler + implements ChannelListener { + public static final AttributeKey<Integer> LAST_CHANNEL_ID_KEY = new AttributeKey<>(); + + public static final ChannelIdTrackingUnknownChannelReferenceHandler TRACKER = + new ChannelIdTrackingUnknownChannelReferenceHandler(); + + public ChannelIdTrackingUnknownChannelReferenceHandler() { + super(); + } + + @Override + public void channelInitialized(Channel channel) { + int channelId = channel.getId(); + Session session = channel.getSession(); + Integer lastTracked = session.setAttribute(LAST_CHANNEL_ID_KEY, channelId); + if (log.isDebugEnabled()) { + log.debug("channelInitialized({}) updated last tracked channel ID {} => {}", + channel, lastTracked, channelId); + } + } + + @Override + public Channel handleUnknownChannelCommand( + ConnectionService service, byte cmd, int channelId, Buffer buffer) + throws IOException { + Session session = service.getSession(); + Integer lastTracked = session.getAttribute(LAST_CHANNEL_ID_KEY); + if ((lastTracked != null) && (channelId <= lastTracked.intValue())) { + // Use TRACE level in order to avoid messages flooding + if (log.isTraceEnabled()) { + log.trace("handleUnknownChannelCommand({}) apply default handling for {} on channel={} (lastTracked={})", + session, SshConstants.getCommandMessageName(cmd), channelId, lastTracked); + } + return super.handleUnknownChannelCommand(service, cmd, channelId, buffer); + } + + throw new SshChannelNotFoundException(channelId, + "Received " + SshConstants.getCommandMessageName(cmd) + " on unassigned channel " + channelId + + " (last assigned=" + lastTracked + ")"); + } +} 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 index 76ebec2..8438ca7 100644 --- 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 @@ -55,13 +55,13 @@ public class DefaultUnknownChannelReferenceHandler @Override public Channel handleUnknownChannelCommand( ConnectionService service, byte cmd, int channelId, Buffer buffer) - throws IOException { + throws IOException { Session session = service.getSession(); // Use DEBUG level to avoid log overflow due to invalid messages flood boolean debugEnabled = log.isDebugEnabled(); if (debugEnabled) { log.debug("handleUnknownChannelCommand({}) received {} command for unknown channel: {}", - session, SshConstants.getCommandMessageName(cmd), channelId); + session, SshConstants.getCommandMessageName(cmd), channelId); } boolean wantReply = false; @@ -85,10 +85,12 @@ public class DefaultUnknownChannelReferenceHandler case SshConstants.SSH_MSG_CHANNEL_DATA: case SshConstants.SSH_MSG_CHANNEL_EXTENDED_DATA: - wantReply = PropertyResolverUtils.getBooleanProperty(session, SEND_REPLY_FOR_CHANNEL_DATA, DEFAULT_SEND_REPLY_FOR_CHANNEL_DATA); + wantReply = PropertyResolverUtils.getBooleanProperty( + session, SEND_REPLY_FOR_CHANNEL_DATA, DEFAULT_SEND_REPLY_FOR_CHANNEL_DATA); // Use TRACE level to avoid log overflow due to invalid messages flood if (log.isTraceEnabled()) { - log.trace("handleUnknownChannelCommand({}) received msg channel data (opcode={}) reply={}", session, cmd, wantReply); + log.trace("handleUnknownChannelCommand({}) received msg channel data (opcode={}) reply={}", + session, cmd, wantReply); } break; @@ -102,12 +104,14 @@ public class DefaultUnknownChannelReferenceHandler return null; } - protected IoWriteFuture sendFailureResponse(ConnectionService service, byte cmd, int channelId) throws IOException { + 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); + session, SshConstants.getCommandMessageName(cmd), channelId); } Buffer rsp = session.createBuffer(SshConstants.SSH_MSG_CHANNEL_FAILURE, Integer.BYTES);