2 * Copyright (c) 2013 Cisco Systems, Inc. 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
8 package org.opendaylight.controller.netconf.ssh.threads;
11 import ch.ethz.ssh2.AuthenticationResult;
12 import ch.ethz.ssh2.PtySettings;
13 import ch.ethz.ssh2.ServerAuthenticationCallback;
14 import ch.ethz.ssh2.ServerConnection;
15 import ch.ethz.ssh2.ServerConnectionCallback;
16 import ch.ethz.ssh2.ServerSession;
17 import ch.ethz.ssh2.ServerSessionCallback;
18 import ch.ethz.ssh2.SimpleServerSessionCallback;
19 import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider;
20 import org.slf4j.Logger;
21 import org.slf4j.LoggerFactory;
23 import javax.annotation.concurrent.ThreadSafe;
24 import java.io.IOException;
25 import java.net.InetSocketAddress;
26 import java.net.Socket;
29 public class SocketThread implements Runnable, ServerAuthenticationCallback, ServerConnectionCallback {
30 private static final Logger logger = LoggerFactory.getLogger(SocketThread.class);
32 private final Socket socket;
33 private final InetSocketAddress clientAddress;
34 private ServerConnection conn = null;
35 private final long sessionId;
36 private String currentUser;
37 private final String remoteAddressWithPort;
38 private final AuthProvider authProvider;
41 public static void start(Socket socket,
42 InetSocketAddress clientAddress,
44 AuthProvider authProvider) throws IOException {
45 Thread netconf_ssh_socket_thread = new Thread(new SocketThread(socket, clientAddress, sessionId, authProvider));
46 netconf_ssh_socket_thread.setDaemon(true);
47 netconf_ssh_socket_thread.start();
50 private SocketThread(Socket socket,
51 InetSocketAddress clientAddress,
53 AuthProvider authProvider) throws IOException {
56 this.clientAddress = clientAddress;
57 this.sessionId = sessionId;
58 this.remoteAddressWithPort = socket.getRemoteSocketAddress().toString().replaceFirst("/", "");
59 this.authProvider = authProvider;
65 conn = new ServerConnection(socket);
67 conn.setPEMHostKey(authProvider.getPEMAsCharArray(), "netconf");
68 } catch (Exception e) {
69 logger.debug("Server authentication setup failed.");
71 conn.setAuthenticationCallback(this);
72 conn.setServerConnectionCallback(this);
75 } catch (IOException e) {
76 logger.error("SocketThread error ", e);
81 public ServerSessionCallback acceptSession(final ServerSession session) {
82 SimpleServerSessionCallback cb = new SimpleServerSessionCallback() {
84 public Runnable requestSubsystem(final ServerSession ss, final String subsystem) throws IOException {
85 return new Runnable() {
88 if (subsystem.equals("netconf")) {
89 IOThread netconf_ssh_input = null;
90 IOThread netconf_ssh_output = null;
92 String hostName = clientAddress.getHostName();
93 int portNumber = clientAddress.getPort();
94 final Socket echoSocket = new Socket(hostName, portNumber);
95 logger.trace("echo socket created");
97 logger.trace("starting netconf_ssh_input thread");
98 netconf_ssh_input = new IOThread(echoSocket.getInputStream(), ss.getStdin(), "input_thread_" + sessionId, ss, conn);
99 netconf_ssh_input.setDaemon(false);
100 netconf_ssh_input.start();
102 logger.trace("starting netconf_ssh_output thread");
103 final String customHeader = "[" + currentUser + ";" + remoteAddressWithPort + ";ssh;;;;;;]\n";
104 netconf_ssh_output = new IOThread(ss.getStdout(), echoSocket.getOutputStream(), "output_thread_" + sessionId, ss, conn, customHeader);
105 netconf_ssh_output.setDaemon(false);
106 netconf_ssh_output.start();
108 } catch (Exception t) {
109 logger.error("SSH bridge could not create echo socket: {}", t.getMessage(), t);
112 if (netconf_ssh_input != null) {
113 netconf_ssh_input.join();
115 } catch (InterruptedException e1) {
116 Thread.currentThread().interrupt();
117 logger.error("netconf_ssh_input join error ", e1);
121 if (netconf_ssh_output != null) {
122 netconf_ssh_output.join();
124 } catch (InterruptedException e2) {
125 Thread.currentThread().interrupt();
126 logger.error("netconf_ssh_output join error ", e2);
130 String reason = "Only netconf subsystem is supported, requested:" + subsystem;
131 closeSession(ss, reason);
137 public void closeSession(ServerSession ss, String reason) {
138 logger.trace("Closing session - {}", reason);
140 ss.getStdin().write(reason.getBytes());
141 } catch (IOException e) {
142 logger.debug("Exception while closing session", e);
148 public Runnable requestPtyReq(final ServerSession ss, final PtySettings pty) throws IOException {
149 return new Runnable() {
152 closeSession(ss, "PTY request not supported");
158 public Runnable requestShell(final ServerSession ss) throws IOException {
159 return new Runnable() {
162 closeSession(ss, "Shell not supported");
172 public String initAuthentication(ServerConnection sc) {
173 logger.trace("Established connection with host {}", remoteAddressWithPort);
174 return "Established connection with host " + remoteAddressWithPort + "\r\n";
178 public String[] getRemainingAuthMethods(ServerConnection sc) {
179 return new String[]{ServerAuthenticationCallback.METHOD_PASSWORD};
183 public AuthenticationResult authenticateWithNone(ServerConnection sc, String username) {
184 return AuthenticationResult.FAILURE;
188 public AuthenticationResult authenticateWithPassword(ServerConnection sc, String username, String password) {
191 if (authProvider.authenticated(username, password)) {
192 currentUser = username;
193 logger.trace("user {}@{} authenticated", currentUser, remoteAddressWithPort);
194 return AuthenticationResult.SUCCESS;
196 } catch (Exception e) {
197 logger.warn("Authentication failed due to :" + e.getLocalizedMessage());
199 return AuthenticationResult.FAILURE;
203 public AuthenticationResult authenticateWithPublicKey(ServerConnection sc, String username, String algorithm,
204 byte[] publickey, byte[] signature) {
205 return AuthenticationResult.FAILURE;