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
@Beta
public class NetconfSshClient extends SshClient {
public static final Factory<SshClient> 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());
+ }
}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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<Void>) () -> {
+ 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);
+ }
+}
--- /dev/null
+/*
+ * 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());
+ }
+}
--- /dev/null
+/*
+ * 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<Integer, Object> 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);
+ }
+ }
+}