package org.torproject.jtor.socks.impl;
import java.io.IOException;
import java.net.Socket;
import org.torproject.jtor.TorException;
public class Socks5Request extends SocksRequest {
final static int SOCKS5_VERSION = 5;
final static int SOCKS5_AUTH_NONE = 0;
final static int SOCKS5_COMMAND_CONNECT = 1;
final static int SOCKS5_ADDRESS_IPV4 = 1;
final static int SOCKS5_ADDRESS_HOSTNAME = 3;
final static int SOCKS5_ADDRESS_IPV6 = 4;
final static int SOCKS5_STATUS_SUCCESS = 0;
final static int SOCKS5_STATUS_FAILURE = 1;
final static int SOCKS5_STATUS_CONNECTION_REFUSED = 5;
private int command;
private int addressType;
private byte[] addressBytes;
private byte[] portBytes;
Socks5Request(Socket socket) {
super(socket);
}
public boolean isConnectRequest() {
return command == SOCKS5_COMMAND_CONNECT;
}
private String addressBytesToHostname() {
if(addressType != SOCKS5_ADDRESS_HOSTNAME)
throw new TorException("SOCKS 4 request is not a hostname request");
final StringBuilder sb = new StringBuilder();
for(int i = 1; i < addressBytes.length; i++) {
char c = (char) (addressBytes[i] & 0xFF);
sb.append(c);
}
return sb.toString();
}
public void readRequest() {
processAuthentication();
if(readByte() != SOCKS5_VERSION)
throw new SocksRequestException();
command = readByte();
readByte(); // Reserved
addressType = readByte();
addressBytes = readAddressBytes();
portBytes = readPortData();
if(addressType == SOCKS5_ADDRESS_IPV4)
setIPv4AddressData(addressBytes);
else if(addressType == SOCKS5_ADDRESS_HOSTNAME)
setHostname(addressBytesToHostname());
else
throw new SocksRequestException();
setPortData(portBytes);
}
public void sendConnectionRefused() throws IOException {
sendResponse(SOCKS5_STATUS_CONNECTION_REFUSED);
}
public void sendError() throws IOException {
sendResponse(SOCKS5_STATUS_FAILURE);
}
public void sendSuccess() throws IOException {
sendResponse(SOCKS5_STATUS_SUCCESS);
}
private void sendResponse(int status) throws IOException {
final int responseLength = 4 + addressBytes.length + portBytes.length;
final byte[] response = new byte[responseLength];
response[0] = SOCKS5_VERSION;
response[1] = (byte) status;
response[2] = 0;
response[3] = (byte) addressType;
System.arraycopy(addressBytes, 0, response, 4, addressBytes.length);
System.arraycopy(portBytes, 0, response, 4 + addressBytes.length, portBytes.length);
socketWrite(response);
}
private void processAuthentication() {
final int nmethods = readByte();
boolean foundAuthNone = false;
for(int i = 0; i < nmethods; i++) {
final int meth = readByte();
if(meth == SOCKS5_AUTH_NONE)
foundAuthNone = true;
}
final byte[] response = new byte[2];
response[0] = SOCKS5_VERSION;
response[1] = SOCKS5_AUTH_NONE;
try {
socketWrite(response);
} catch (IOException e) {
throw new SocksRequestException(e);
}
}
private byte[] readAddressBytes() {
switch(addressType) {
case SOCKS5_ADDRESS_IPV4:
return readIPv4AddressData();
case SOCKS5_ADDRESS_IPV6:
return readIPv6AddressData();
case SOCKS5_ADDRESS_HOSTNAME:
return readHostnameData();
default:
throw new SocksRequestException();
}
}
private byte[] readHostnameData() {
final int length = readByte();
final byte[] addrData = new byte[length + 1];
addrData[0] = (byte) length;
readAll(addrData, 1, length);
return addrData;
}
}