Merge changes I393151af,I0a302cde
[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 java.io.IOException;
12 import java.net.InetSocketAddress;
13 import java.net.Socket;
14
15 import javax.annotation.concurrent.ThreadSafe;
16
17 import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider;
18 import org.slf4j.Logger;
19 import org.slf4j.LoggerFactory;
20
21 import ch.ethz.ssh2.AuthenticationResult;
22 import ch.ethz.ssh2.PtySettings;
23 import ch.ethz.ssh2.ServerAuthenticationCallback;
24 import ch.ethz.ssh2.ServerConnection;
25 import ch.ethz.ssh2.ServerConnectionCallback;
26 import ch.ethz.ssh2.ServerSession;
27 import ch.ethz.ssh2.ServerSessionCallback;
28 import ch.ethz.ssh2.SimpleServerSessionCallback;
29
30 @ThreadSafe
31 public class SocketThread implements Runnable, ServerAuthenticationCallback, ServerConnectionCallback {
32     private static final Logger logger =  LoggerFactory.getLogger(SocketThread.class);
33
34     private final Socket socket;
35     private final InetSocketAddress clientAddress;
36     private ServerConnection conn = null;
37     private final long sessionId;
38     private String currentUser;
39     private final String remoteAddressWithPort;
40     private final AuthProvider authProvider;
41
42
43     public static void start(Socket socket,
44                              InetSocketAddress clientAddress,
45                              long sessionId,
46                              AuthProvider authProvider) throws IOException{
47         Thread netconf_ssh_socket_thread = new Thread(new SocketThread(socket,clientAddress,sessionId,authProvider));
48         netconf_ssh_socket_thread.setDaemon(true);
49         netconf_ssh_socket_thread.start();
50     }
51     private SocketThread(Socket socket,
52                          InetSocketAddress clientAddress,
53                          long sessionId,
54                          AuthProvider authProvider) throws IOException {
55
56         this.socket = socket;
57         this.clientAddress = clientAddress;
58         this.sessionId = sessionId;
59         this.remoteAddressWithPort = socket.getRemoteSocketAddress().toString().replaceFirst("/","");
60         this.authProvider = authProvider;
61
62     }
63
64     @Override
65     public void run() {
66         conn = new ServerConnection(socket);
67         try {
68             conn.setPEMHostKey(authProvider.getPEMAsCharArray(),"netconf");
69         } catch (Exception e) {
70             logger.debug("Server authentication setup failed.");
71         }
72         conn.setAuthenticationCallback(this);
73         conn.setServerConnectionCallback(this);
74         try {
75             conn.connect();
76         } catch (IOException e) {
77             logger.error("SocketThread error ",e);
78         }
79     }
80     @Override
81     public ServerSessionCallback acceptSession(final ServerSession session)
82     {
83         SimpleServerSessionCallback cb = new SimpleServerSessionCallback()
84         {
85             @Override
86             public Runnable requestSubsystem(final ServerSession ss, final String subsystem) throws IOException
87             {
88                 return new Runnable(){
89                     @Override
90                     public void run()
91                     {
92                         if (subsystem.equals("netconf")){
93                             IOThread netconf_ssh_input = null;
94                             IOThread  netconf_ssh_output = null;
95                             try {
96                                 String hostName = clientAddress.getHostName();
97                                 int portNumber = clientAddress.getPort();
98                                 final Socket echoSocket = new Socket(hostName, portNumber);
99                                 logger.trace("echo socket created");
100
101                                 logger.trace("starting netconf_ssh_input thread");
102                                 netconf_ssh_input =  new IOThread(echoSocket.getInputStream(),ss.getStdin(),"input_thread_"+sessionId,ss,conn);
103                                 netconf_ssh_input.setDaemon(false);
104                                 netconf_ssh_input.start();
105
106                                 logger.trace("starting netconf_ssh_output thread");
107                                 final String customHeader = "["+currentUser+";"+remoteAddressWithPort+";ssh;;;;;;]\n";
108                                 netconf_ssh_output = new IOThread(ss.getStdout(),echoSocket.getOutputStream(),"output_thread_"+sessionId,ss,conn,customHeader);
109                                 netconf_ssh_output.setDaemon(false);
110                                 netconf_ssh_output.start();
111
112                             } catch (Throwable t){
113                                 logger.error("SSH bridge couldn't create echo socket",t.getMessage(),t);
114
115                                 try {
116                                     if (netconf_ssh_input!=null){
117                                         netconf_ssh_input.join();
118                                     }
119                                 } catch (InterruptedException e) {
120                                     Thread.currentThread().interrupt();
121                                    logger.error("netconf_ssh_input join error ",e);
122                                 }
123
124                                 try {
125                                     if (netconf_ssh_output!=null){
126                                         netconf_ssh_output.join();
127                                     }
128                                 } catch (InterruptedException e) {
129                                     Thread.currentThread().interrupt();
130                                     logger.error("netconf_ssh_output join error ",e);
131                                 }
132
133                             }
134                         } else {
135                             try {
136                                 ss.getStdin().write("wrong subsystem requested - closing connection".getBytes());
137                                 ss.close();
138                             } catch (IOException e) {
139                                 logger.debug("excpetion while sending bad subsystem response",e);
140                             }
141                         }
142                     }
143                 };
144             }
145             @Override
146             public Runnable requestPtyReq(final ServerSession ss, final PtySettings pty) throws IOException
147             {
148                 return new Runnable()
149                 {
150                     @Override
151                     public void run()
152                     {
153                         //noop
154                     }
155                 };
156             }
157
158             @Override
159             public Runnable requestShell(final ServerSession ss) throws IOException
160             {
161                 return new Runnable()
162                 {
163                     @Override
164                     public void run()
165                     {
166                         //noop
167                     }
168                 };
169             }
170         };
171
172         return cb;
173     }
174
175     @Override
176     public String initAuthentication(ServerConnection sc)
177     {
178         logger.trace("Established connection with host {}",remoteAddressWithPort);
179         return "Established connection with host "+remoteAddressWithPort+"\r\n";
180     }
181
182     @Override
183     public String[] getRemainingAuthMethods(ServerConnection sc)
184     {
185         return new String[] { ServerAuthenticationCallback.METHOD_PASSWORD };
186     }
187
188     @Override
189     public AuthenticationResult authenticateWithNone(ServerConnection sc, String username)
190     {
191         return AuthenticationResult.FAILURE;
192     }
193
194     @Override
195     public AuthenticationResult authenticateWithPassword(ServerConnection sc, String username, String password)
196     {
197
198         try {
199             if (authProvider.authenticated(username,password)){
200                 currentUser = username;
201                 logger.trace("user {}@{} authenticated",currentUser,remoteAddressWithPort);
202                 return AuthenticationResult.SUCCESS;
203             }
204         } catch (Exception e){
205             logger.warn("Authentication failed due to :" + e.getLocalizedMessage());
206         }
207         return AuthenticationResult.FAILURE;
208     }
209
210     @Override
211     public AuthenticationResult authenticateWithPublicKey(ServerConnection sc, String username, String algorithm,
212             byte[] publickey, byte[] signature)
213     {
214         return AuthenticationResult.FAILURE;
215     }
216
217 }