package TorJava;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.ArrayList;
public class DefaultHiddenServiceRequestHandler implements HiddenServiceRequestHandler {
private int mLocalPort = 0;
private boolean closed = false;
ArrayList<StreamFerry> mThreads = new ArrayList<StreamFerry>();
public DefaultHiddenServiceRequestHandler(int port) {
this.mLocalPort = port;
}
/**
* accept an incoming hidden service connection
*/
@Override
public boolean accept(TCPStream incomingConnection) {
try {
synchronized(this) {
if (closed) return false;
Socket localServer = new Socket("127.0.0.1", mLocalPort);
mThreads.add(new StreamFerry(incomingConnection, localServer, true));
mThreads.add(new StreamFerry(incomingConnection, localServer, false));
}
} catch (IOException e) {
// Try to notify the remote client
incomingConnection.close();
}
return true;
}
/**
* StreamFerry ferries data between a hidden service connection and a local server
* @author cmg47
*
*/
private class StreamFerry extends Thread {
public static final int mBufferSize = 498;
private TCPStream remoteClient = null;
private Socket localServer = null;
private InputStream in = null;
private OutputStream out = null;
private boolean stopped = false;
public StreamFerry(TCPStream remoteClient, Socket localServer, boolean leaving) {
this.remoteClient = remoteClient;
this.localServer = localServer;
try {
if (leaving) {
this.in = localServer.getInputStream();
this.out = remoteClient.getOutputStream();
} else {
this.in = remoteClient.getInputStream();
this.out = localServer.getOutputStream();
}
this.start();
} catch (IOException e) {
// IOException occurs on local server connection
remoteClient.close();
}
}
@Override
public void run() {
super.run();
byte[] buffer = new byte[mBufferSize];
int read;
while (!stopped) {
try {
read = in.read(buffer);
if (read <= 0) close();
else {
out.write(buffer, 0, read);
out.flush();
}
} catch (IOException e) {
close();
}
}
}
/**
* close this connection
*/
public void close() {
stopped = true;
this.interrupt();
try {
if (!remoteClient.closed) remoteClient.close();
if (!localServer.isClosed()) localServer.close();
} catch (IOException e) {
// Oh well, we tried.
}
threadFinished(this);
}
}
/**
* for StreamFerrys when they have finished ferrying
* @param s
*/
private void threadFinished(StreamFerry s) {
mThreads.remove(s);
}
/**
* stop all threads
*/
@Override
public void close() {
synchronized(this) {
closed = true;
StreamFerry[] ferries = new StreamFerry[mThreads.size()];
mThreads.toArray(ferries);
for (int i=0; i<ferries.length; i++) {
if (ferries[i] != null) ferries[i].close();
}
mThreads.clear();
}
}
}