Capture server key before returning from callback
[netconf.git] / netconf / callhome-protocol / src / main / java / org / opendaylight / netconf / callhome / protocol / NetconfCallHomeServer.java
index 34658aae5588a6bc7e5aa2426852914937c87fbf..47ee60eeccf93e7aad2f2087417a73b5f887cba8 100644 (file)
@@ -5,28 +5,26 @@
  * 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.callhome.protocol;
 
-import com.google.common.base.Preconditions;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.VisibleForTesting;
 import java.io.IOException;
 import java.net.InetSocketAddress;
 import java.net.SocketAddress;
 import java.security.PublicKey;
-import org.apache.sshd.ClientSession;
-import org.apache.sshd.SshClient;
-import org.apache.sshd.client.ServerKeyVerifier;
-import org.apache.sshd.client.SessionFactory;
+import org.apache.sshd.client.SshClient;
 import org.apache.sshd.client.future.AuthFuture;
-import org.apache.sshd.client.session.ClientSessionImpl;
-import org.apache.sshd.common.KeyExchange;
-import org.apache.sshd.common.Session;
-import org.apache.sshd.common.SessionListener;
+import org.apache.sshd.client.keyverifier.ServerKeyVerifier;
+import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.client.session.SessionFactory;
 import org.apache.sshd.common.future.SshFutureListener;
 import org.apache.sshd.common.io.IoAcceptor;
 import org.apache.sshd.common.io.IoServiceFactory;
