|
|
package org.openslx.taskmanager.tasks;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.openslx.taskmanager.api.AbstractTask;
import com.google.gson.annotations.Expose;
public class SSLRelayTask extends AbstractTask {
@Expose
private String clientAIp;
@Expose
private int clientAPort;
@Expose
private String clientBIp;
@Expose
private int clientBPort;
@Expose
private boolean auth;
private SSLSocket sockA;
private SSLSocket sockB;
private Relay aToB;
private Relay bToA;
private boolean enabled;
private Output status;
@Override
protected boolean initTask() {
this.setStatusObject(status);
return true;
}
@Override
protected boolean execute() {
SSLSocketFactory ssf = initSSLSocketFactory();
if (ssf==null) {
status.error = "Could not initialize SSLSocketFactory";
return false;
}
try {
initSockets(ssf);
initRelays();
} catch (UnknownHostException uhx) {
status.error = "One of the hosts to relay to/from is unknown: " + uhx.getMessage();
return close();
} catch (IOException iox) {
status.error = iox.getMessage();
return close();
}
Thread aToBThread = initThread('a');
Thread bToAThread = initThread('b');
if (aToBThread == null || bToAThread == null) {
status.error = "Could not initialize Threads.";
return close();
}
while(enabled) {
aToBThread.start();
bToAThread.start();
}
return close();
}
/**
* During testing phase there is the option to disable SSL/TLS authentication.
* THIS SHOULD BE REMOVED FOR ROLLOUT
* If auth == true, set system properties for key- and truststore and return default.
* Else, return SocketFactory from all-trusting SSLContext (returned by trustAll().
* @return
*/
private SSLSocketFactory initSSLSocketFactory () {
if (auth) {
System.setProperty("javax.net.ssl.keyStore", "keystore.jks");
System.setProperty("javax.net.ssl.trustStore", "cacerts.jks");
return (SSLSocketFactory) SSLSocketFactory.getDefault();
}
else {
try {
return trustAll().getSocketFactory();
} catch (NoSuchAlgorithmException nax) {
status.error = nax.getMessage();
return null;
} catch (KeyManagementException kmx) {
status.error = kmx.getMessage();
return null;
}
}
}
/**
* Returns one Thread to run the relay. Is there a nicer way to do this?
* Scoping posed some difficulties
* @param source
* @return
*/
private Thread initThread (char source) {
if (source == 'a') {
return new Thread() {
public void run() {
try {
aToB.relay();
} catch (IOException iox) {
status.error = iox.getMessage();
return;
} catch (InterruptedException ix) {
status.error = ix.getMessage();
return;
}
};
};
}
else if (source == 'b') {
return new Thread() {
public void run() {
try {
bToA.relay();
} catch (IOException iox) {
status.error = iox.getMessage();
return;
} catch (InterruptedException ix) {
status.error = ix.getMessage();
return;
}
};
};
}
else
return null;
}
/**
* Creates sockA and sockB
* @param ssf
* @throws IOException
* @throws UnknownHostException
*/
private void initSockets (SSLSocketFactory ssf) throws IOException, UnknownHostException {
sockA = (SSLSocket) ssf.createSocket(clientAIp, clientAPort);
System.out.println("connected to " + clientAIp + " on port " + Integer.toString(clientAPort));
sockB = (SSLSocket) ssf.createSocket(clientBIp, clientBPort);
System.out.println("connected to " + clientBIp + " on port " + Integer.toString(clientBPort));
}
/**
* Creates relays aToB and bToA
* @throws IOException
*/
private void initRelays () throws IOException {
aToB = new Relay(sockA, sockB);
System.out.println("relay created from " + clientAIp + " to " + clientBIp);
bToA = new Relay(sockB, sockA);
System.out.println("relay created from " + clientBIp + " to " + clientAIp);
}
/**
* Create all-trusting TrustManager for no-auth mode and return SSLContext.
*/
private SSLContext trustAll () throws NoSuchAlgorithmException, KeyManagementException {
TrustManager[] trustAllMan = 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, trustAllMan, new java.security.SecureRandom());
return ctx;
}
private boolean close() {
try {
if (aToB != null)
aToB.close();
if (bToA != null)
bToA.close();
sockA.close();
sockB.close();
} catch (IOException iox) {
status.error = iox.getMessage();
return false;
}
return true;
}
/**
* Do the actual relaying in one direction
*/
private class Relay {
private InputStream in;
private OutputStream out;
private byte[] buffer = new byte[16384];
public Relay (SSLSocket sIn, SSLSocket sOut) throws IOException {
in = sIn.getInputStream();
out = sOut.getOutputStream();
}
public void relay() throws IOException, InterruptedException {
int readBytes = in.read(buffer);
out.write(buffer, 0, readBytes);
}
public void close() throws IOException {
in.close();
out.close();
}
}
/**
* Output - contains additional status data of this task
*/
@SuppressWarnings( "unused" )
private static class Output
{
protected String error = null;
}
}
|