2 * Copyright (c) 2016 Brocade Communication Systems and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.netconf.callhome.protocol;
11 import com.google.common.base.Preconditions;
12 import java.io.IOException;
13 import java.net.InetSocketAddress;
14 import java.net.SocketAddress;
15 import java.security.PublicKey;
16 import org.apache.sshd.ClientSession;
17 import org.apache.sshd.SshClient;
18 import org.apache.sshd.client.ServerKeyVerifier;
19 import org.apache.sshd.client.SessionFactory;
20 import org.apache.sshd.client.future.AuthFuture;
21 import org.apache.sshd.common.Session;
22 import org.apache.sshd.common.SessionListener;
23 import org.apache.sshd.common.future.SshFutureListener;
24 import org.apache.sshd.common.io.IoAcceptor;
25 import org.apache.sshd.common.io.IoServiceFactory;
26 import org.apache.sshd.common.io.mina.MinaServiceFactory;
27 import org.apache.sshd.common.io.nio2.Nio2ServiceFactory;
28 import org.opendaylight.netconf.callhome.protocol.CallHomeSessionContext.Factory;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
32 public class NetconfCallHomeServer implements AutoCloseable, ServerKeyVerifier {
34 private static final Logger LOG = LoggerFactory.getLogger(NetconfCallHomeServer.class);
36 private final IoAcceptor acceptor;
37 private final SshClient client;
38 private final CallHomeAuthorizationProvider authProvider;
39 private final CallHomeSessionContext.Factory sessionFactory;
40 private final InetSocketAddress bindAddress;
42 NetconfCallHomeServer(SshClient sshClient, CallHomeAuthorizationProvider authProvider, Factory factory,
43 InetSocketAddress socketAddress) {
44 this.client = Preconditions.checkNotNull(sshClient);
45 this.authProvider = Preconditions.checkNotNull(authProvider);
46 this.sessionFactory = Preconditions.checkNotNull(factory);
47 this.bindAddress = socketAddress;
49 sshClient.setServerKeyVerifier(this);
51 SessionFactory clientSessions = new SessionFactory();
52 clientSessions.setClient(sshClient);
53 clientSessions.addListener(createSessionListener());
55 IoServiceFactory minaFactory = createServiceFactory(sshClient);
56 this.acceptor = minaFactory.createAcceptor(clientSessions);
59 IoServiceFactory createServiceFactory(SshClient sshClient) {
61 return createMinaServiceFactory(sshClient);
62 } catch (NoClassDefFoundError e) {
63 LOG.warn("Mina is not available, defaulting to NIO.");
64 return new Nio2ServiceFactory(sshClient);
69 protected IoServiceFactory createMinaServiceFactory(SshClient sshClient) {
70 return new MinaServiceFactory(sshClient);
73 SessionListener createSessionListener() {
74 return new SessionListener() {
77 public void sessionEvent(Session session, Event event) {
78 ClientSession cSession = (ClientSession) session;
79 LOG.debug("SSH session {} event {}", session, event);
93 public void sessionCreated(Session session) {
94 LOG.debug("SSH session {} created", session);
98 public void sessionClosed(Session session) {
99 CallHomeSessionContext ctx = CallHomeSessionContext.getFrom((ClientSession) session);
103 LOG.debug("SSH Session {} closed", session);
108 private void doPostAuth(final ClientSession cSession) {
109 CallHomeSessionContext.getFrom(cSession).openNetconfChannel();
112 private void doAuth(final ClientSession cSession) {
114 final AuthFuture authFuture = CallHomeSessionContext.getFrom(cSession).authorize();
115 authFuture.addListener(newAuthSshFutureListener(cSession));
116 } catch (IOException e) {
117 LOG.error("Failed to authorize session {}", cSession, e);
121 SshFutureListener<AuthFuture> newAuthSshFutureListener(final ClientSession cSession) {
122 return new SshFutureListener<AuthFuture>() {
124 public void operationComplete(AuthFuture authFuture) {
125 if (authFuture.isSuccess()) {
127 } else if (authFuture.isFailure()) {
128 onFailure(authFuture.getException());
129 } else if (authFuture.isCanceled()) {
132 authFuture.removeListener(this);
135 private void onSuccess() {
136 LOG.debug("Authorize success");
139 private void onFailure(Throwable throwable) {
140 LOG.error("Failed to authorize session {}", cSession, throwable);
141 cSession.close(true);
144 private void onCanceled() {
145 LOG.warn("Authorize cancelled");
146 cSession.close(true);
152 public boolean verifyServerKey(ClientSession sshClientSession, SocketAddress remoteAddress, PublicKey serverKey) {
153 final CallHomeAuthorization authorization = authProvider.provideAuth(remoteAddress, serverKey);
154 // server is not authorized
155 if (!authorization.isServerAllowed()) {
156 LOG.info("Incoming session {} was rejected by Authorization Provider.",sshClientSession);
159 CallHomeSessionContext session = sessionFactory.createIfNotExists(sshClientSession, authorization, remoteAddress);
160 // Session was created, session with same name does not exists
161 if(session != null) {
164 // Session was not created, session with same name exists
165 LOG.info("Incoming session {} was rejected. Session with same name {} is already active.",sshClientSession,authorization.getSessionName());
169 public void bind() throws IOException {
172 acceptor.bind(bindAddress);
173 } catch (IOException e) {
174 LOG.error("Unable to start NETCONF CallHome Service", e);
181 public void close() throws Exception {
182 acceptor.close(true);