-import org.apache.sshd.common.io.mina.MinaServiceFactory;
-import org.apache.sshd.common.io.nio2.Nio2ServiceFactory;
+import org.apache.sshd.common.session.Session;
+import org.apache.sshd.common.session.SessionListener;
+import org.apache.sshd.netty.NettyIoServiceFactory;
 import org.opendaylight.netconf.callhome.protocol.CallHomeSessionContext.Factory;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -35,48 +33,46 @@ public class NetconfCallHomeServer implements AutoCloseable, ServerKeyVerifier {
 
     private static final Logger LOG = LoggerFactory.getLogger(NetconfCallHomeServer.class);
 
-    private final IoAcceptor acceptor;
-    private final SshClient client;
     private final CallHomeAuthorizationProvider authProvider;
-    private final CallHomeSessionContext.Factory sessionFactory;
+    private final IoServiceFactory serviceFactory;
     private final InetSocketAddress bindAddress;
-    private StatusRecorder recorder;
+    private final StatusRecorder recorder;
+    private final Factory sessionFactory;
+    private final IoAcceptor acceptor;
+    private final SshClient client;
+
+    NetconfCallHomeServer(final SshClient sshClient, final CallHomeAuthorizationProvider authProvider,
+            final Factory factory, final InetSocketAddress socketAddress, final StatusRecorder recorder) {
+        this(sshClient, authProvider, factory, socketAddress, recorder,
+            new NettyIoServiceFactory(factory.getNettyGroup()));
+    }
 
-    NetconfCallHomeServer(SshClient sshClient, CallHomeAuthorizationProvider authProvider, Factory factory,
-                          InetSocketAddress socketAddress, StatusRecorder recorder) {
-        this.client = Preconditions.checkNotNull(sshClient);
-        this.authProvider = Preconditions.checkNotNull(authProvider);
-        this.sessionFactory = Preconditions.checkNotNull(factory);
+    @VisibleForTesting
+    NetconfCallHomeServer(final SshClient sshClient, final CallHomeAuthorizationProvider authProvider,
+            final Factory factory, final InetSocketAddress socketAddress, final StatusRecorder recorder,
+            final IoServiceFactory serviceFactory) {
+        this.client = requireNonNull(sshClient);
+        this.authProvider = requireNonNull(authProvider);
+        this.sessionFactory = requireNonNull(factory);
         this.bindAddress = socketAddress;
         this.recorder = recorder;
+        this.serviceFactory = requireNonNull(serviceFactory);
 
         sshClient.setServerKeyVerifier(this);
+        sshClient.addSessionListener(createSessionListener());
 
-        SessionFactory clientSessions = new SessionFactory();
-        clientSessions.setClient(sshClient);
-        clientSessions.addListener(createSessionListener());
-
-        IoServiceFactory minaFactory = createServiceFactory(sshClient);
-        this.acceptor = minaFactory.createAcceptor(clientSessions);
-    }
-
-    private IoServiceFactory createServiceFactory(SshClient sshClient) {
-        try {
-            return createMinaServiceFactory(sshClient);
-        } catch (NoClassDefFoundError e) {
-            LOG.warn("Mina is not available, defaulting to NIO.");
-            return new Nio2ServiceFactory(sshClient);
-        }
+        acceptor = serviceFactory.createAcceptor(new SessionFactory(sshClient));
     }
 
-    protected IoServiceFactory createMinaServiceFactory(SshClient sshClient) {
-        return new MinaServiceFactory(sshClient);
+    @VisibleForTesting
+    SshClient getClient() {
+        return client;
     }
 
     SessionListener createSessionListener() {
         return new SessionListener() {
             @Override
-            public void sessionEvent(Session session, Event event) {
+            public void sessionEvent(final Session session, final Event event) {
                 ClientSession clientSession = (ClientSession) session;
                 LOG.debug("SSH session {} event {}", session, event);
                 switch (event) {
@@ -92,12 +88,12 @@ public class NetconfCallHomeServer implements AutoCloseable, ServerKeyVerifier {
             }
 
             @Override
-            public void sessionCreated(Session session) {
+            public void sessionCreated(final Session session) {
                 LOG.debug("SSH session {} created", session);
             }
 
             @Override
-            public void sessionClosed(Session session) {
+            public void sessionClosed(final Session session) {
                 CallHomeSessionContext ctx = CallHomeSessionContext.getFrom((ClientSession) session);
                 if (ctx != null) {
                     ctx.removeSelf();
@@ -107,7 +103,7 @@ public class NetconfCallHomeServer implements AutoCloseable, ServerKeyVerifier {
         };
     }
 
-    private void doPostAuth(final ClientSession session) {
+    private static void doPostAuth(final ClientSession session) {
         CallHomeSessionContext.getFrom(session).openNetconfChannel();
     }
 
@@ -121,9 +117,11 @@ public class NetconfCallHomeServer implements AutoCloseable, ServerKeyVerifier {
     }
 
     private SshFutureListener<AuthFuture> newAuthSshFutureListener(final ClientSession session) {
+        final PublicKey serverKey = session.getKex().getServerKey();
+
         return new SshFutureListener<AuthFuture>() {
             @Override
-            public void operationComplete(AuthFuture authFuture) {
+            public void operationComplete(final AuthFuture authFuture) {
                 if (authFuture.isSuccess()) {
                     onSuccess();
                 } else if (authFuture.isFailure()) {
@@ -138,14 +136,9 @@ public class NetconfCallHomeServer implements AutoCloseable, ServerKeyVerifier {
                 LOG.debug("Authorize success");
             }
 
-            private void onFailure(Throwable throwable) {
-                ClientSessionImpl impl = (ClientSessionImpl) session;
+            private void onFailure(final Throwable throwable) {
                 LOG.error("Authorize failed for session {}", session, throwable);
-
-                KeyExchange kex = impl.getKex();
-                PublicKey key = kex.getServerKey();
-                recorder.reportFailedAuth(key);
-
+                recorder.reportFailedAuth(serverKey);
                 session.close(true);
             }
 
@@ -157,7 +150,8 @@ public class NetconfCallHomeServer implements AutoCloseable, ServerKeyVerifier {
     }
 
     @Override
-    public boolean verifyServerKey(ClientSession sshClientSession, SocketAddress remoteAddress, PublicKey serverKey) {
+    public boolean verifyServerKey(final ClientSession sshClientSession, final SocketAddress remoteAddress,
+            final PublicKey serverKey) {
         final CallHomeAuthorization authorization = authProvider.provideAuth(remoteAddress, serverKey);
         // server is not authorized
         if (!authorization.isServerAllowed()) {
@@ -181,13 +175,14 @@ public class NetconfCallHomeServer implements AutoCloseable, ServerKeyVerifier {
             client.start();
             acceptor.bind(bindAddress);
         } catch (IOException e) {
-            LOG.error("Unable to start NETCONF CallHome Service", e);
+            LOG.error("Unable to start NETCONF CallHome Service on {}", bindAddress, e);
             throw e;
         }
     }
 
     @Override
-    public void close() throws Exception {
+    public void close() {
         acceptor.close(true);
+        serviceFactory.close(true);
     }
 }