* 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;
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(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);
+ 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()));
+ }
+
+ @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) {
doAuth(clientSession);
break;
case Authenticated:
- doPostAuth(clientSession);
+ CallHomeSessionContext.getFrom(clientSession).openNetconfChannel();
break;
default:
break;
}
@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();
}
LOG.debug("SSH Session {} closed", session);
}
- };
- }
-
- private void doPostAuth(final ClientSession session) {
- CallHomeSessionContext.getFrom(session).openNetconfChannel();
- }
- private void doAuth(final ClientSession session) {
- try {
- final AuthFuture authFuture = CallHomeSessionContext.getFrom(session).authorize();
- authFuture.addListener(newAuthSshFutureListener(session));
- } catch (IOException e) {
- LOG.error("Failed to authorize session {}", session, e);
- }
+ private void doAuth(final ClientSession session) {
+ try {
+ final AuthFuture authFuture = CallHomeSessionContext.getFrom(session).authorize();
+ authFuture.addListener(newAuthSshFutureListener(session));
+ } catch (IOException e) {
+ LOG.error("Failed to authorize session {}", session, e);
+ }
+ }
+ };
}
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()) {
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);
}
}
@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()) {
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);
}
}