package tor.examples;
import tor.Consensus;
import tor.TorCircuit;
import tor.TorSocket;
import tor.TorStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
/**
* Created by gho on 08/08/14.
*/
public class PortForwarder {
static HashMap<SocketChannel,PortFwdClient> clients = new HashMap<>();
public static void main(String[] args) throws IOException {
if(args.length != 3) {
System.out.println("Usage: PortForwader listenPort remoteHost remotePort");
System.out.println();
System.out.println("If remoteHost is DIR then will establish connection to dir port on final router");
System.out.println();
return;
}
int LISTENPORT = Integer.parseInt(args[0]);
String REMOTE=args[1];
int PORT = Integer.parseInt(args[2]);
System.out.println("Connection to "+REMOTE+":"+PORT+ " when client connects on localhost:"+LISTENPORT);
// establish a circuit
Consensus con = Consensus.getConsensus();
// If you're having speed issues, try adding "Fast" to the lists of flags below.
TorSocket sock = new TorSocket(con.getRandomORWithFlag("Guard,Fast,Running,Valid"));
TorCircuit circ = sock.createCircuit(true);
circ.create();
circ.extend(con.getRandomORWithFlag("Exit,Fast,Valid,Running,HSDir".split(","), PORT));
circ.setBlocking(false);
// setup server socket and select
ServerSocketChannel serverSock = ServerSocketChannel.open();
serverSock.socket().bind(new InetSocketAddress(LISTENPORT));
serverSock.configureBlocking(false);
Selector select = Selector.open();
serverSock.register(select, SelectionKey.OP_ACCEPT);
while(true) {
select.select(1000);
Set keys = select.selectedKeys();
Iterator iterator = keys.iterator();
while (iterator.hasNext()) {
SelectionKey k = (SelectionKey) iterator.next();
if (!k.isValid())
continue;
// new connection?
if (k.isAcceptable() && k.channel() == serverSock) {
// new client
SocketChannel csock = serverSock.accept();
if(csock == null)
continue;
System.out.println("new client conn");
csock.configureBlocking(false);
csock.register(select, SelectionKey.OP_READ);
clients.put(csock, new PortFwdClient(csock, circ, REMOTE, PORT));
} else {
// data on client socket
PortFwdClient cl = clients.get(k.channel());
if(cl!=null)
cl.newClientData();
}
}
}
}
// represents a port forward client/server pair
static class PortFwdClient implements TorStream.TorStreamListener {
SocketChannel s;
TorCircuit circ;
TorStream stream;
PortFwdClient(SocketChannel s, TorCircuit circ, String host, int port) {
this.s = s;
this.circ = circ;
try {
// establish stream
if(host.equals("DIR"))
stream = circ.createDirStream(this);
else
stream = circ.createStream(host, port, this);
} catch (IOException e) {
removeMe();
}
}
// data from remote host (e.g. tor endpoint)
@Override
public void dataArrived(TorStream stream) {
try {
int availBytes = stream.getInputStream().available();
byte buf[] = new byte[availBytes];
stream.getInputStream().read(buf);
s.write(ByteBuffer.wrap(buf));
} catch (IOException e) {
removeMe();
}
}
@Override public void connected(TorStream s) { }
@Override public void disconnected(TorStream s) { removeMe(); }
@Override public void failure(TorStream s) { removeMe(); }
// data from client
public void newClientData() {
try {
ByteBuffer inbuf = ByteBuffer.allocate(16384);
if (s.read(inbuf) < 1)
return;
inbuf.flip();
byte buf[]= new byte[inbuf.limit()];
inbuf.get(buf);
stream.send(buf);
} catch (IOException e) {
removeMe();
}
}
// tear down this pair
public void removeMe() {
try {
stream.destroy();
s.close();
} catch (IOException e1) {
e1.printStackTrace();
}
clients.remove(s);
}
}
}