Merge "Fixed typo in SnapshotBackedWriteTransaction class"
[controller.git] / opendaylight / netconf / netconf-ssh / src / main / java / org / opendaylight / controller / netconf / ssh / threads / SocketThread.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.controller.netconf.ssh.threads;
9
10
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;
22
23 import javax.annotation.concurrent.ThreadSafe;
24 import java.io.IOException;
25 import java.net.InetSocketAddress;
26 import java.net.Socket;
27
28 @ThreadSafe
29 public class SocketThread implements Runnable, ServerAuthenticationCallback, ServerConnectionCallback {
30     private static final Logger logger = LoggerFactory.getLogger(SocketThread.class);
31
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;
39
40
41     public static void start(Socket socket,
42                              InetSocketAddress clientAddress,
43                              long sessionId,
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();
48     }
49
50     private SocketThread(Socket socket,
51                          InetSocketAddress clientAddress,
52                          long sessionId,
53                          AuthProvider authProvider) throws IOException {
54
55         this.socket = socket;
56         this.clientAddress = clientAddress;
57         this.sessionId = sessionId;
58         this.remoteAddressWithPort = socket.getRemoteSocketAddress().toString().replaceFirst("/", "");
59         this.authProvider = authProvider;
60
61     }
62
63     @Override
64     public void run() {
65         conn = new ServerConnection(socket);
66         try {
67             conn.setPEMHostKey(authProvider.getPEMAsCharArray(), "netconf");
68         } catch (Exception e) {
69             logger.warn("Server authentication setup failed.", e);
70         }
71         conn.setAuthenticationCallback(this);
72         conn.setServerConnectionCallback(this);
73         try {
74             conn.connect();
75         } catch (IOException e) {
76             logger.error("SocketThread error ", e);
77         }
78     }
79
80     @Override
81     public ServerSessionCallback acceptSession(final ServerSession session) {
82         SimpleServerSessionCallback cb = new SimpleServerSessionCallback() {
83             @Override
84             public Runnable requestSubsystem(final ServerSession ss, final String subsystem) throws IOException {
85                 return new Runnable() {
86                     @Override
87                     public void run() {
88                         if (subsystem.equals("netconf")) {
89                             IOThread netconf_ssh_input = null;
90                             IOThread netconf_ssh_output = null;
91                             try {
92                                 String hostName = clientAddress.getHostName();
93                                 int portNumber = clientAddress.getPort();
94                                 final Socket echoSocket = new Socket(hostName, portNumber);
95                                 logger.trace("echo socket created");
96
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();
101
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();
107
108                             } catch (Exception t) {
109                                 logger.error("SSH bridge could not create echo socket: {}", t.getMessage(), t);
110
111                                 try {
112                                     if (netconf_ssh_input != null) {
113                                         netconf_ssh_input.join();
114                                     }
115                                 } catch (InterruptedException e1) {
116                                     Thread.currentThread().interrupt();
117                                     logger.error("netconf_ssh_input join error ", e1);
118                                 }
119
120                                 try {
121                                     if (netconf_ssh_output != null) {
122                                         netconf_ssh_output.join();
123                                     }
124                                 } catch (InterruptedException e2) {
125                                     Thread.currentThread().interrupt();
126                                     logger.error("netconf_ssh_output join error ", e2);
127                                 }
128                             }
129                         } else {
130                             String reason = "Only netconf subsystem is supported, requested:" + subsystem;
131                             closeSession(ss, reason);
132                         }
133                     }
134                 };
135             }
136
137             public void closeSession(ServerSession ss, String reason) {
138                 logger.trace("Closing session - {}", reason);
139                 try {
140                     ss.getStdin().write(reason.getBytes());
141                 } catch (IOException e) {
142                     logger.debug("Exception while closing session", e);
143                 }
144                 ss.close();
145             }
146
147             @Override
148             public Runnable requestPtyReq(final ServerSession ss, final PtySettings pty) throws IOException {
149                 return new Runnable() {
150                     @Override
151                     public void run() {
152                         closeSession(ss, "PTY request not supported");
153                     }
154                 };
155             }
156
157             @Override
158             public Runnable requestShell(final ServerSession ss) throws IOException {
159                 return new Runnable() {
160                     @Override
161                     public void run() {
162                         closeSession(ss, "Shell not supported");
163                     }
164                 };
165             }
166         };
167
168         return cb;
169     }
170
171     @Override
172     public String initAuthentication(ServerConnection sc) {
173         logger.trace("Established connection with host {}", remoteAddressWithPort);
174         return "Established connection with host " + remoteAddressWithPort + "\r\n";
175     }
176
177     @Override
178     public String[] getRemainingAuthMethods(ServerConnection sc) {
179         return new String[]{ServerAuthenticationCallback.METHOD_PASSWORD};
180     }
181
182     @Override
183     public AuthenticationResult authenticateWithNone(ServerConnection sc, String username) {
184         return AuthenticationResult.FAILURE;
185     }
186
187     @Override
188     public AuthenticationResult authenticateWithPassword(ServerConnection sc, String username, String password) {
189
190         try {
191             if (authProvider.authenticated(username, password)) {
192                 currentUser = username;
193                 logger.trace("user {}@{} authenticated", currentUser, remoteAddressWithPort);
194                 return AuthenticationResult.SUCCESS;
195             }
196         } catch (Exception e) {
197             logger.warn("Authentication failed due to :" + e.getLocalizedMessage());
198         }
199         return AuthenticationResult.FAILURE;
200     }
201
202     @Override
203     public AuthenticationResult authenticateWithPublicKey(ServerConnection sc, String username, String algorithm,
204                                                           byte[] publickey, byte[] signature) {
205         return AuthenticationResult.FAILURE;
206     }
207
208 }