package org.torproject.jtor.socks.impl;
import java.io.IOException;
import java.net.Socket;
import org.torproject.jtor.TorException;
import org.torproject.jtor.circuits.CircuitManager;
import org.torproject.jtor.circuits.OpenStreamResponse;
import org.torproject.jtor.circuits.Stream;
import org.torproject.jtor.circuits.cells.RelayCell;
import org.torproject.jtor.logging.Logger;
public class SocksClientTask implements Runnable {
private final Socket socket;
private final Logger logger;
private final CircuitManager circuitManager;
SocksClientTask(Socket socket, Logger logger, CircuitManager circuitManager) {
this.socket = socket;
this.logger = logger;
this.circuitManager = circuitManager;
}
public void run() {
final int version = readByte();
dispatchRequest(version);
closeSocket();
}
private int readByte() {
try {
return socket.getInputStream().read();
} catch (IOException e) {
logger.warning("IO error reading version byte: "+ e.getMessage());
return -1;
}
}
private void dispatchRequest(int versionByte) {
switch(versionByte) {
case 'H':
case 'G':
case 'P':
sendHttpPage();
break;
case 4:
processRequest(new Socks4Request(socket));
break;
case 5:
processRequest(new Socks5Request(socket));
break;
default:
// fall through, do nothing
}
}
private void processRequest(SocksRequest request) {
try {
request.readRequest();
if(!request.isConnectRequest()) {
logger.warning("Non connect command");
request.sendError();
return;
}
final OpenStreamResponse openResponse = openConnectStream(request);
switch(openResponse.getStatus()) {
case STATUS_STREAM_OPENED:
request.sendSuccess();
runOpenConnection(openResponse.getStream());
break;
case STATUS_STREAM_ERROR:
if(openResponse.getErrorCode() == RelayCell.REASON_CONNECTREFUSED)
request.sendConnectionRefused();
else
request.sendError();
break;
default:
request.sendError();
break;
}
} catch (SocksRequestException e) {
logger.warning("Failure reading SOCKS request");
} catch (InterruptedException e) {
logger.warning("Stream open interrupted");
Thread.currentThread().interrupt();
} catch (IOException e) {
logger.warning("Error sending SOCKS response: "+ e);
}
}
private void runOpenConnection(Stream stream) {
SocksStreamConnection.runConnection(socket, stream, logger);
}
private OpenStreamResponse openConnectStream(SocksRequest request) throws InterruptedException {
if(request.hasHostname()) {
logger.debug("SOCKS CONNECT request to "+ request.getHostname() +":"+ request.getPort());
return circuitManager.openExitStreamTo(request.getHostname(), request.getPort());
} else {
logger.debug("SOCKS CONNECT request to "+ request.getAddress() +":"+ request.getPort());
return circuitManager.openExitStreamTo(request.getAddress(), request.getPort());
}
}
private void sendHttpPage() {
throw new TorException("Returning HTTP page not implemented");
}
private void closeSocket() {
try {
socket.close();
} catch (IOException e) {
logger.warning("Error closing SOCKS socket: "+ e.getMessage());
}
}
}