summaryrefslogblamecommitdiffstats
path: root/src/main/java/org/openslx/taskmanager/tasks/RemoteDebug.java
blob: a30db08178db8631dcba488dee7116454c692b63 (plain) (tree)
1
2
3
4
5
6
7
8
9

                                      
                               


                            


                            
 
                              
                                          
 

                      


                                  
                                       











                                                




                                              








                                               


                         


                                   
                                                        
                                                 

                                                                          
                                     

                                                                              






                                                                                                      
                                                                               







                                                                        
                                                                     


                                        




                                                                                            



                                                                                                   

         




                                                                                           





                                                                                                    



                                                                



                                              

                                                                                   
                                                                               



                           




                                                                                                          
                                                                     





                                                                      
                                                              






                                                                                                       

         




                                                                                                        









                                                                                                             








                                                                                                       
                                                                 
                                                                                                


                           




                                  

                                                                               
                                                                     
                                                                           



                                                                                  

         




                                  

                                                                         
                                                                    
                                                              


                                                                   

         



                                                                           







                                                                                                                    



                                                                                                                            




                                                 
                                                     
                                              


                                                                             
                                                                                 











                                                                            



                                                                                    





















                                                                                         
                                                                               


                                                                          
                                                      
                                                                                                                

                                                              

                                                 
                                                                                                                




                                               






                                                                     
 

 
package org.openslx.taskmanager.tasks;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;

import java.security.KeyStore;
import java.security.cert.X509Certificate;

import java.util.Date;

import java.net.InetAddress;
import java.net.InetSocketAddress;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.openslx.taskmanager.api.AbstractTask;

import com.google.gson.annotations.Expose;

/**
 * @author Christoph Schulthess
 *
 */
public class RemoteDebug extends AbstractTask 
{
	
	protected Output status = new Output();
	
	@Expose
	public String ip;
	@Expose 
	public int port;
	
	/**
	 * Run debug task
	 */
	@Override
	protected boolean execute()
	{
		status.addMessage( "INFO: Executing." );
		SSLContext ctx = getSSLContext();
		if ( ctx == null ) {
			status.addMessage( "ERROR: SSLContext is null." );
			return false;
		}
		status.addMessage( "INFO: SSLContext successfully created." );
		
		try ( SSLSocket dbgSock = getDbgSock( ctx );
				SSLServerSocket srvSock = getSrvSock( ctx ) ) {
			connectToDbg( dbgSock );
			bindToPort( srvSock );
			try ( SSLSocket poolSock = ( SSLSocket ) srvSock.accept() ) {
				status.addMessage( "INFO: Connection from pool client established." );
				status.setCltAddr( poolSock.getInetAddress() );
				relay( dbgSock, poolSock );
			} catch ( Exception ex ) {
				throw( ex );
			}
		} catch ( Exception e ) {
			status.addMessage( "ERROR: " + e.getMessage() );
			return false;
		}
		status.addMessage( "INFO: Task finished properly." );
		return true;		
	}
	
	/**
	 * @param SSLSocket dbgSock
	 * @throws IOException
	 * Connect SSLSocket dbgSock to debug server at ip:port specified by the JSON object
	 */
	protected void connectToDbg ( SSLSocket dbgSock ) throws IOException {
		InetSocketAddress addr = new InetSocketAddress( ip, port );
		dbgSock.connect( addr );
		status.addMessage( "INFO: Connected to debug server at " + ip + ":" + port + "." );
	}
	
	/**
	 * @param SSLServerSocket srvSock
	 * @throws IOException
	 * Bind SSLServerSocket srvSock to local port and update status object accordingly.
	 */
	protected void bindToPort ( SSLServerSocket srvSock ) throws IOException {
		srvSock.bind( null );
		status.setListenPort( srvSock.getLocalPort() );
		status.addMessage( "INFO: Listening on localhost:" + status.getListenPort() + "." );
	}
	
	/**
	 * @return SSLContext
	 * Get all-trusting SSLContext via the trustAll() method
	 */
	protected SSLContext getSSLContext() {
		SSLContext ctx = null;
		try {
			ctx = trustAll();
		} catch ( Exception e ) {
			status.addMessage( "ERROR: Failed to create SSLContext." );
			//status.addMessage( "DEBUG: " + getStrStackTrace(e) );
		}
		return ctx;
	}

	/**
	 * @param dbgSock - SSLSocket connected to debug server
	 * @param poolSock - SSLSocket retrieved via srvSock.accept()
	 * Create and start the two necessary relays. Join them, so that proper postcondition is enforced.
	 */
	private void relay( SSLSocket dbgSock, SSLSocket poolSock ) {
		Relay toDbg = new Relay( poolSock, dbgSock, status );
		Relay toPool = new Relay( dbgSock, poolSock, status );
		toDbg.setName( "PoolToDebug" );
		toPool.setName( "DebugToPool" );
		toDbg.start();
		toPool.start();
		status.addMessage( "INFO: Threads started." );
		
		try {
			for ( Relay r : new Relay[]{ toDbg, toPool })
				r.join();
		} catch ( InterruptedException ix ) {
			status.addMessage( "INFO: Relay closed: " + Thread.currentThread().getName() );
		}
	}
	
