/**
* OnionCoffee - Anonymous Communication through TOR Network
* Copyright (C) 2005-2007 RWTH Aachen University, Informatik IV
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package TorJava.Proxy;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.net.Socket;
import uk.ac.cam.cl.dtg.android.tor.TorProxy.TorProxyControlService;
import TorJava.TCPStream;
import TorJava.TCPStreamProperties;
import TorJava.Tor;
import TorJava.TorKeeper;
class HTTPConnection extends Thread {
Socket local;
/** http header for CONNECT-calls */
private static final String success200 = "HTTP/1.0 200 Connection Established\r\n\r\n";
/** the error page including header */
private static final String error500a = "HTTP/1.0 500 Internal Server Error\r\n"
+ "Content-Type: text/html\r\n\r\n"
+ "<HTML><HEAD><TITLE>Internal Server Error</TITLE></HEAD>\r\n"
+ "<BODY><H1>Internal Server Error</H1>\r\n"
+ "The request failed.<br>\r\n<pre>";
private static final String error500b = "</pre>\r\n</BODY></HTML>\r\n";
/** just some $arbitrarily chosen variable ;-) **/
private static final int payload = 498; // maximum length of Tor-Cell
// Payload
private TorProxyControlService torService = null;
public HTTPConnection(Socket local, TorProxyControlService torService) {
// start connection
this.local = local;
this.torService = torService;
this.setName("HTTPConnection");
this.start();
}
private String FilterAndReplace(String input) {
// if (!MainWindow.processHTTPHeaders()) return input+"\r\n";
try {
int posColon = input.indexOf(':');
if (posColon < 0)
throw new Exception("HTTP Header without colon...");
String name = input.substring(0, posColon);
String value = input.substring(posColon + 1);
// filtered?
boolean filtered = false;
for (int i = 0; (i < TorJava.TorConfig.setFilteredHeaders.size())
&& (!filtered); ++i) {
String conf = ((String) TorJava.TorConfig.setFilteredHeaders
.elementAt(i)).toLowerCase();
int idx = name.toLowerCase().indexOf(conf);
filtered = ((idx >= 0) && (idx <= 2)); // fuzzy matching
}
if (filtered) {
// System.out.println("Removed header "+name);
return "";
}
// filter out, if client resends cookie that we send to him!
if (name.equalsIgnoreCase("Cookie")) {
// value should be something like: Route="TorJava
int idxTorJava = value.toLowerCase().indexOf("torjava");
int idxRoute = value.toLowerCase().indexOf("route");
if ((idxRoute + 6 <= idxTorJava)
&& (idxRoute + 8 >= idxTorJava)) {
int nextQM = value.indexOf("\"", idxTorJava);
System.err.println("filter out: " + idxRoute + " "
+ idxTorJava + " " + nextQM + " " + value);
if (nextQM < 0) {
value = value.substring(0, idxRoute - 1);
} else {
try {
String before = value.substring(0, idxRoute - 1);
String after = "";
if (nextQM + 1 < value.length())
after = value.substring(nextQM + 1);
value = before + after;
if (value.startsWith(";"))
value = value.substring(1);
if (value.startsWith(" "))
value = value.substring(1);
} catch (Exception e) {
System.err.println(e.getMessage());
e.printStackTrace();
}
}
System.err.println("filtered out to: " + value);
}
}
// substitute?
for (int i = 0; i < TorJava.TorConfig.setReplaceHeaders.size(); ++i) {
String[] replace = (String[]) TorJava.TorConfig.setReplaceHeaders
.elementAt(i);
if (name.equalsIgnoreCase(replace[0])) {
// System.out.println("replacing > "+name+": "+value);
value = replace[1];
}
}
// System.out.println(input+" > "+name+": "+value);
return name + ":" + value + "\r\n";
} catch (Exception e) {
// if ANY error occurs, just copy the header (i.e. 'error' in this
// case is also the first line
// of an http-response... so be careful here!
// System.out.println("header "+input+" is strange");
return input + "\r\n";
}
}
public void run() {
final int PROTOCOL_HTTP = 1;
final int PROTOCOL_HTTPS = 2;
TCPStream remoteA = null;
Socket remoteS = null;
int protocol = PROTOCOL_HTTP;
try {
Tor tor = TorKeeper.getTor();
// prepare parsing of http-request
BufferedReader br = new BufferedReader(new InputStreamReader(local
.getInputStream()));
StringBuffer request = new StringBuffer(5000);
String host = null;
int port = 80;
int body_length = 0;
// parsing first line
String header = br.readLine().trim();
this.setName("HTTPConnection " + header);
// System.out.println("Parsing '"+header+"'");
String[] parts = header.split("[ ]");
if (parts[0].startsWith("CONNECT")) {
protocol = PROTOCOL_HTTPS;
// HTTPS (or whatever else)
String[] tt = parts[1].split("[:]");
host = tt[0].trim();
port = Integer.parseInt(tt[1].trim());
}
// HTTP-Protocol
request.append(parts[0] + " ");
if (parts[1].startsWith("http://")) {
String uri_without_protocol = parts[1].substring(7);
int pos_next_slash = uri_without_protocol.indexOf('/');
host = uri_without_protocol.substring(0, pos_next_slash - 1);
if (host.indexOf(':') >= 0) {
try {
String[] tt = header.split("[:]");
host = tt[0].trim();
port = Integer.parseInt(tt[1].trim());
} catch (Exception e) {
}
}
request.append(uri_without_protocol.substring(pos_next_slash));
} else if (protocol != PROTOCOL_HTTPS) {
throw new Exception("unsupported protocol");
}
for (int i = 2; i < parts.length; ++i)
request.append(" " + parts[i]);
request.append("\r\n");
// parse more header of http-request
header = br.readLine().trim();
while (header.length() > 0) {
if (header.startsWith("Host: ")) {
host = header.substring(5).trim();
//System.out.println("Overwriting host with "+host+" from '"
// +header+"'");
if (host.indexOf(':') >= 0) {
try {
String[] tt = host.split("[:]");
host = tt[0].trim();
port = Integer.parseInt(tt[1].trim());
} catch (Exception e) {
}
}
} else if (header.startsWith("Content-Length: ")) {
try {
String[] len = header.split("[:]");
body_length = Integer.parseInt(len[1].trim());
} catch (Exception e) {
body_length = 0;
}
}
if (!header.startsWith("Connection:")) {
//Log.w("HTTPConnection", header);
request.append(FilterAndReplace(header));
}
header = br.readLine().trim();
}
request.append("Connection: close\r\n\r\n");
// read request body
char[] body = new char[body_length];
br.read(body, 0, body_length);
request.append(body);
// remote connection
OutputStream remoteOutput;
InputStream remoteInput;
String additionalHeader = "";
if (torService.isTorProcessActive()) {
//Log.w("HTTPConnection", "Host: " + host + "; Port: " + port);
remoteA = tor.connect(new TCPStreamProperties(host, port));
//Log.w("HTTPConnection", "After Host: " + host + "; Port: " + port);
remoteOutput = remoteA.getOutputStream();
remoteInput = remoteA.getInputStream();
String strRoute = remoteA.getRoute();
// additionalHeader = "X-Anonymized: TorJava"++"\r\n";
additionalHeader = "Set-Cookie: Route=\"TorJava" + strRoute
+ "\" ; Max-Age=300; Discard\r\n";
} else {
remoteS = new Socket(host, port);
remoteOutput = remoteS.getOutputStream();
remoteInput = remoteS.getInputStream();
}
if (protocol == PROTOCOL_HTTP) {
// write http-request
DataOutputStream remote_write = new DataOutputStream(
remoteOutput);
remote_write.write(request.toString().getBytes());
remote_write.flush();
// relay http-response - HEADER
BufferedWriter local_write = new BufferedWriter(
new OutputStreamWriter(local.getOutputStream()));
br = new BufferedReader(new InputStreamReader(remoteInput));
header = br.readLine().trim();
while (header.length() > 0) {
local_write.write(FilterAndReplace(header));
header = br.readLine().trim();
}
// add custom HTTP-Header
local_write.write(additionalHeader);
// finish HTTP-header
local_write.write("\r\n");
// relay http-response - BODY
char[] data = new char[payload];
int length;
length = br.read(data, 0, payload);
while (length > 0) {
local_write.write(data, 0, length);
length = br.read(data, 0, payload);
}
local_write.flush();
} else if (protocol == PROTOCOL_HTTPS) {
// send HTTP-success message
DataOutputStream local_write = new DataOutputStream(local
.getOutputStream());
local_write.write(success200.getBytes());
local_write.flush();
// relay subsequent data
SocksConnection.relay(local, new DataInputStream(remoteInput),
new DataOutputStream(remoteOutput));
}
}
// pretty print error and display to client
catch (Exception e) {
try {
// System.err.println(e);
// e.printStackTrace();
DataOutputStream local_write = new DataOutputStream(local
.getOutputStream());
local_write.write(error500a.getBytes());
local_write.flush();
e.printStackTrace(new PrintStream(local.getOutputStream()));
local_write.write(error500b.getBytes());
local_write.flush();
} catch (Exception e2) {
}
} finally {
try {
local.close();
} catch (Exception e) {
}
;
try {
remoteA.close();
} catch (Exception e) {
}
;
try {
remoteS.close();
} catch (Exception e) {
}
;
}
}
}