package com.subgraph.orchid.socks; import java.net.Socket; import com.subgraph.orchid.TorConfig; import com.subgraph.orchid.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_COMMAND_RESOLV = 0xF0; final static int SOCKS5_COMMAND_RESOLV_PTR = 0xF1; 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; final static int SOCKS5_STATUS_COMMAND_NOT_SUPPORTED = 7; private int command; private int addressType; private byte[] addressBytes = new byte[0]; private byte[] portBytes = new byte[0]; Socks5Request(TorConfig config, Socket socket) { super(config, socket); } public boolean isConnectRequest() { return command == SOCKS5_COMMAND_CONNECT; } public int getCommandCode() { return command; } 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() throws SocksRequestException { if(!processAuthentication()) { throw new SocksRequestException("Failed to negotiate authentication"); } 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 SocksRequestException { sendResponse(SOCKS5_STATUS_CONNECTION_REFUSED); } public void sendError(boolean isUnsupportedCommand) throws SocksRequestException { if(isUnsupportedCommand) { sendResponse(SOCKS5_STATUS_COMMAND_NOT_SUPPORTED); } else { sendResponse(SOCKS5_STATUS_FAILURE); } } public void sendSuccess() throws SocksRequestException { sendResponse(SOCKS5_STATUS_SUCCESS); } private void sendResponse(int status) throws SocksRequestException { 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 boolean processAuthentication() throws SocksRequestException { 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; } if(foundAuthNone) { sendAuthenticationResponse(SOCKS5_AUTH_NONE); return true; } else { sendAuthenticationResponse(0xFF); return false; } } private void sendAuthenticationResponse(int method) throws SocksRequestException { final byte[] response = new byte[2]; response[0] = SOCKS5_VERSION; response[1] = (byte) method; socketWrite(response); } private byte[] readAddressBytes() throws SocksRequestException { 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() throws SocksRequestException { final int length = readByte(); final byte[] addrData = new byte[length + 1]; addrData[0] = (byte) length; readAll(addrData, 1, length); return addrData; } }