	/**
	 * @return SSLContext
	 * @throws Exception
	 * Create all-trusting X509 certificate manager to disable verification in the bwlp-environment.
	 */
	protected SSLContext trustAll () throws Exception 
	{
		TrustManager[] trustAllMgr = new TrustManager[] { new X509TrustManager() {
				public java.security.cert.X509Certificate[] getAcceptedIssuers() {
					return null;
				}
				public void checkClientTrusted( X509Certificate[] certs, String authType ) {}
				public void checkServerTrusted( X509Certificate[] certs, String authType ) {}
			}
		};
		
		KeyStore ks = KeyStore.getInstance( "JKS" );
		try ( InputStream ksIs = new FileInputStream( "/opt/taskmanager/data/keystore.jks" ) ){
			ks.load( ksIs, "password".toCharArray() );
		}
		KeyManagerFactory kmf = KeyManagerFactory.getInstance( 
				KeyManagerFactory.getDefaultAlgorithm() );
		kmf.init( ks, "password".toCharArray() );
		
		SSLContext ctx = SSLContext.getInstance( "SSL" );
		ctx.init( kmf.getKeyManagers(), trustAllMgr, new java.security.SecureRandom() );
		return ctx;
	}

	/**
	 * @param ctx - SSLContext
	 * @return SSLServerSocket
	 * @throws IOException
	 */
	private SSLServerSocket getSrvSock( SSLContext ctx ) throws IOException
	{
		status.addMessage( "INFO: Creating server socket." );
		SSLServerSocketFactory sssf = ctx.getServerSocketFactory();
		status.addMessage( "INFO: Server socket factory created." );
		SSLServerSocket s = ( SSLServerSocket ) sssf.createServerSocket();
		status.addMessage( "INFO: Server socket created." );
		return s;
	}

	/**
	 * @param ctx - SSLContext
	 * @return SSLSocket
	 * @throws IOException
	 */
	private SSLSocket getDbgSock( SSLContext ctx ) throws IOException
	{
		status.addMessage( "INFO: Creating debug socket." );
		SSLSocketFactory ssf = ctx.getSocketFactory();
		SSLSocket s = ( SSLSocket ) ssf.createSocket();
		status.addMessage( "INFO: Debug socket created." );
		return s;
	}
	
	
	/**
	 * Set status class for this object and push a first message to it.
	 */
	@Override
	protected boolean initTask() 
	{
		this.setStatusObject( this.status );
		status.addMessage( "INFO: Initiating directed relay to debug server at: " + ip + ":" + port + "." );
		return true;
	}
	
	/**
	 * Status class that holds information about the current debug task.
	 * Most important is probably the listen port which is sent to the client to connect its VNC server in reverse mode.
	 */
	public static class Output 
	{		
		
		protected String messages = null;
		protected Date d = null;
		protected InetAddress cltAddr = null;
		protected int listenPort = -1;
		
		public void setListenPort ( int port ) { listenPort = port; }
		public int getListenPort ()	{ return listenPort; }
		public void setCltAddr ( InetAddress addr ) { cltAddr = addr;	}
		
		public void addMessage( String str )
		{
			d = new Date();
			if ( messages == null ) {
				messages = d.toString() + "-" + str;
			} else {
				messages += "\n" + d.toString() + "-" + str;
			}
		}
	}
	
	/**
	 * Generic relay class to write data from one socket to another.
	 * For a working debug task, you need two of these - one for each direction.
	 */
	private class Relay extends Thread
	{
		boolean active = true;
		private byte[] buffer = new byte[16384];
		
		private SSLSocket srcSock;
		private SSLSocket destSock;
		
		private Output status;
		
		public Relay ( SSLSocket srcSock, SSLSocket destSock, Output status ) {
			this.srcSock = srcSock;
			this.destSock = destSock;
			this.status = status;
		}
		
		@Override
		public void run() {
			boolean first = true;
			int readBytes;
			try ( InputStream in = srcSock.getInputStream();
					OutputStream out = destSock.getOutputStream() ) {
				status.addMessage( "INFO: " + this.getName() );
				while( active ) {
					readBytes = in.read( buffer );
					out.write( buffer, 0, readBytes );
					if ( first ) {
						status.addMessage( "INFO: Relay operating: " + this.getName() );
						first = false;
					}
				}	
			} catch ( Exception e ) {
				status.addMessage( "DEBUG: " + this.getName() + " - " + getStrStackTrace( e ) );
				active = false;
				return;
			}
		}	
	}

	public static String getStrStackTrace(Throwable aThrowable) {
		Writer result = new StringWriter();
		PrintWriter printWriter = new PrintWriter(result);
		aThrowable.printStackTrace(printWriter);
		return result.toString();
	}
}