package org.openslx.taskmanager.tasks; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetSocketAddress; import java.security.cert.X509Certificate; import java.util.Date; 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; public class DirectedRelay extends AbstractTask { protected Output status = new Output(); @Expose public String ip; @Expose public int port; @Override protected boolean execute() { SSLContext ctx = getSSLContext(); if ( ctx == null ) return false; 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." ); relay( dbgSock, poolSock ); } catch ( Exception ex ) { throw( ex ); } } catch ( Exception e ) { status.addMessage( "ERROR: " + e.getMessage() ); return false; } return true; } 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 + "." ); status.setDbgAddr( addr ); } protected void bindToPort ( SSLServerSocket srvSock ) throws IOException { srvSock.bind( null ); status.setListenPort( srvSock.getLocalPort() ); status.addMessage( "INFO: Listening on localhost:" + status.getListenPort() + "." ); } protected SSLContext getSSLContext() { SSLContext ctx = null; try { ctx = trustAll(); status.addMessage( "INFO: SSLContext created." ); } catch ( Exception e ) { status.addMessage( "ERROR: Failed to create SSLContext." ); } return ctx; } 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(); try { for ( Relay r : new Relay[]{ toDbg, toPool }) r.join(); } catch ( InterruptedException ix ) { status.addMessage( "INFO: Relay closed: " + Thread.currentThread().getName() ); } } 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 ) {} } }; SSLContext ctx = SSLContext.getInstance( "SSL" ); ctx.init( null, trustAllMgr, new java.security.SecureRandom() ); return ctx; } private SSLServerSocket getSrvSock( SSLContext ctx ) throws IOException { SSLServerSocketFactory sssf = ctx.getServerSocketFactory(); return ( SSLServerSocket ) sssf.createServerSocket(); } private SSLSocket getDbgSock( SSLContext ctx ) throws IOException { SSLSocketFactory ssf = ctx.getSocketFactory(); return ( SSLSocket ) ssf.createSocket(); } @Override protected boolean initTask() { this.setStatusObject( this.status ); status.addMessage( "INFO: Initiating directed relay to debug server at: " + ip + ":" + port + "." ); return true; } public static class Output { protected String messages = null; protected Date d = null; protected InetSocketAddress dbgAddr = null; protected int listenPort; public void setListenPort ( int port ) { listenPort = port; } public int getListenPort () { return listenPort; } public void setDbgAddr ( InetSocketAddress addr ) { dbgAddr = addr; } public void addMessage( String str ) { d = new Date(); if ( messages == null ) { messages = d.toString() + "-" + str; } else { messages += "\n" + d.toString() + "-" + str; } } } 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() ) { while( active ) { readBytes = in.read( buffer ); out.write( buffer, 0, readBytes ); if ( first ) status.addMessage( "INFO: Relay operating: " + this.getName() ); } } catch ( Exception e ) { active = false; return; } } } }