325 lines
8.2 KiB
Java
325 lines
8.2 KiB
Java
package com.runjva.sourceforge.jsocks.protocol;
|
|
|
|
import java.io.DataInputStream;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.OutputStream;
|
|
import java.net.InetAddress;
|
|
import java.net.UnknownHostException;
|
|
|
|
/**
|
|
* SOCKS5 request/response message.
|
|
*/
|
|
|
|
class Socks5Message extends ProxyMessage {
|
|
/** Address type of given message */
|
|
public int addrType;
|
|
|
|
byte[] data;
|
|
|
|
|
|
/**
|
|
* Server error response.
|
|
*
|
|
* @param cmd
|
|
* Error code.
|
|
*/
|
|
public Socks5Message(int cmd) {
|
|
super(cmd, null, 0);
|
|
data = new byte[3];
|
|
data[0] = SOCKS_VERSION; // Version.
|
|
data[1] = (byte) cmd; // Reply code for some kind of failure.
|
|
data[2] = 0; // Reserved byte.
|
|
}
|
|
|
|
/**
|
|
* Construct client request or server response.
|
|
*
|
|
* @param cmd
|
|
* - Request/Response code.
|
|
* @param ip
|
|
* - IP field.
|
|
* @paarm port - port field.
|
|
*/
|
|
public Socks5Message(int cmd, InetAddress ip, int port) {
|
|
super(cmd, ip, port);
|
|
|
|
if (ip == null) {
|
|
this.host = "0.0.0.0";
|
|
} else {
|
|
this.host = ip.getHostName();
|
|
}
|
|
|
|
this.version = SOCKS_VERSION;
|
|
|
|
byte[] addr;
|
|
|
|
if (ip == null) {
|
|
addr = new byte[4];
|
|
addr[0] = addr[1] = addr[2] = addr[3] = 0;
|
|
} else {
|
|
addr = ip.getAddress();
|
|
}
|
|
|
|
if (addr.length == 4) {
|
|
addrType = SOCKS_ATYP_IPV4;
|
|
} else {
|
|
addrType = SOCKS_ATYP_IPV6;
|
|
}
|
|
|
|
data = new byte[6 + addr.length];
|
|
data[0] = (byte) SOCKS_VERSION; // Version
|
|
data[1] = (byte) command; // Command
|
|
data[2] = (byte) 0; // Reserved byte
|
|
data[3] = (byte) addrType; // Address type
|
|
|
|
// Put Address
|
|
System.arraycopy(addr, 0, data, 4, addr.length);
|
|
// Put port
|
|
data[data.length - 2] = (byte) (port >> 8);
|
|
data[data.length - 1] = (byte) (port);
|
|
}
|
|
|
|
/**
|
|
* Construct client request or server response.
|
|
*
|
|
* @param cmd
|
|
* - Request/Response code.
|
|
* @param hostName
|
|
* - IP field as hostName, uses ADDR_TYPE of HOSTNAME.
|
|
* @paarm port - port field.
|
|
*/
|
|
public Socks5Message(int cmd, String hostName, int port) {
|
|
super(cmd, null, port);
|
|
this.host = hostName;
|
|
this.version = SOCKS_VERSION;
|
|
|
|
addrType = SOCKS_ATYP_DOMAINNAME;
|
|
final byte addr[] = hostName.getBytes();
|
|
|
|
data = new byte[7 + addr.length];
|
|
data[0] = (byte) SOCKS_VERSION; // Version
|
|
data[1] = (byte) command; // Command
|
|
data[2] = (byte) 0; // Reserved byte
|
|
data[3] = (byte) SOCKS_ATYP_DOMAINNAME; // Address type
|
|
data[4] = (byte) addr.length; // Length of the address
|
|
|
|
// Put Address
|
|
System.arraycopy(addr, 0, data, 5, addr.length);
|
|
// Put port
|
|
data[data.length - 2] = (byte) (port >> 8);
|
|
data[data.length - 1] = (byte) (port);
|
|
}
|
|
|
|
/**
|
|
* Initialises Message from the stream. Reads server response from given
|
|
* stream.
|
|
*
|
|
* @param in
|
|
* Input stream to read response from.
|
|
* @throws SocksException
|
|
* If server response code is not SOCKS_SUCCESS(0), or if any
|
|
* error with protocol occurs.
|
|
* @throws IOException
|
|
* If any error happens with I/O.
|
|
*/
|
|
public Socks5Message(InputStream in) throws SocksException, IOException {
|
|
this(in, true);
|
|
}
|
|
|
|
/**
|
|
* Initialises Message from the stream. Reads server response or client
|
|
* request from given stream.
|
|
*
|
|
* @param in
|
|
* Input stream to read response from.
|
|
* @param clinetMode
|
|
* If true read server response, else read client request.
|
|
* @throws SocksException
|
|
* If server response code is not SOCKS_SUCCESS(0) and reading
|
|
* in client mode, or if any error with protocol occurs.
|
|
* @throws IOException
|
|
* If any error happens with I/O.
|
|
*/
|
|
public Socks5Message(InputStream in, boolean clientMode)
|
|
throws SocksException, IOException {
|
|
|
|
read(in, clientMode);
|
|
}
|
|
|
|
/**
|
|
* Initialises Message from the stream. Reads server response from given
|
|
* stream.
|
|
*
|
|
* @param in
|
|
* Input stream to read response from.
|
|
* @throws SocksException
|
|
* If server response code is not SOCKS_SUCCESS(0), or if any
|
|
* error with protocol occurs.
|
|
* @throws IOException
|
|
* If any error happens with I/O.
|
|
*/
|
|
public void read(InputStream in) throws SocksException, IOException {
|
|
read(in, true);
|
|
}
|
|
|
|
/**
|
|
* Initialises Message from the stream. Reads server response or client
|
|
* request from given stream.
|
|
*
|
|
* @param in
|
|
* Input stream to read response from.
|
|
* @param clinetMode
|
|
* If true read server response, else read client request.
|
|
* @throws SocksException
|
|
* If server response code is not SOCKS_SUCCESS(0) and reading
|
|
* in client mode, or if any error with protocol occurs.
|
|
* @throws IOException
|
|
* If any error happens with I/O.
|
|
*/
|
|
public void read(InputStream in, boolean clientMode) throws SocksException,
|
|
IOException {
|
|
|
|
data = null;
|
|
ip = null;
|
|
|
|
final DataInputStream di = new DataInputStream(in);
|
|
|
|
version = di.readUnsignedByte();
|
|
command = di.readUnsignedByte();
|
|
|
|
if (clientMode && (command != 0)) {
|
|
throw new SocksException(command);
|
|
}
|
|
|
|
di.readUnsignedByte();
|
|
addrType = di.readUnsignedByte();
|
|
|
|
byte addr[];
|
|
|
|
switch (addrType) {
|
|
case SOCKS_ATYP_IPV4:
|
|
addr = new byte[4];
|
|
di.readFully(addr);
|
|
host = bytes2IPV4(addr, 0);
|
|
break;
|
|
case SOCKS_ATYP_IPV6:
|
|
addr = new byte[SOCKS_IPV6_LENGTH];// I believe it is 16 bytes,huge!
|
|
di.readFully(addr);
|
|
host = bytes2IPV6(addr, 0);
|
|
break;
|
|
case SOCKS_ATYP_DOMAINNAME:
|
|
// log.debug("Reading ATYP_DOMAINNAME");
|
|
addr = new byte[di.readUnsignedByte()];// Next byte shows the length
|
|
di.readFully(addr);
|
|
host = new String(addr);
|
|
break;
|
|
default:
|
|
throw (new SocksException(SocksProxyBase.SOCKS_JUST_ERROR));
|
|
}
|
|
|
|
port = di.readUnsignedShort();
|
|
|
|
if ((addrType != SOCKS_ATYP_DOMAINNAME) && doResolveIP) {
|
|
try {
|
|
ip = InetAddress.getByName(host);
|
|
} catch (final UnknownHostException uh_ex) {
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Writes the message to the stream.
|
|
*
|
|
* @param out
|
|
* Output stream to which message should be written.
|
|
*/
|
|
public void write(OutputStream out) throws SocksException, IOException {
|
|
if (data == null) {
|
|
Socks5Message msg;
|
|
|
|
if (addrType == SOCKS_ATYP_DOMAINNAME) {
|
|
msg = new Socks5Message(command, host, port);
|
|
} else {
|
|
if (ip == null) {
|
|
try {
|
|
ip = InetAddress.getByName(host);
|
|
} catch (final UnknownHostException uh_ex) {
|
|
throw new SocksException(
|
|
SocksProxyBase.SOCKS_JUST_ERROR);
|
|
}
|
|
}
|
|
msg = new Socks5Message(command, ip, port);
|
|
}
|
|
data = msg.data;
|
|
}
|
|
out.write(data);
|
|
}
|
|
|
|
/**
|
|
* Returns IP field of the message as IP, if the message was created with
|
|
* ATYP of HOSTNAME, it will attempt to resolve the hostname, which might
|
|
* fail.
|
|
*
|
|
* @throws UnknownHostException
|
|
* if host can't be resolved.
|
|
*/
|
|
public InetAddress getInetAddress() throws UnknownHostException {
|
|
if (ip != null) {
|
|
return ip;
|
|
}
|
|
|
|
return (ip = InetAddress.getByName(host));
|
|
}
|
|
|
|
/**
|
|
* Returns string representation of the message.
|
|
*/
|
|
public String toString() {
|
|
// FIXME: Single line version, please.
|
|
final String s = "Socks5Message:" + "\n" + "VN " + version + "\n"
|
|
+ "CMD " + command + "\n" + "ATYP " + addrType + "\n"
|
|
+ "ADDR " + host + "\n" + "PORT " + port + "\n";
|
|
return s;
|
|
}
|
|
|
|
/**
|
|
*Wether to resolve hostIP returned from SOCKS server that is wether to
|
|
* create InetAddress object from the hostName string
|
|
*/
|
|
static public boolean resolveIP() {
|
|
return doResolveIP;
|
|
}
|
|
|
|
/**
|
|
*Wether to resolve hostIP returned from SOCKS server that is wether to
|
|
* create InetAddress object from the hostName string
|
|
*
|
|
* @param doResolve
|
|
* Wether to resolve hostIP from SOCKS server.
|
|
*@return Previous value.
|
|
*/
|
|
static public boolean resolveIP(boolean doResolve) {
|
|
final boolean old = doResolveIP;
|
|
doResolveIP = doResolve;
|
|
return old;
|
|
}
|
|
|
|
/*
|
|
* private static final void debug(String s){ if(DEBUG) System.out.print(s);
|
|
* } private static final boolean DEBUG = false;
|
|
*/
|
|
|
|
// SOCKS5 constants
|
|
public static final int SOCKS_VERSION = 5;
|
|
|
|
public static final int SOCKS_ATYP_IPV4 = 0x1; // Where is 2??
|
|
public static final int SOCKS_ATYP_DOMAINNAME = 0x3; // !!!!rfc1928
|
|
public static final int SOCKS_ATYP_IPV6 = 0x4;
|
|
|
|
public static final int SOCKS_IPV6_LENGTH = 16;
|
|
|
|
static boolean doResolveIP = true;
|
|
|
|
}
|