From: Tibor Král Date: Mon, 26 Oct 2020 03:03:02 +0000 (+0100) Subject: Add workaround for SSH connection issue related to SSHD-1028 X-Git-Tag: v1.13.0~62 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=commitdiff_plain;h=427145f1947aded67c713f04cbfb151d6972bd77;p=netconf.git Add workaround for SSH connection issue related to SSHD-1028 Provide custom version of Nio2Session which overrides the bugged method. JIRA: NETCONF-736 Change-Id: I0ef091680a9bdbe6bcab889335bb5ff48e91b703 Signed-off-by: Tibor Král (cherry picked from commit 28925cdfe32f72ebcd034c9c7c9c107f176ca452) --- diff --git a/netconf/netconf-netty-util/src/main/java/org/opendaylight/netconf/nettyutil/handler/ssh/client/NetconfSshClient.java b/netconf/netconf-netty-util/src/main/java/org/opendaylight/netconf/nettyutil/handler/ssh/client/NetconfSshClient.java index d5d0d97aa9..b4f318ebd2 100644 --- a/netconf/netconf-netty-util/src/main/java/org/opendaylight/netconf/nettyutil/handler/ssh/client/NetconfSshClient.java +++ b/netconf/netconf-netty-util/src/main/java/org/opendaylight/netconf/nettyutil/handler/ssh/client/NetconfSshClient.java @@ -8,8 +8,11 @@ package org.opendaylight.netconf.nettyutil.handler.ssh.client; import com.google.common.annotations.Beta; +import org.opendaylight.netconf.nettyutil.handler.ssh.sshd1028.NetconfNio2ServiceFactoryFactory; import org.opendaylight.netconf.shaded.sshd.client.SshClient; import org.opendaylight.netconf.shaded.sshd.common.Factory; +import org.opendaylight.netconf.shaded.sshd.common.io.IoConnector; + /** * An extension to {@link SshClient} which uses {@link NetconfSessionFactory} to create sessions (leading towards @@ -18,9 +21,20 @@ import org.opendaylight.netconf.shaded.sshd.common.Factory; @Beta public class NetconfSshClient extends SshClient { public static final Factory DEFAULT_NETCONF_SSH_CLIENT_FACTORY = NetconfSshClient::new; + private final NetconfNio2ServiceFactoryFactory nio2ServiceFactoryFactory; + + public NetconfSshClient() { + this.nio2ServiceFactoryFactory = new NetconfNio2ServiceFactoryFactory(); + } @Override protected NetconfSessionFactory createSessionFactory() { return new NetconfSessionFactory(this); } + + @Override + protected IoConnector createConnector() { + setIoServiceFactoryFactory(nio2ServiceFactoryFactory); + return getIoServiceFactory().createConnector(getSessionFactory()); + } } diff --git a/netconf/netconf-netty-util/src/main/java/org/opendaylight/netconf/nettyutil/handler/ssh/sshd1028/NetconfNio2Connector.java b/netconf/netconf-netty-util/src/main/java/org/opendaylight/netconf/nettyutil/handler/ssh/sshd1028/NetconfNio2Connector.java new file mode 100644 index 0000000000..c565ac230b --- /dev/null +++ b/netconf/netconf-netty-util/src/main/java/org/opendaylight/netconf/nettyutil/handler/ssh/sshd1028/NetconfNio2Connector.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.nettyutil.handler.ssh.sshd1028; + +import java.nio.channels.AsynchronousChannelGroup; +import java.nio.channels.AsynchronousSocketChannel; +import org.opendaylight.netconf.shaded.sshd.common.FactoryManager; +import org.opendaylight.netconf.shaded.sshd.common.io.IoHandler; +import org.opendaylight.netconf.shaded.sshd.common.io.nio2.Nio2Connector; +import org.opendaylight.netconf.shaded.sshd.common.io.nio2.Nio2Session; + +/** + * Custom Nio2Connector which uses NetconfNio2Session instead of Nio2Session. + * Should be removed when SSHD-1028 is fixed. + */ +public class NetconfNio2Connector extends Nio2Connector { + + public NetconfNio2Connector(final FactoryManager manager, final IoHandler handler, + final AsynchronousChannelGroup group) { + super(manager, handler, group); + } + + @Override + protected Nio2Session createSession(final FactoryManager manager, final IoHandler handler, + final AsynchronousSocketChannel socket) throws Throwable { + return new NetconfNio2Session(this, manager, handler, socket, null); + } +} diff --git a/netconf/netconf-netty-util/src/main/java/org/opendaylight/netconf/nettyutil/handler/ssh/sshd1028/NetconfNio2ServiceFactory.java b/netconf/netconf-netty-util/src/main/java/org/opendaylight/netconf/nettyutil/handler/ssh/sshd1028/NetconfNio2ServiceFactory.java new file mode 100644 index 0000000000..3faeb0c5e7 --- /dev/null +++ b/netconf/netconf-netty-util/src/main/java/org/opendaylight/netconf/nettyutil/handler/ssh/sshd1028/NetconfNio2ServiceFactory.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.nettyutil.handler.ssh.sshd1028; + +import java.lang.reflect.Field; +import java.nio.channels.AsynchronousChannelGroup; +import java.security.AccessController; +import java.security.PrivilegedAction; +import org.opendaylight.netconf.shaded.sshd.common.FactoryManager; +import org.opendaylight.netconf.shaded.sshd.common.io.IoConnector; +import org.opendaylight.netconf.shaded.sshd.common.io.IoHandler; +import org.opendaylight.netconf.shaded.sshd.common.io.nio2.Nio2ServiceFactory; +import org.opendaylight.netconf.shaded.sshd.common.util.threads.CloseableExecutorService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Custom Nio2ServiceFactory which creates instances of NetconfNio2Connector instead of Nio2Connector. + * Should be removed when SSHD-1028 is fixed. + */ +public class NetconfNio2ServiceFactory extends Nio2ServiceFactory { + private static final Logger LOG = LoggerFactory.getLogger(NetconfNio2ServiceFactory.class); + private static final Field FIELD_GROUP; + + static { + final Field fieldGroup; + try { + fieldGroup = NetconfNio2ServiceFactory.class.getSuperclass().getDeclaredField("group"); + } catch (NoSuchFieldException e) { + LOG.error("Cannot access the ChannelGroup from the " + "Nio2ServiceFactory"); + throw new ExceptionInInitializerError(e); + } + + AccessController.doPrivileged((PrivilegedAction) () -> { + fieldGroup.setAccessible(true); + return null; + }); + + FIELD_GROUP = fieldGroup; + } + + public NetconfNio2ServiceFactory(final FactoryManager factoryManager, final CloseableExecutorService service) { + super(factoryManager, service); + } + + @Override + public IoConnector createConnector(final IoHandler handler) { + if (FIELD_GROUP != null) { + try { + final AsynchronousChannelGroup group = (AsynchronousChannelGroup)FIELD_GROUP.get(this); + return autowireCreatedService(new NetconfNio2Connector(getFactoryManager(), handler, group)); + } catch (IllegalAccessException e) { + LOG.error("NetconfNio2Connector cannot be instanciated. Creating default Nio2Connector instead."); + } + } + + return super.createConnector(handler); + } +} diff --git a/netconf/netconf-netty-util/src/main/java/org/opendaylight/netconf/nettyutil/handler/ssh/sshd1028/NetconfNio2ServiceFactoryFactory.java b/netconf/netconf-netty-util/src/main/java/org/opendaylight/netconf/nettyutil/handler/ssh/sshd1028/NetconfNio2ServiceFactoryFactory.java new file mode 100644 index 0000000000..b400e0ca34 --- /dev/null +++ b/netconf/netconf-netty-util/src/main/java/org/opendaylight/netconf/nettyutil/handler/ssh/sshd1028/NetconfNio2ServiceFactoryFactory.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.nettyutil.handler.ssh.sshd1028; + +import org.opendaylight.netconf.shaded.sshd.common.FactoryManager; +import org.opendaylight.netconf.shaded.sshd.common.io.IoServiceFactory; +import org.opendaylight.netconf.shaded.sshd.common.io.nio2.Nio2ServiceFactoryFactory; + +/** + * Custom Nio2ServiceFactoryFactory which creates instances of NetconfNio2ServiceFactory instead of Nio2ServiceFactory. + * Should be removed when SSHD-1028 is fixed. + */ +public class NetconfNio2ServiceFactoryFactory extends Nio2ServiceFactoryFactory { + + public NetconfNio2ServiceFactoryFactory() { + super(null); + } + + @Override + public IoServiceFactory create(final FactoryManager manager) { + return new NetconfNio2ServiceFactory(manager, newExecutor()); + } +} diff --git a/netconf/netconf-netty-util/src/main/java/org/opendaylight/netconf/nettyutil/handler/ssh/sshd1028/NetconfNio2Session.java b/netconf/netconf-netty-util/src/main/java/org/opendaylight/netconf/nettyutil/handler/ssh/sshd1028/NetconfNio2Session.java new file mode 100644 index 0000000000..9c6e8cf0ca --- /dev/null +++ b/netconf/netconf-netty-util/src/main/java/org/opendaylight/netconf/nettyutil/handler/ssh/sshd1028/NetconfNio2Session.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.nettyutil.handler.ssh.sshd1028; + +import java.io.IOException; +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.AsynchronousSocketChannel; +import org.opendaylight.netconf.shaded.sshd.common.FactoryManager; +import org.opendaylight.netconf.shaded.sshd.common.io.IoHandler; +import org.opendaylight.netconf.shaded.sshd.common.io.nio2.Nio2CompletionHandler; +import org.opendaylight.netconf.shaded.sshd.common.io.nio2.Nio2Service; +import org.opendaylight.netconf.shaded.sshd.common.io.nio2.Nio2Session; +import org.opendaylight.netconf.shaded.sshd.common.util.Readable; + +/** + * Custom Nio2Session which fixes the issue with connections not being properly closed. + * Should be removed when SSHD-1028 is fixed. + */ +public class NetconfNio2Session extends Nio2Session { + + public NetconfNio2Session(final Nio2Service service, final FactoryManager manager, final IoHandler handler, + final AsynchronousSocketChannel socket, final SocketAddress acceptanceAddress) + throws IOException { + super(service, manager, handler, socket, acceptanceAddress); + } + + /** + * This method in sshd-osgi:2.5.0 and 2.5.1 contains a bug. The close(true) statement was removed. We can override + * it making a workaround for this issue - until SSHD-1028 is fixed. + */ + @Override + @SuppressWarnings("IllegalCatch") + protected void handleReadCycleCompletion(final ByteBuffer buffer, final Readable bufReader, + final Nio2CompletionHandler completionHandler, + final Integer result, final Object attachment) { + try { + boolean debugEnabled = log.isDebugEnabled(); + if (result >= 0) { + if (debugEnabled) { + log.debug("handleReadCycleCompletion({}) read {} bytes", this, result); + } + buffer.flip(); + IoHandler handler = getIoHandler(); + handler.messageReceived(this, bufReader); + if (!closeFuture.isClosed()) { + // re-use reference for next iteration since we finished processing it + buffer.clear(); + doReadCycle(buffer, completionHandler); + } else { + if (debugEnabled) { + log.debug("handleReadCycleCompletion({}) IoSession has been closed, stop reading", this); + } + } + } else { + if (debugEnabled) { + log.debug("handleReadCycleCompletion({}) Socket has been disconnected (result={}), closing " + + "IoSession now", this, result); + } + close(true); + } + } catch (Throwable exc) { + completionHandler.failed(exc, attachment); + } + } +}