package com.notmacchallenge;
import java.io.*;
import java.net.*;
import java.security.KeyStore;
import java.security.Security;
import java.util.*;
import javax.net.ServerSocketFactory;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
public class HttpTunnel implements Runnable
{
String type = "";
Socket sock1 = null;
Socket sock2 = null;
BufferedInputStream in1;
String notMacIP = null;
String notMacPort = "443";
int port;
String ip;
String keystore;
String keystorepass;
String keypass;
static RandomAccessFile log = null;
static public void startTunnel()
{
Properties p = new Properties();
try
{
new Thread(new HttpTunnel(null,null,null,null,null,"terminator")).start();
p.load(new FileInputStream("/Library/Application Support/NotMac/httpTunnel.prop"));
if (p.getProperty("type").equals("clientServer") || p.getProperty("type").equals("bothServer"))
{
String ip = p.getProperty("ip");
String baseIP = ip.substring(0,ip.lastIndexOf(".")+1);
String endIP = ip.substring(ip.lastIndexOf(".")+1);
int end = Integer.parseInt(endIP);
for (int x=0; x<3; x++)
{
new Thread(new HttpTunnel(443,baseIP+(end+x),p.getProperty("keystore"+x),p.getProperty("keystorepass"),p.getProperty("keypass"),p.getProperty("notMacIP"),p.getProperty("notMacPort"),"clientServer")).start();
new Thread(new HttpTunnel(80,baseIP+(end+x),null,null,null,p.getProperty("notMacIP"),p.getProperty("notMacPort"),"clientServer")).start();
}
}
if (p.getProperty("type").equals("mainServer") || p.getProperty("type").equals("bothServer"))
{
for (int x=0; x<60; x++)
{
try
{
new Socket("127.0.0.210",443).close();
break;
}
catch(Exception e)
{
}
Thread.sleep(500);
}
new Thread(new HttpTunnel(Integer.parseInt(p.getProperty("notMacPort")),null,"/Library/Application Support/NotMac/notmac.keystore","password","password",p.getProperty("notMacIP"),p.getProperty("notMacPort"),"mainServer")).start();//bind to all IPs
}
}
catch(Exception e)
{
log(e);
}
}
public HttpTunnel(int port, String ip, String keystore, String keystorepass, String keypass, String notMacIP, String notMacPort, String type)
{
this.port=port;
this.ip=ip;
this.keystore=keystore;
this.keystorepass=keystorepass;
this.keypass=keypass;
this.type=type;
this.notMacIP = notMacIP;
this.notMacPort = notMacPort;
}
public HttpTunnel(BufferedInputStream in1, Socket sock1, Socket sock2, String notMacIP, String notMacPort, String type)
{
this.in1 = in1;
this.sock1=sock1;
this.sock2=sock2;
this.type=type;
this.notMacIP = notMacIP;
this.notMacPort = notMacPort;
}
public void run()
{
if (type.equalsIgnoreCase("terminator"))
{
try
{
ServerSocket terminator = new ServerSocket(53819,1000,InetAddress.getByName("127.0.0.1"));
terminator.accept().close();
System.exit(0);
}
catch(Exception e)
{
log(e);
}
System.exit(0);
}
else if (type.equalsIgnoreCase("tunnel"))
{
String ip1=sock1.getLocalAddress().getHostAddress();
String ip2=sock2.getLocalAddress().getHostAddress();
log("Tunneling " + ip1+":"+sock1.getLocalPort() + " to " + ip2+":"+sock2.getPort());
try
{
InputStream in = in1 == null?sock1.getInputStream():in1;
OutputStream out = sock2.getOutputStream();
log("Streaming from " + ip1 +":" + sock1.getPort() + " to " + ip2+":"+sock2.getPort());
byte b[] = new byte[32768];
int bytesRead = 0;
while(bytesRead >= 0)
{
bytesRead = in.read(b);
if (bytesRead > 0) {out.write(b,0,bytesRead);out.flush();}
}
}
catch(Exception e)
{
}
log("Closed " + ip1+":"+sock1.getPort() + " to " + ip2+":"+sock2.getPort());
try{if (in1 != null) in1.close();}catch(Exception e) {}
try {sock1.close();}catch(Exception e) {}
try{Thread.sleep(500);}catch(Exception e) {}
try {sock2.close();}catch(Exception e) {}
}
else if (type.equalsIgnoreCase("clientServer"))
{
String user = "test";
String pass = "test";
//load user and pass from config
try
{
ServerSocket ss = null;
if (port == 443) ss = getServerSocket(port,ip,keystore,keystorepass,keypass);
else if (ip == null) ss = new ServerSocket(port);
else ss = new ServerSocket(port,1000,InetAddress.getByName(ip));
log("Listening on: "+type+" "+ip+":"+port);
while(true)
{
Socket sockIN = ss.accept();
log("1.)Got connection:"+ip+":"+port);
log("Connecting to:"+notMacIP+":"+notMacPort);
Socket sockOUT = new Socket(notMacIP,Integer.parseInt(notMacPort));
if (Integer.parseInt(notMacPort) == 443)
{
TrustManager[] trustAllCerts = new TrustManager[]{ new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {return null;}
public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {}
public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {}
}};
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
sockOUT = (SSLSocket)sc.getSocketFactory().createSocket(sockOUT,notMacIP,Integer.parseInt(notMacPort),true);
((SSLSocket)sockOUT).setUseClientMode(true);
((SSLSocket)sockOUT).startHandshake();
}
log("2.)Got connection:"+notMacIP+":"+notMacPort);
new Thread(new HttpTunnel(null,sockIN,sockOUT,notMacIP,notMacPort,"tunnel")).start();//sock1 to sock2 read & write
new Thread(new HttpTunnel(null,sockOUT,sockIN,notMacIP,notMacPort,"tunnel")).start();//and sock2 to sock1 read & write
}
}
catch(Exception e)
{
log(e);
}
}
else if (type.equalsIgnoreCase("mainServer"))
{
try
{
ServerSocket ss = null;
if (port == 443) ss = getServerSocket(port,ip,keystore,keystorepass,keypass);
else if (ip == null) ss = new ServerSocket(port);
else ss = new ServerSocket(port,1000,InetAddress.getByName(ip));
log("Listening on: "+type+" "+ip+":"+port);
while(true)
{
try
{
Socket sockIN = ss.accept();
BufferedInputStream bi = new BufferedInputStream(sockIN.getInputStream());
Vector headers = getHeaders(bi);
String host = "";
int skipBytes = 0;
for (int x=0; x<headers.size(); x++)
{
String data = headers.elementAt(x).toString();
if (data.toUpperCase().startsWith("HOST:"))
{
host = data.substring(data.indexOf(":")+1).trim();
if (host.indexOf(":") >= 0) host = host.substring(0,host.indexOf(":"));
}
}
//validate username. if ok, continue.
boolean ok = true;
if (ok && (host.toUpperCase().endsWith(".MAC.COM") || host.toUpperCase().endsWith(".APPLE.COM")))
{
bi.skip(skipBytes);
Socket sockOUT = new Socket("127.0.0.1",53443);
TrustManager[] trustAllCerts = new TrustManager[]{ new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {return null;}
public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {}
public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {}
}};
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
sockOUT = (SSLSocket)sc.getSocketFactory().createSocket(sockOUT,notMacIP,53443,true);
((SSLSocket)sockOUT).setUseClientMode(true);
((SSLSocket)sockOUT).startHandshake();
new Thread(new HttpTunnel(bi,sockIN,sockOUT,notMacIP,notMacPort,"tunnel")).start();//sock1 to sock2 read & write
new Thread(new HttpTunnel(null,sockOUT,sockIN,notMacIP,notMacPort,"tunnel")).start();//and sock2 to sock1 read & write
}
else
{
sockIN.close();
}
}
catch(Exception ee)
{
log(ee);
}
}
}
catch(Exception e)
{
log(e);
}
}
}
public Vector getHeaders(BufferedInputStream original_is) throws Exception
{
Vector headers = new Vector();
byte headerBytes[] = new byte[32768];
String headerStr = "";
int bytesRead = 0;
original_is.mark(256000);
while (bytesRead >= 0)
{
bytesRead = original_is.read(headerBytes);
if (bytesRead > 0)
{
headerStr += new String(headerBytes,0,bytesRead);
}
if (headerStr.indexOf("\r\n\r\n") >= 0 || headerStr.length() > 128000) break;
}
if (headerStr.equals(""))
{
return null;
}
else
{
bytesRead = headerStr.indexOf("\r\n\r\n")+4;
original_is.reset();
headerStr = headerStr.substring(0,bytesRead).trim();
headerStr = NMCommon.url_decode(headerStr);
BufferedReader bsr = new BufferedReader(new StringReader(headerStr));
String data = "";
while ((data=bsr.readLine()) != null)
{
data = data.trim();
headers.addElement(data);
}
}
return headers;
}
public ServerSocket getServerSocket(int serverPort, String listen_ip, String KEYSTORE, String keystorepass, String keypass) throws Exception
{
ServerSocketFactory ssf = getSSLContext(KEYSTORE,keystorepass,keypass,"SSLv3").getServerSocketFactory();
SSLServerSocket serverSocket = null;
if (listen_ip == null) serverSocket = (SSLServerSocket) ssf.createServerSocket(serverPort,serverPort);
else serverSocket = (SSLServerSocket) ssf.createServerSocket(serverPort,1000,InetAddress.getByName(listen_ip));
serverSocket.setEnabledCipherSuites(serverSocket.getSupportedCipherSuites());
serverSocket.setNeedClientAuth(false);
return serverSocket;
}
public SSLContext getSSLContext(String KEYSTORE, String keystorepass, String keypass, String secureType) throws Exception
{
Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
KeyStore keystore = KeyStore.getInstance("JKS");
keystore.load(new FileInputStream(KEYSTORE), keystorepass.toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(keystore, keypass.toCharArray());
SSLContext sslc = SSLContext.getInstance(secureType);
sslc.init(kmf.getKeyManagers(), null, null);
return sslc;
}
static public void log(String s)
{
try
{
if (log == null)
{
log = new RandomAccessFile("/Library/Application Support/NotMac/httpTunnel.log","rw");;
log.seek(log.length());
}
log.write((s+"\r\n").getBytes());
System.out.println(s);
}
catch(Exception e)
{
}
}
static public void log(Exception e)
{
StackTraceElement ste[] = e.getStackTrace();
for (int x=0; x<ste.length; x++)
{
log(ste[x].getClassName()+"."+ste[x].getMethodName()+":"+ste[x].getLineNumber()+"\r\n");
}
e.printStackTrace();
}
}