first commit of new OrbotVPN integration into Orbot
This commit is contained in:
parent
dbbd8292c2
commit
e45991899a
|
@ -29,3 +29,6 @@
|
||||||
[submodule "external/polipo"]
|
[submodule "external/polipo"]
|
||||||
path = external/polipo
|
path = external/polipo
|
||||||
url = https://github.com/jech/polipo.git
|
url = https://github.com/jech/polipo.git
|
||||||
|
[submodule "external/badvpn"]
|
||||||
|
path = external/badvpn
|
||||||
|
url = https://github.com/ambrop72/badvpn.git
|
||||||
|
|
|
@ -102,6 +102,14 @@
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
|
|
||||||
|
<service android:name="org.torproject.android.vpn.OrbotVpnService"
|
||||||
|
android:permission="android.permission.BIND_VPN_SERVICE">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.net.VpnService"/>
|
||||||
|
</intent-filter>
|
||||||
|
</service>
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 5153894f4375d7a0e43d0b60c1b759543e6b383a
|
|
@ -1 +1 @@
|
||||||
Subproject commit 40233cadbbbf77214913db818a1458c6ddd14a9f
|
Subproject commit a64f3ab3ee5c433cc1f046a7e26df7a49e308e4c
|
|
@ -73,6 +73,9 @@
|
||||||
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<item android:id="@+id/menu_vpn"
|
||||||
|
android:title="start VPN"
|
||||||
|
yourapp:showAsAction="never"/>
|
||||||
<!--
|
<!--
|
||||||
<item android:id="@+id/menu_diag"
|
<item android:id="@+id/menu_diag"
|
||||||
android:title="Test Mode"
|
android:title="Test Mode"
|
||||||
|
|
|
@ -0,0 +1,269 @@
|
||||||
|
package com.runjva.sourceforge.jsocks.main;
|
||||||
|
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.runjva.sourceforge.jsocks.protocol.InetRange;
|
||||||
|
import com.runjva.sourceforge.jsocks.protocol.ProxyServer;
|
||||||
|
import com.runjva.sourceforge.jsocks.protocol.SocksProxyBase;
|
||||||
|
import com.runjva.sourceforge.jsocks.server.IdentAuthenticator;
|
||||||
|
|
||||||
|
public class SOCKS {
|
||||||
|
|
||||||
|
private static final int DEFAULT_LISTENING_PORT = 1080;
|
||||||
|
final private static Logger log = LoggerFactory.getLogger(SOCKS.class);
|
||||||
|
|
||||||
|
static public void usage() {
|
||||||
|
System.out.println("Usage: java SOCKS [inifile1 inifile2 ...]\n"
|
||||||
|
+ "If none inifile is given, uses socks.properties.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static public void main(String[] args) {
|
||||||
|
|
||||||
|
String[] file_names;
|
||||||
|
int port = DEFAULT_LISTENING_PORT;
|
||||||
|
String logFile = null;
|
||||||
|
String host = null;
|
||||||
|
|
||||||
|
final IdentAuthenticator auth = new IdentAuthenticator();
|
||||||
|
|
||||||
|
InetAddress localIP = null;
|
||||||
|
|
||||||
|
if (args.length == 0) {
|
||||||
|
file_names = new String[] { "socks.properties" };
|
||||||
|
} else {
|
||||||
|
file_names = args;
|
||||||
|
}
|
||||||
|
|
||||||
|
inform("Loading properties");
|
||||||
|
for (int i = 0; i < file_names.length; ++i) {
|
||||||
|
|
||||||
|
inform("Reading file " + file_names[i]);
|
||||||
|
|
||||||
|
final Properties pr = loadProperties(file_names[i]);
|
||||||
|
if (pr == null) {
|
||||||
|
System.err.println("Loading of properties from "
|
||||||
|
+ file_names[i] + "failed.");
|
||||||
|
usage();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!addAuth(auth, pr)) {
|
||||||
|
System.err.println("Error in file " + file_names[i] + ".");
|
||||||
|
usage();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// First file should contain all global settings,
|
||||||
|
// like port and host and log.
|
||||||
|
if (i == 0) {
|
||||||
|
final String port_s = (String) pr.get("port");
|
||||||
|
if (port_s != null) {
|
||||||
|
try {
|
||||||
|
port = Integer.parseInt(port_s);
|
||||||
|
} catch (final NumberFormatException nfe) {
|
||||||
|
System.err.println("Can't parse port: " + port_s);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
serverInit(pr);
|
||||||
|
logFile = (String) pr.get("log");
|
||||||
|
host = (String) pr.get("host");
|
||||||
|
}
|
||||||
|
|
||||||
|
// inform("Props:"+pr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logFile != null) {
|
||||||
|
System.err.println("log property not supported anymore.");
|
||||||
|
}
|
||||||
|
if (host != null) {
|
||||||
|
try {
|
||||||
|
localIP = InetAddress.getByName(host);
|
||||||
|
} catch (final UnknownHostException uhe) {
|
||||||
|
System.err.println("Can't resolve local ip: " + host);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inform("Using Ident Authentication scheme: " + auth);
|
||||||
|
final ProxyServer server = new ProxyServer(auth);
|
||||||
|
server.start(port, 5, localIP);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Properties loadProperties(String file_name) {
|
||||||
|
|
||||||
|
final Properties pr = new Properties();
|
||||||
|
|
||||||
|
try {
|
||||||
|
final InputStream fin = new FileInputStream(file_name);
|
||||||
|
pr.load(fin);
|
||||||
|
fin.close();
|
||||||
|
} catch (final IOException ioe) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return pr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean addAuth(IdentAuthenticator ident, Properties pr) {
|
||||||
|
|
||||||
|
InetRange irange;
|
||||||
|
|
||||||
|
final String range = (String) pr.get("range");
|
||||||
|
if (range == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
irange = parseInetRange(range);
|
||||||
|
|
||||||
|
final String users = (String) pr.get("users");
|
||||||
|
|
||||||
|
if (users == null) {
|
||||||
|
ident.add(irange, null);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Hashtable<String, String> uhash = new Hashtable<String, String>();
|
||||||
|
|
||||||
|
final StringTokenizer st = new StringTokenizer(users, ";");
|
||||||
|
while (st.hasMoreTokens()) {
|
||||||
|
uhash.put(st.nextToken(), "");
|
||||||
|
}
|
||||||
|
|
||||||
|
ident.add(irange, uhash);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does server initialisation.
|
||||||
|
*/
|
||||||
|
static void serverInit(Properties props) {
|
||||||
|
int val;
|
||||||
|
val = readInt(props, "iddleTimeout");
|
||||||
|
if (val >= 0) {
|
||||||
|
ProxyServer.setIddleTimeout(val);
|
||||||
|
inform("Setting iddle timeout to " + val + " ms.");
|
||||||
|
}
|
||||||
|
val = readInt(props, "acceptTimeout");
|
||||||
|
if (val >= 0) {
|
||||||
|
ProxyServer.setAcceptTimeout(val);
|
||||||
|
inform("Setting accept timeout to " + val + " ms.");
|
||||||
|
}
|
||||||
|
val = readInt(props, "udpTimeout");
|
||||||
|
if (val >= 0) {
|
||||||
|
ProxyServer.setUDPTimeout(val);
|
||||||
|
inform("Setting udp timeout to " + val + " ms.");
|
||||||
|
}
|
||||||
|
|
||||||
|
val = readInt(props, "datagramSize");
|
||||||
|
if (val >= 0) {
|
||||||
|
ProxyServer.setDatagramSize(val);
|
||||||
|
inform("Setting datagram size to " + val + " bytes.");
|
||||||
|
}
|
||||||
|
|
||||||
|
proxyInit(props);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialises proxy, if any specified.
|
||||||
|
*/
|
||||||
|
static void proxyInit(Properties props) {
|
||||||
|
String proxy_list;
|
||||||
|
SocksProxyBase proxy = null;
|
||||||
|
StringTokenizer st;
|
||||||
|
|
||||||
|
proxy_list = (String) props.get("proxy");
|
||||||
|
if (proxy_list == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
st = new StringTokenizer(proxy_list, ";");
|
||||||
|
while (st.hasMoreTokens()) {
|
||||||
|
final String proxy_entry = st.nextToken();
|
||||||
|
|
||||||
|
final SocksProxyBase p = SocksProxyBase.parseProxy(proxy_entry);
|
||||||
|
|
||||||
|
if (p == null) {
|
||||||
|
exit("Can't parse proxy entry:" + proxy_entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
inform("Adding Proxy:" + p);
|
||||||
|
|
||||||
|
if (proxy != null) {
|
||||||
|
p.setChainProxy(proxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
proxy = p;
|
||||||
|
|
||||||
|
}
|
||||||
|
if (proxy == null) {
|
||||||
|
return; // Empty list
|
||||||
|
}
|
||||||
|
|
||||||
|
final String direct_hosts = (String) props.get("directHosts");
|
||||||
|
if (direct_hosts != null) {
|
||||||
|
final InetRange ir = parseInetRange(direct_hosts);
|
||||||
|
inform("Setting direct hosts:" + ir);
|
||||||
|
proxy.setDirect(ir);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProxyServer.setProxy(proxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inits range from the string of semicolon separated ranges.
|
||||||
|
*/
|
||||||
|
static InetRange parseInetRange(String source) {
|
||||||
|
final InetRange irange = new InetRange();
|
||||||
|
|
||||||
|
final StringTokenizer st = new StringTokenizer(source, ";");
|
||||||
|
while (st.hasMoreTokens()) {
|
||||||
|
irange.add(st.nextToken());
|
||||||
|
}
|
||||||
|
|
||||||
|
return irange;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integer representaion of the property named name, or -1 if one is not
|
||||||
|
* found.
|
||||||
|
*/
|
||||||
|
static int readInt(Properties props, String name) {
|
||||||
|
int result = -1;
|
||||||
|
final String val = (String) props.get(name);
|
||||||
|
if (val == null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
final StringTokenizer st = new StringTokenizer(val);
|
||||||
|
if (!st.hasMoreElements()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
result = Integer.parseInt(st.nextToken());
|
||||||
|
} catch (final NumberFormatException nfe) {
|
||||||
|
inform("Bad value for " + name + ":" + val);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display functions
|
||||||
|
// /////////////////
|
||||||
|
|
||||||
|
static void inform(String s) {
|
||||||
|
log.info(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void exit(String msg) {
|
||||||
|
System.err.println("Error:" + msg);
|
||||||
|
System.err.println("Aborting operation");
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 926 B |
|
@ -0,0 +1,35 @@
|
||||||
|
package com.runjva.sourceforge.jsocks.protocol;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Authentication interface provides for performing method specific
|
||||||
|
* authentication for SOCKS5 connections.
|
||||||
|
*/
|
||||||
|
public interface Authentication {
|
||||||
|
/**
|
||||||
|
* This method is called when SOCKS5 server have selected a particular
|
||||||
|
* authentication method, for whch an implementaion have been registered.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This method should return an array {inputstream,outputstream
|
||||||
|
* [,UDPEncapsulation]}. The reason for that is that SOCKS5 protocol allows
|
||||||
|
* to have method specific encapsulation of data on the socket for purposes
|
||||||
|
* of integrity or security. And this encapsulation should be performed by
|
||||||
|
* those streams returned from the method. It is also possible to
|
||||||
|
* encapsulate datagrams. If authentication method supports such
|
||||||
|
* encapsulation an instance of the UDPEncapsulation interface should be
|
||||||
|
* returned as third element of the array, otherwise either null should be
|
||||||
|
* returned as third element, or array should contain only 2 elements.
|
||||||
|
*
|
||||||
|
* @param methodId
|
||||||
|
* Authentication method selected by the server.
|
||||||
|
* @param proxySocket
|
||||||
|
* Socket used to conect to the proxy.
|
||||||
|
* @return Two or three element array containing Input/Output streams which
|
||||||
|
* should be used on this connection. Third argument is optional and
|
||||||
|
* should contain an instance of UDPEncapsulation. It should be
|
||||||
|
* provided if the authentication method used requires any
|
||||||
|
* encapsulation to be done on the datagrams.
|
||||||
|
*/
|
||||||
|
Object[] doSocksAuthentication(int methodId, java.net.Socket proxySocket)
|
||||||
|
throws java.io.IOException;
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.runjva.sourceforge.jsocks.protocol;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SOCKS5 none authentication. Dummy class does almost nothing.
|
||||||
|
*/
|
||||||
|
public class AuthenticationNone implements Authentication {
|
||||||
|
|
||||||
|
public Object[] doSocksAuthentication(final int methodId,
|
||||||
|
final java.net.Socket proxySocket) throws java.io.IOException {
|
||||||
|
|
||||||
|
if (methodId != 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
InputStream in = proxySocket.getInputStream();
|
||||||
|
OutputStream out = proxySocket.getOutputStream();
|
||||||
|
return new Object[] { in, out };
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,492 @@
|
||||||
|
package com.runjva.sourceforge.jsocks.protocol;
|
||||||
|
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class InetRange provides the means of defining the range of inetaddresses.
|
||||||
|
* It's used by Proxy class to store and look up addresses of machines, that
|
||||||
|
* should be contacted directly rather then through the proxy.
|
||||||
|
* <P>
|
||||||
|
* InetRange provides several methods to add either standalone addresses, or
|
||||||
|
* ranges (e.g. 100.200.300.0:100.200.300.255, which covers all addresses on on
|
||||||
|
* someones local network). It also provides methods for checking wether given
|
||||||
|
* address is in this range. Any number of ranges and standalone addresses can
|
||||||
|
* be added to the range.
|
||||||
|
*/
|
||||||
|
public class InetRange implements Cloneable {
|
||||||
|
|
||||||
|
Hashtable<String, Object[]> host_names;
|
||||||
|
Vector<Object[]> all;
|
||||||
|
Vector<String> end_names;
|
||||||
|
|
||||||
|
boolean useSeparateThread = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the empty range.
|
||||||
|
*/
|
||||||
|
public InetRange() {
|
||||||
|
all = new Vector<Object[]>();
|
||||||
|
host_names = new Hashtable<String, Object[]>();
|
||||||
|
end_names = new Vector<String>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds another host or range to this range. The String can be one of those:
|
||||||
|
* <UL>
|
||||||
|
* <li>Host name. eg.(Athena.myhost.com or 45.54.56.65)
|
||||||
|
*
|
||||||
|
* <li>Range in the form .myhost.net.au <BR>
|
||||||
|
* In which case anything that ends with .myhost.net.au will be considered
|
||||||
|
* in the range.
|
||||||
|
*
|
||||||
|
* <li>Range in the form ddd.ddd.ddd. <BR>
|
||||||
|
* This will be treated as range ddd.ddd.ddd.0 to ddd.ddd.ddd.255. It is not
|
||||||
|
* necessary to specify 3 first bytes you can use just one or two. For
|
||||||
|
* example 130. will cover address between 130.0.0.0 and 13.255.255.255.
|
||||||
|
*
|
||||||
|
* <li>Range in the form host_from[: \t\n\r\f]host_to. <br>
|
||||||
|
* That is two hostnames or ips separated by either whitespace or colon.
|
||||||
|
* </UL>
|
||||||
|
*/
|
||||||
|
public synchronized boolean add(final String s0) {
|
||||||
|
if (s0 == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String s = s0.trim();
|
||||||
|
if (s.length() == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object[] entry;
|
||||||
|
|
||||||
|
if (s.endsWith(".")) {
|
||||||
|
// thing like: 111.222.33.
|
||||||
|
// it is being treated as range 111.222.33.000 - 111.222.33.255
|
||||||
|
|
||||||
|
final int[] addr = ip2intarray(s);
|
||||||
|
long from, to;
|
||||||
|
from = to = 0;
|
||||||
|
|
||||||
|
if (addr == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
if (addr[i] >= 0) {
|
||||||
|
from += (((long) addr[i]) << 8 * (3 - i));
|
||||||
|
} else {
|
||||||
|
to = from;
|
||||||
|
while (i < 4) {
|
||||||
|
to += 255l << 8 * (3 - i++);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entry = new Object[] { s, null, new Long(from), new Long(to) };
|
||||||
|
all.addElement(entry);
|
||||||
|
|
||||||
|
} else if (s.startsWith(".")) {
|
||||||
|
// Thing like: .myhost.com
|
||||||
|
|
||||||
|
end_names.addElement(s);
|
||||||
|
all.addElement(new Object[] { s, null, null, null });
|
||||||
|
} else {
|
||||||
|
final StringTokenizer tokens = new StringTokenizer(s, " \t\r\n\f:");
|
||||||
|
if (tokens.countTokens() > 1) {
|
||||||
|
entry = new Object[] { s, null, null, null };
|
||||||
|
resolve(entry, tokens.nextToken(), tokens.nextToken());
|
||||||
|
all.addElement(entry);
|
||||||
|
} else {
|
||||||
|
entry = new Object[] { s, null, null, null };
|
||||||
|
all.addElement(entry);
|
||||||
|
host_names.put(s, entry);
|
||||||
|
resolve(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds another ip for this range.
|
||||||
|
*
|
||||||
|
* @param ip
|
||||||
|
* IP os the host which should be added to this range.
|
||||||
|
*/
|
||||||
|
public synchronized void add(final InetAddress ip) {
|
||||||
|
long from, to;
|
||||||
|
from = to = ip2long(ip);
|
||||||
|
all.addElement(new Object[] { ip.getHostName(), ip, new Long(from),
|
||||||
|
new Long(to) });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds another range of ips for this range.Any host with ip address greater
|
||||||
|
* than or equal to the address of from and smaller than or equal to the
|
||||||
|
* address of to will be included in the range.
|
||||||
|
*
|
||||||
|
* @param from
|
||||||
|
* IP from where range starts(including).
|
||||||
|
* @param to
|
||||||
|
* IP where range ends(including).
|
||||||
|
*/
|
||||||
|
public synchronized void add(final InetAddress from, final InetAddress to) {
|
||||||
|
all.addElement(new Object[] {
|
||||||
|
from.getHostAddress() + ":" + to.getHostAddress(), null,
|
||||||
|
new Long(ip2long(from)), new Long(ip2long(to)) });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks wether the givan host is in the range. Attempts to resolve host
|
||||||
|
* name if required.
|
||||||
|
*
|
||||||
|
* @param host
|
||||||
|
* Host name to check.
|
||||||
|
* @return true If host is in the range, false otherwise.
|
||||||
|
* @see InetRange#contains(String,boolean)
|
||||||
|
*/
|
||||||
|
public synchronized boolean contains(final String host) {
|
||||||
|
return contains(host, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks wether the given host is in the range.
|
||||||
|
* <P>
|
||||||
|
* Algorithm: <BR>
|
||||||
|
* <ol>
|
||||||
|
* <li>Look up if the hostname is in the range (in the Hashtable).
|
||||||
|
* <li>Check if it ends with one of the speciefied endings.
|
||||||
|
* <li>Check if it is ip(eg.130.220.35.98). If it is check if it is in the
|
||||||
|
* range.
|
||||||
|
* <li>If attemptResolve is true, host is name, rather than ip, and all
|
||||||
|
* previous attempts failed, try to resolve the hostname, and check wether
|
||||||
|
* the ip associated with the host is in the range.It also repeats all
|
||||||
|
* previos steps with the hostname obtained from InetAddress, but the name
|
||||||
|
* is not allways the full name,it is quite likely to be the same. Well it
|
||||||
|
* was on my machine.
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* @param host
|
||||||
|
* Host name to check.
|
||||||
|
* @param attemptResolve
|
||||||
|
* Wether to lookup ip address which corresponds to the host,if
|
||||||
|
* required.
|
||||||
|
* @return true If host is in the range, false otherwise.
|
||||||
|
*/
|
||||||
|
public synchronized boolean contains(final String host0,
|
||||||
|
final boolean attemptResolve) {
|
||||||
|
if (all.size() == 0) {
|
||||||
|
return false; // Empty range
|
||||||
|
}
|
||||||
|
|
||||||
|
String host = host0.trim();
|
||||||
|
if (host.length() == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkHost(host)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (checkHostEnding(host)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
final long l = host2long(host);
|
||||||
|
if (l >= 0) {
|
||||||
|
return contains(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!attemptResolve) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final InetAddress ip = InetAddress.getByName(host);
|
||||||
|
return contains(ip);
|
||||||
|
} catch (final UnknownHostException uhe) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks wether the given ip is in the range.
|
||||||
|
*
|
||||||
|
* @param ip
|
||||||
|
* Address of the host to check.
|
||||||
|
* @return true If host is in the range, false otherwise.
|
||||||
|
*/
|
||||||
|
public synchronized boolean contains(final InetAddress ip) {
|
||||||
|
if (checkHostEnding(ip.getHostName())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (checkHost(ip.getHostName())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return contains(ip2long(ip));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all entries in the range as strings. <BR>
|
||||||
|
* These strings can be used to delete entries from the range with remove
|
||||||
|
* function.
|
||||||
|
*
|
||||||
|
* @return Array of entries as strings.
|
||||||
|
* @see InetRange#remove(String)
|
||||||
|
*/
|
||||||
|
public synchronized String[] getAll() {
|
||||||
|
final int size = all.size();
|
||||||
|
Object entry[];
|
||||||
|
final String all_names[] = new String[size];
|
||||||
|
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
entry = all.elementAt(i);
|
||||||
|
all_names[i] = (String) entry[0];
|
||||||
|
}
|
||||||
|
return all_names;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an entry from this range.<BR>
|
||||||
|
*
|
||||||
|
* @param s
|
||||||
|
* Entry to remove.
|
||||||
|
* @return true if successfull.
|
||||||
|
*/
|
||||||
|
public synchronized boolean remove(final String s) {
|
||||||
|
final Enumeration<Object[]> enumx = all.elements();
|
||||||
|
while (enumx.hasMoreElements()) {
|
||||||
|
final Object[] entry = enumx.nextElement();
|
||||||
|
if (s.equals(entry[0])) {
|
||||||
|
all.removeElement(entry);
|
||||||
|
end_names.removeElement(s);
|
||||||
|
host_names.remove(s);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get string representaion of this Range. */
|
||||||
|
public String toString() {
|
||||||
|
final String all[] = getAll();
|
||||||
|
if (all.length == 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
String s = all[0];
|
||||||
|
for (int i = 1; i < all.length; ++i) {
|
||||||
|
s += "; " + all[i];
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates a clone of this Object */
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public Object clone() {
|
||||||
|
final InetRange new_range = new InetRange();
|
||||||
|
new_range.all = (Vector<Object[]>) all.clone();
|
||||||
|
new_range.end_names = (Vector<String>) end_names.clone();
|
||||||
|
new_range.host_names = (Hashtable<String, Object[]>) host_names.clone();
|
||||||
|
return new_range;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private methods
|
||||||
|
// ///////////////
|
||||||
|
/**
|
||||||
|
* Same as previous but used internally, to avoid unnecessary convertion of
|
||||||
|
* IPs, when checking subranges
|
||||||
|
*/
|
||||||
|
private synchronized boolean contains(final long ip) {
|
||||||
|
final Enumeration<Object[]> enumx = all.elements();
|
||||||
|
while (enumx.hasMoreElements()) {
|
||||||
|
final Object[] obj = enumx.nextElement();
|
||||||
|
final Long from = obj[2] == null ? null : (Long) obj[2];
|
||||||
|
final Long to = obj[3] == null ? null : (Long) obj[3];
|
||||||
|
if ((from != null) && (from.longValue() <= ip)
|
||||||
|
&& (to.longValue() >= ip)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkHost(final String host) {
|
||||||
|
return host_names.containsKey(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkHostEnding(final String host) {
|
||||||
|
final Enumeration<String> enumx = end_names.elements();
|
||||||
|
while (enumx.hasMoreElements()) {
|
||||||
|
if (host.endsWith(enumx.nextElement())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resolve(final Object[] entry) {
|
||||||
|
// First check if it's in the form ddd.ddd.ddd.ddd.
|
||||||
|
final long ip = host2long((String) entry[0]);
|
||||||
|
if (ip >= 0) {
|
||||||
|
entry[2] = entry[3] = new Long(ip);
|
||||||
|
} else {
|
||||||
|
final InetRangeResolver res = new InetRangeResolver(entry);
|
||||||
|
res.resolve(useSeparateThread);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resolve(final Object[] entry, final String from,
|
||||||
|
final String to) {
|
||||||
|
long f, t;
|
||||||
|
if (((f = host2long(from)) >= 0) && ((t = host2long(to)) >= 0)) {
|
||||||
|
entry[2] = new Long(f);
|
||||||
|
entry[3] = new Long(t);
|
||||||
|
} else {
|
||||||
|
final InetRangeResolver res = new InetRangeResolver(entry, from, to);
|
||||||
|
res.resolve(useSeparateThread);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Class methods
|
||||||
|
// /////////////
|
||||||
|
|
||||||
|
// Converts ipv4 to long value(unsigned int)
|
||||||
|
// /////////////////////////////////////////
|
||||||
|
static long ip2long(final InetAddress ip) {
|
||||||
|
long l = 0;
|
||||||
|
final byte[] addr = ip.getAddress();
|
||||||
|
|
||||||
|
if (addr.length == 4) { // IPV4
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
l += (((long) addr[i] & 0xFF) << 8 * (3 - i));
|
||||||
|
}
|
||||||
|
} else { // IPV6
|
||||||
|
return 0; // Have no idea how to deal with those
|
||||||
|
}
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
long host2long(final String host) {
|
||||||
|
long ip = 0;
|
||||||
|
|
||||||
|
// check if it's ddd.ddd.ddd.ddd
|
||||||
|
if (!Character.isDigit(host.charAt(0))) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int[] addr = ip2intarray(host);
|
||||||
|
if (addr == null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < addr.length; ++i) {
|
||||||
|
ip += ((long) (addr[i] >= 0 ? addr[i] : 0)) << 8 * (3 - i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int[] ip2intarray(final String host) {
|
||||||
|
final int[] address = { -1, -1, -1, -1 };
|
||||||
|
int i = 0;
|
||||||
|
final StringTokenizer tokens = new StringTokenizer(host, ".");
|
||||||
|
if (tokens.countTokens() > 4) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
while (tokens.hasMoreTokens()) {
|
||||||
|
try {
|
||||||
|
address[i++] = Integer.parseInt(tokens.nextToken()) & 0xFF;
|
||||||
|
} catch (final NumberFormatException nfe) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* //* This was the test main function //**********************************
|
||||||
|
*
|
||||||
|
* public static void main(String args[])throws UnknownHostException{ int i;
|
||||||
|
*
|
||||||
|
* InetRange ir = new InetRange();
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* for(i=0;i<args.length;++i){ System.out.println("Adding:" + args[i]);
|
||||||
|
* ir.add(args[i]); }
|
||||||
|
*
|
||||||
|
* String host; java.io.DataInputStream din = new
|
||||||
|
* java.io.DataInputStream(System.in); try{ host = din.readLine();
|
||||||
|
* while(host!=null){ if(ir.contains(host)){
|
||||||
|
* System.out.println("Range contains ip:"+host); }else{
|
||||||
|
* System.out.println(host+" is not in the range"); } host = din.readLine();
|
||||||
|
* } }catch(java.io.IOException io_ex){ io_ex.printStackTrace(); } }
|
||||||
|
* ******************
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class InetRangeResolver implements Runnable {
|
||||||
|
|
||||||
|
Object[] entry;
|
||||||
|
|
||||||
|
String from;
|
||||||
|
String to;
|
||||||
|
|
||||||
|
InetRangeResolver(final Object[] entry) {
|
||||||
|
this.entry = entry;
|
||||||
|
from = null;
|
||||||
|
to = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
InetRangeResolver(final Object[] entry, final String from, final String to) {
|
||||||
|
this.entry = entry;
|
||||||
|
this.from = from;
|
||||||
|
this.to = to;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void resolve() {
|
||||||
|
resolve(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void resolve(final boolean inSeparateThread) {
|
||||||
|
if (inSeparateThread) {
|
||||||
|
final Thread t = new Thread(this);
|
||||||
|
t.start();
|
||||||
|
} else {
|
||||||
|
run();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
if (from == null) {
|
||||||
|
final InetAddress ip = InetAddress.getByName((String) entry[0]);
|
||||||
|
entry[1] = ip;
|
||||||
|
final Long l = new Long(InetRange.ip2long(ip));
|
||||||
|
entry[2] = l;
|
||||||
|
entry[3] = l;
|
||||||
|
} else {
|
||||||
|
final InetAddress f = InetAddress.getByName(from);
|
||||||
|
final InetAddress t = InetAddress.getByName(to);
|
||||||
|
entry[2] = new Long(InetRange.ip2long(f));
|
||||||
|
entry[3] = new Long(InetRange.ip2long(t));
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (final UnknownHostException uhe) {
|
||||||
|
// System.err.println("Resolve failed for "+from+','+to+','+entry[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,118 @@
|
||||||
|
package com.runjva.sourceforge.jsocks.protocol;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract class which describes SOCKS4/5 response/request.
|
||||||
|
*/
|
||||||
|
public abstract class ProxyMessage {
|
||||||
|
|
||||||
|
/** Host as an IP address */
|
||||||
|
public InetAddress ip = null;
|
||||||
|
|
||||||
|
/** SOCKS version, or version of the response for SOCKS4 */
|
||||||
|
public int version;
|
||||||
|
|
||||||
|
/** Port field of the request/response */
|
||||||
|
public int port;
|
||||||
|
|
||||||
|
/** Request/response code as an int */
|
||||||
|
public int command;
|
||||||
|
|
||||||
|
/** Host as string. */
|
||||||
|
public String host = null;
|
||||||
|
|
||||||
|
/** User field for SOCKS4 request messages */
|
||||||
|
public String user = null;
|
||||||
|
|
||||||
|
ProxyMessage(int command, InetAddress ip, int port) {
|
||||||
|
this.command = command;
|
||||||
|
this.ip = ip;
|
||||||
|
this.port = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProxyMessage() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 abstract void read(InputStream in) throws SocksException,
|
||||||
|
IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 abstract void read(InputStream in, boolean client_mode)
|
||||||
|
throws SocksException, IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the message to the stream.
|
||||||
|
*
|
||||||
|
* @param out
|
||||||
|
* Output stream to which message should be written.
|
||||||
|
*/
|
||||||
|
public abstract void write(OutputStream out) throws SocksException,
|
||||||
|
IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Address field of this message as InetAddress object.
|
||||||
|
*
|
||||||
|
* @return Host address or null, if one can't be determined.
|
||||||
|
*/
|
||||||
|
public InetAddress getInetAddress() throws UnknownHostException {
|
||||||
|
return ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get string representaion of this message.
|
||||||
|
*
|
||||||
|
* @return string representation of this message.
|
||||||
|
*/
|
||||||
|
public String toString() {
|
||||||
|
return "Proxy Message:\n" + "Version:" + version + "\n" + "Command:"
|
||||||
|
+ command + "\n" + "IP: " + ip + "\n" + "Port: " + port
|
||||||
|
+ "\n" + "User: " + user + "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Package methods
|
||||||
|
// ////////////////
|
||||||
|
|
||||||
|
static final String bytes2IPV4(byte[] addr, int offset) {
|
||||||
|
String hostName = "" + (addr[offset] & 0xFF);
|
||||||
|
for (int i = offset + 1; i < offset + 4; i++) {
|
||||||
|
hostName += "." + (addr[i] & 0xFF);
|
||||||
|
}
|
||||||
|
return hostName;
|
||||||
|
}
|
||||||
|
|
||||||
|
static final String bytes2IPV6(byte[] addr, int offset) {
|
||||||
|
// Have no idea how they look like!
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,669 @@
|
||||||
|
package com.runjva.sourceforge.jsocks.protocol;
|
||||||
|
|
||||||
|
import java.io.EOFException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InterruptedIOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.PushbackInputStream;
|
||||||
|
import java.net.ConnectException;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.NoRouteToHostException;
|
||||||
|
import java.net.ServerSocket;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.net.SocketAddress;
|
||||||
|
import java.nio.channels.SocketChannel;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import android.net.VpnService;
|
||||||
|
|
||||||
|
import com.runjva.sourceforge.jsocks.server.ServerAuthenticator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SOCKS4 and SOCKS5 proxy, handles both protocols simultaniously. Implements
|
||||||
|
* all SOCKS commands, including UDP relaying.
|
||||||
|
* <p>
|
||||||
|
* In order to use it you will need to implement ServerAuthenticator interface.
|
||||||
|
* There is an implementation of this interface which does no authentication
|
||||||
|
* ServerAuthenticatorNone, but it is very dangerous to use, as it will give
|
||||||
|
* access to your local network to anybody in the world. One should never use
|
||||||
|
* this authentication scheme unless one have pretty good reason to do so. There
|
||||||
|
* is a couple of other authentication schemes in socks.server package.
|
||||||
|
*
|
||||||
|
* @see socks.server.ServerAuthenticator
|
||||||
|
*/
|
||||||
|
public class ProxyServer implements Runnable {
|
||||||
|
|
||||||
|
ServerAuthenticator auth;
|
||||||
|
ProxyMessage msg = null;
|
||||||
|
|
||||||
|
Socket sock = null, remote_sock = null;
|
||||||
|
ServerSocket ss = null;
|
||||||
|
UDPRelayServer relayServer = null;
|
||||||
|
InputStream in, remote_in;
|
||||||
|
OutputStream out, remote_out;
|
||||||
|
|
||||||
|
int mode;
|
||||||
|
static final int START_MODE = 0;
|
||||||
|
static final int ACCEPT_MODE = 1;
|
||||||
|
static final int PIPE_MODE = 2;
|
||||||
|
static final int ABORT_MODE = 3;
|
||||||
|
|
||||||
|
static final int BUF_SIZE = 8192;
|
||||||
|
|
||||||
|
Thread pipe_thread1, pipe_thread2;
|
||||||
|
long lastReadTime;
|
||||||
|
|
||||||
|
static int iddleTimeout = 180000; // 3 minutes
|
||||||
|
static int acceptTimeout = 180000; // 3 minutes
|
||||||
|
|
||||||
|
static Logger log = LoggerFactory.getLogger(ProxyServer.class);
|
||||||
|
static SocksProxyBase proxy;
|
||||||
|
|
||||||
|
static VpnService vpnService;
|
||||||
|
|
||||||
|
// Public Constructors
|
||||||
|
// ///////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a proxy server with given Authentication scheme.
|
||||||
|
*
|
||||||
|
* @param auth
|
||||||
|
* Authentication scheme to be used.
|
||||||
|
*/
|
||||||
|
public ProxyServer(final ServerAuthenticator auth) {
|
||||||
|
this.auth = auth;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Other constructors
|
||||||
|
// //////////////////
|
||||||
|
|
||||||
|
ProxyServer(final ServerAuthenticator auth, final Socket s) {
|
||||||
|
this.auth = auth;
|
||||||
|
this.sock = s;
|
||||||
|
this.mode = START_MODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public methods
|
||||||
|
// ///////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set proxy.
|
||||||
|
* <p>
|
||||||
|
* Allows Proxy chaining so that one Proxy server is connected to another
|
||||||
|
* and so on. If proxy supports SOCKSv4, then only some SOCKSv5 requests can
|
||||||
|
* be handled, UDP would not work, however CONNECT and BIND will be
|
||||||
|
* translated.
|
||||||
|
*
|
||||||
|
* @param p
|
||||||
|
* Proxy which should be used to handle user requests.
|
||||||
|
*/
|
||||||
|
public static void setProxy(final SocksProxyBase p) {
|
||||||
|
proxy = p;
|
||||||
|
// FIXME: Side effect.
|
||||||
|
UDPRelayServer.proxy = proxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setVpnService (final VpnService v)
|
||||||
|
{
|
||||||
|
vpnService = v;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get proxy.
|
||||||
|
*
|
||||||
|
* @return Proxy wich is used to handle user requests.
|
||||||
|
*/
|
||||||
|
public static SocksProxyBase getProxy() {
|
||||||
|
return proxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the timeout for connections, how long shoud server wait for data to
|
||||||
|
* arrive before dropping the connection.<br>
|
||||||
|
* Zero timeout implies infinity.<br>
|
||||||
|
* Default timeout is 3 minutes.
|
||||||
|
*/
|
||||||
|
public static void setIddleTimeout(final int timeout) {
|
||||||
|
iddleTimeout = timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the timeout for BIND command, how long the server should wait for
|
||||||
|
* the incoming connection.<br>
|
||||||
|
* Zero timeout implies infinity.<br>
|
||||||
|
* Default timeout is 3 minutes.
|
||||||
|
*/
|
||||||
|
public static void setAcceptTimeout(final int timeout) {
|
||||||
|
acceptTimeout = timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the timeout for UDPRelay server.<br>
|
||||||
|
* Zero timeout implies infinity.<br>
|
||||||
|
* Default timeout is 3 minutes.
|
||||||
|
*/
|
||||||
|
public static void setUDPTimeout(final int timeout) {
|
||||||
|
UDPRelayServer.setTimeout(timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the size of the datagrams used in the UDPRelayServer.<br>
|
||||||
|
* Default size is 64K, a bit more than maximum possible size of the
|
||||||
|
* datagram.
|
||||||
|
*/
|
||||||
|
public static void setDatagramSize(final int size) {
|
||||||
|
UDPRelayServer.setDatagramSize(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the Proxy server at given port.<br>
|
||||||
|
* This methods blocks.
|
||||||
|
*/
|
||||||
|
public void start(final int port) {
|
||||||
|
start(port, 5, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a server with the specified port, listen backlog, and local IP
|
||||||
|
* address to bind to. The localIP argument can be used on a multi-homed
|
||||||
|
* host for a ServerSocket that will only accept connect requests to one of
|
||||||
|
* its addresses. If localIP is null, it will default accepting connections
|
||||||
|
* on any/all local addresses. The port must be between 0 and 65535,
|
||||||
|
* inclusive. <br>
|
||||||
|
* This methods blocks.
|
||||||
|
*/
|
||||||
|
public void start(final int port, final int backlog,
|
||||||
|
final InetAddress localIP) {
|
||||||
|
try {
|
||||||
|
ss = new ServerSocket(port, backlog, localIP);
|
||||||
|
final String address = ss.getInetAddress().getHostAddress();
|
||||||
|
final int localPort = ss.getLocalPort();
|
||||||
|
log.info("Starting SOCKS Proxy on: {}:{}", address, localPort);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
final Socket s = ss.accept();
|
||||||
|
final String hostName = s.getInetAddress().getHostName();
|
||||||
|
final int port2 = s.getPort();
|
||||||
|
log.info("Accepted from:{}:{}", hostName, port2);
|
||||||
|
|
||||||
|
final ProxyServer ps = new ProxyServer(auth, s);
|
||||||
|
(new Thread(ps)).start();
|
||||||
|
}
|
||||||
|
} catch (final IOException ioe) {
|
||||||
|
ioe.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop server operation.It would be wise to interrupt thread running the
|
||||||
|
* server afterwards.
|
||||||
|
*/
|
||||||
|
public void stop() {
|
||||||
|
try {
|
||||||
|
if (ss != null) {
|
||||||
|
ss.close();
|
||||||
|
}
|
||||||
|
} catch (final IOException ioe) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Runnable interface
|
||||||
|
// //////////////////
|
||||||
|
public void run() {
|
||||||
|
switch (mode) {
|
||||||
|
case START_MODE:
|
||||||
|
try {
|
||||||
|
startSession();
|
||||||
|
} catch (final IOException ioe) {
|
||||||
|
handleException(ioe);
|
||||||
|
// ioe.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
abort();
|
||||||
|
if (auth != null) {
|
||||||
|
auth.endSession();
|
||||||
|
}
|
||||||
|
log.info("Main thread(client->remote)stopped.");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ACCEPT_MODE:
|
||||||
|
try {
|
||||||
|
doAccept();
|
||||||
|
mode = PIPE_MODE;
|
||||||
|
pipe_thread1.interrupt(); // Tell other thread that connection
|
||||||
|
// have
|
||||||
|
// been accepted.
|
||||||
|
pipe(remote_in, out);
|
||||||
|
} catch (final IOException ioe) {
|
||||||
|
// log("Accept exception:"+ioe);
|
||||||
|
handleException(ioe);
|
||||||
|
} finally {
|
||||||
|
abort();
|
||||||
|
log.info("Accept thread(remote->client) stopped");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PIPE_MODE:
|
||||||
|
try {
|
||||||
|
pipe(remote_in, out);
|
||||||
|
} catch (final IOException ioe) {
|
||||||
|
} finally {
|
||||||
|
abort();
|
||||||
|
log.info("Support thread(remote->client) stopped");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ABORT_MODE:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
log.warn("Unexpected MODE " + mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private methods
|
||||||
|
// ///////////////
|
||||||
|
private void startSession() throws IOException {
|
||||||
|
sock.setSoTimeout(iddleTimeout);
|
||||||
|
|
||||||
|
try {
|
||||||
|
auth = auth.startSession(sock);
|
||||||
|
} catch (final IOException ioe) {
|
||||||
|
log.warn("Auth throwed exception:", ioe);
|
||||||
|
auth = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auth == null) { // Authentication failed
|
||||||
|
log.info("Authentication failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
in = auth.getInputStream();
|
||||||
|
out = auth.getOutputStream();
|
||||||
|
|
||||||
|
msg = readMsg(in);
|
||||||
|
handleRequest(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleRequest(final ProxyMessage msg) throws IOException {
|
||||||
|
if (!auth.checkRequest(msg)) {
|
||||||
|
throw new SocksException(SocksProxyBase.SOCKS_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg.ip == null) {
|
||||||
|
if (msg instanceof Socks5Message) {
|
||||||
|
msg.ip = InetAddress.getByName(msg.host);
|
||||||
|
} else {
|
||||||
|
throw new SocksException(SocksProxyBase.SOCKS_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log(msg);
|
||||||
|
|
||||||
|
switch (msg.command) {
|
||||||
|
case SocksProxyBase.SOCKS_CMD_CONNECT:
|
||||||
|
onConnect(msg);
|
||||||
|
break;
|
||||||
|
case SocksProxyBase.SOCKS_CMD_BIND:
|
||||||
|
onBind(msg);
|
||||||
|
break;
|
||||||
|
case SocksProxyBase.SOCKS_CMD_UDP_ASSOCIATE:
|
||||||
|
onUDP(msg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new SocksException(SocksProxyBase.SOCKS_CMD_NOT_SUPPORTED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleException(final IOException ioe) {
|
||||||
|
// If we couldn't read the request, return;
|
||||||
|
if (msg == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// If have been aborted by other thread
|
||||||
|
if (mode == ABORT_MODE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// If the request was successfully completed, but exception happened
|
||||||
|
// later
|
||||||
|
if (mode == PIPE_MODE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int error_code = SocksProxyBase.SOCKS_FAILURE;
|
||||||
|
|
||||||
|
if (ioe instanceof SocksException) {
|
||||||
|
error_code = ((SocksException) ioe).errCode;
|
||||||
|
} else if (ioe instanceof NoRouteToHostException) {
|
||||||
|
error_code = SocksProxyBase.SOCKS_HOST_UNREACHABLE;
|
||||||
|
} else if (ioe instanceof ConnectException) {
|
||||||
|
error_code = SocksProxyBase.SOCKS_CONNECTION_REFUSED;
|
||||||
|
} else if (ioe instanceof InterruptedIOException) {
|
||||||
|
error_code = SocksProxyBase.SOCKS_TTL_EXPIRE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((error_code > SocksProxyBase.SOCKS_ADDR_NOT_SUPPORTED)
|
||||||
|
|| (error_code < 0)) {
|
||||||
|
error_code = SocksProxyBase.SOCKS_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
sendErrorMessage(error_code);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onConnect(final ProxyMessage msg) throws IOException {
|
||||||
|
Socket s;
|
||||||
|
|
||||||
|
if (proxy == null) {
|
||||||
|
//s = new Socket(msg.ip, msg.port);
|
||||||
|
|
||||||
|
s= SocketChannel.open().socket();
|
||||||
|
if ((null != s) && (null != vpnService)) {
|
||||||
|
vpnService.protect(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
s.connect(new InetSocketAddress(msg.ip,msg.port));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
s = new SocksSocket(proxy, msg.ip, msg.port);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vpnService != null)
|
||||||
|
vpnService.protect(s);
|
||||||
|
|
||||||
|
log.info("Connected to " + s.getInetAddress() + ":" + s.getPort());
|
||||||
|
|
||||||
|
ProxyMessage response = null;
|
||||||
|
final InetAddress localAddress = s.getLocalAddress();
|
||||||
|
final int localPort = s.getLocalPort();
|
||||||
|
|
||||||
|
if (msg instanceof Socks5Message) {
|
||||||
|
final int cmd = SocksProxyBase.SOCKS_SUCCESS;
|
||||||
|
response = new Socks5Message(cmd, localAddress, localPort);
|
||||||
|
} else {
|
||||||
|
final int cmd = Socks4Message.REPLY_OK;
|
||||||
|
response = new Socks4Message(cmd, localAddress, localPort);
|
||||||
|
|
||||||
|
}
|
||||||
|
response.write(out);
|
||||||
|
startPipe(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onBind(final ProxyMessage msg) throws IOException {
|
||||||
|
ProxyMessage response = null;
|
||||||
|
|
||||||
|
if (proxy == null) {
|
||||||
|
ss = new ServerSocket(0);
|
||||||
|
} else {
|
||||||
|
ss = new SocksServerSocket(proxy, msg.ip, msg.port);
|
||||||
|
}
|
||||||
|
|
||||||
|
ss.setSoTimeout(acceptTimeout);
|
||||||
|
|
||||||
|
final InetAddress inetAddress = ss.getInetAddress();
|
||||||
|
final int localPort = ss.getLocalPort();
|
||||||
|
log.info("Trying accept on {}:{}", inetAddress, localPort);
|
||||||
|
|
||||||
|
if (msg.version == 5) {
|
||||||
|
final int cmd = SocksProxyBase.SOCKS_SUCCESS;
|
||||||
|
response = new Socks5Message(cmd, inetAddress, localPort);
|
||||||
|
} else {
|
||||||
|
final int cmd = Socks4Message.REPLY_OK;
|
||||||
|
response = new Socks4Message(cmd, inetAddress, localPort);
|
||||||
|
}
|
||||||
|
response.write(out);
|
||||||
|
|
||||||
|
mode = ACCEPT_MODE;
|
||||||
|
|
||||||
|
pipe_thread1 = Thread.currentThread();
|
||||||
|
pipe_thread2 = new Thread(this);
|
||||||
|
pipe_thread2.start();
|
||||||
|
|
||||||
|
// Make timeout infinit.
|
||||||
|
sock.setSoTimeout(0);
|
||||||
|
int eof = 0;
|
||||||
|
|
||||||
|
try {
|
||||||
|
while ((eof = in.read()) >= 0) {
|
||||||
|
if (mode != ACCEPT_MODE) {
|
||||||
|
if (mode != PIPE_MODE) {
|
||||||
|
return;// Accept failed
|
||||||
|
}
|
||||||
|
|
||||||
|
remote_out.write(eof);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (final EOFException e) {
|
||||||
|
log.debug("Connection closed while we were trying to accept", e);
|
||||||
|
return;
|
||||||
|
} catch (final InterruptedIOException e) {
|
||||||
|
log.debug("Interrupted by unsucessful accept thread", e);
|
||||||
|
if (mode != PIPE_MODE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
// System.out.println("Finnaly!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eof < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not restore timeout, instead timeout is set on the
|
||||||
|
// remote socket. It does not make any difference.
|
||||||
|
|
||||||
|
pipe(in, remote_out);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onUDP(final ProxyMessage msg) throws IOException {
|
||||||
|
if (msg.ip.getHostAddress().equals("0.0.0.0")) {
|
||||||
|
msg.ip = sock.getInetAddress();
|
||||||
|
}
|
||||||
|
log.info("Creating UDP relay server for {}:{}", msg.ip, msg.port);
|
||||||
|
|
||||||
|
relayServer = new UDPRelayServer(msg.ip, msg.port,
|
||||||
|
Thread.currentThread(), sock, auth);
|
||||||
|
|
||||||
|
ProxyMessage response;
|
||||||
|
|
||||||
|
response = new Socks5Message(SocksProxyBase.SOCKS_SUCCESS,
|
||||||
|
relayServer.relayIP, relayServer.relayPort);
|
||||||
|
|
||||||
|
response.write(out);
|
||||||
|
|
||||||
|
relayServer.start();
|
||||||
|
|
||||||
|
// Make timeout infinit.
|
||||||
|
sock.setSoTimeout(0);
|
||||||
|
try {
|
||||||
|
while (in.read() >= 0) {
|
||||||
|
/* do nothing */;
|
||||||
|
// FIXME: Consider a slight delay here?
|
||||||
|
}
|
||||||
|
} catch (final EOFException eofe) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private methods
|
||||||
|
// ////////////////
|
||||||
|
|
||||||
|
private void doAccept() throws IOException {
|
||||||
|
Socket s = null;
|
||||||
|
final long startTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
s = ss.accept();
|
||||||
|
if (s.getInetAddress().equals(msg.ip)) {
|
||||||
|
// got the connection from the right host
|
||||||
|
// Close listenning socket.
|
||||||
|
ss.close();
|
||||||
|
break;
|
||||||
|
} else if (ss instanceof SocksServerSocket) {
|
||||||
|
// We can't accept more then one connection
|
||||||
|
s.close();
|
||||||
|
ss.close();
|
||||||
|
throw new SocksException(SocksProxyBase.SOCKS_FAILURE);
|
||||||
|
} else {
|
||||||
|
if (acceptTimeout != 0) { // If timeout is not infinit
|
||||||
|
final long passed = System.currentTimeMillis() - startTime;
|
||||||
|
final int newTimeout = acceptTimeout - (int) passed;
|
||||||
|
|
||||||
|
if (newTimeout <= 0) {
|
||||||
|
throw new InterruptedIOException("newTimeout <= 0");
|
||||||
|
}
|
||||||
|
ss.setSoTimeout(newTimeout);
|
||||||
|
}
|
||||||
|
s.close(); // Drop all connections from other hosts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accepted connection
|
||||||
|
remote_sock = s;
|
||||||
|
remote_in = s.getInputStream();
|
||||||
|
remote_out = s.getOutputStream();
|
||||||
|
|
||||||
|
// Set timeout
|
||||||
|
remote_sock.setSoTimeout(iddleTimeout);
|
||||||
|
|
||||||
|
final InetAddress inetAddress = s.getInetAddress();
|
||||||
|
final int port = s.getPort();
|
||||||
|
log.info("Accepted from {}:{}", s.getInetAddress(), port);
|
||||||
|
|
||||||
|
ProxyMessage response;
|
||||||
|
|
||||||
|
if (msg.version == 5) {
|
||||||
|
final int cmd = SocksProxyBase.SOCKS_SUCCESS;
|
||||||
|
response = new Socks5Message(cmd, inetAddress, port);
|
||||||
|
} else {
|
||||||
|
final int cmd = Socks4Message.REPLY_OK;
|
||||||
|
response = new Socks4Message(cmd, inetAddress, port);
|
||||||
|
}
|
||||||
|
response.write(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ProxyMessage readMsg(final InputStream in) throws IOException {
|
||||||
|
PushbackInputStream push_in;
|
||||||
|
if (in instanceof PushbackInputStream) {
|
||||||
|
push_in = (PushbackInputStream) in;
|
||||||
|
} else {
|
||||||
|
push_in = new PushbackInputStream(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
final int version = push_in.read();
|
||||||
|
push_in.unread(version);
|
||||||
|
|
||||||
|
ProxyMessage msg;
|
||||||
|
|
||||||
|
if (version == 5) {
|
||||||
|
msg = new Socks5Message(push_in, false);
|
||||||
|
} else if (version == 4) {
|
||||||
|
msg = new Socks4Message(push_in, false);
|
||||||
|
} else {
|
||||||
|
throw new SocksException(SocksProxyBase.SOCKS_FAILURE);
|
||||||
|
}
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startPipe(final Socket s) {
|
||||||
|
mode = PIPE_MODE;
|
||||||
|
remote_sock = s;
|
||||||
|
try {
|
||||||
|
remote_in = s.getInputStream();
|
||||||
|
remote_out = s.getOutputStream();
|
||||||
|
pipe_thread1 = Thread.currentThread();
|
||||||
|
pipe_thread2 = new Thread(this);
|
||||||
|
pipe_thread2.start();
|
||||||
|
pipe(in, remote_out);
|
||||||
|
} catch (final IOException ioe) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendErrorMessage(final int error_code) {
|
||||||
|
ProxyMessage err_msg;
|
||||||
|
if (msg instanceof Socks4Message) {
|
||||||
|
err_msg = new Socks4Message(Socks4Message.REPLY_REJECTED);
|
||||||
|
} else {
|
||||||
|
err_msg = new Socks5Message(error_code);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
err_msg.write(out);
|
||||||
|
} catch (final IOException ioe) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void abort() {
|
||||||
|
if (mode == ABORT_MODE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mode = ABORT_MODE;
|
||||||
|
try {
|
||||||
|
log.info("Aborting operation");
|
||||||
|
if (remote_sock != null) {
|
||||||
|
remote_sock.close();
|
||||||
|
}
|
||||||
|
if (sock != null) {
|
||||||
|
sock.close();
|
||||||
|
}
|
||||||
|
if (relayServer != null) {
|
||||||
|
relayServer.stop();
|
||||||
|
}
|
||||||
|
if (ss != null) {
|
||||||
|
ss.close();
|
||||||
|
}
|
||||||
|
if (pipe_thread1 != null) {
|
||||||
|
pipe_thread1.interrupt();
|
||||||
|
}
|
||||||
|
if (pipe_thread2 != null) {
|
||||||
|
pipe_thread2.interrupt();
|
||||||
|
}
|
||||||
|
} catch (final IOException ioe) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static final void log(final ProxyMessage msg) {
|
||||||
|
log.debug("Request version: {}, Command: ", msg.version,
|
||||||
|
command2String(msg.command));
|
||||||
|
|
||||||
|
final String user = msg.version == 4 ? ", User:" + msg.user : "";
|
||||||
|
log.debug("IP:" + msg.ip + ", Port:" + msg.port + user);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void pipe(final InputStream in, final OutputStream out)
|
||||||
|
throws IOException {
|
||||||
|
lastReadTime = System.currentTimeMillis();
|
||||||
|
final byte[] buf = new byte[BUF_SIZE];
|
||||||
|
int len = 0;
|
||||||
|
while (len >= 0) {
|
||||||
|
try {
|
||||||
|
if (len != 0) {
|
||||||
|
out.write(buf, 0, len);
|
||||||
|
out.flush();
|
||||||
|
}
|
||||||
|
len = in.read(buf);
|
||||||
|
lastReadTime = System.currentTimeMillis();
|
||||||
|
} catch (final InterruptedIOException iioe) {
|
||||||
|
if (iddleTimeout == 0) {
|
||||||
|
return;// Other thread interrupted us.
|
||||||
|
}
|
||||||
|
final long timeSinceRead = System.currentTimeMillis()
|
||||||
|
- lastReadTime;
|
||||||
|
|
||||||
|
if (timeSinceRead >= iddleTimeout - 1000) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
len = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static final String command_names[] = { "CONNECT", "BIND", "UDP_ASSOCIATE" };
|
||||||
|
|
||||||
|
static final String command2String(int cmd) {
|
||||||
|
if ((cmd > 0) && (cmd < 4)) {
|
||||||
|
return command_names[cmd - 1];
|
||||||
|
} else {
|
||||||
|
return "Unknown Command " + cmd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,167 @@
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SOCKS4 Reply/Request message.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class Socks4Message extends ProxyMessage {
|
||||||
|
|
||||||
|
private byte[] msgBytes;
|
||||||
|
private int msgLength;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Server failed reply, cmd command for failed request
|
||||||
|
*/
|
||||||
|
public Socks4Message(final int cmd) {
|
||||||
|
super(cmd, null, 0);
|
||||||
|
this.user = null;
|
||||||
|
|
||||||
|
msgLength = 2;
|
||||||
|
msgBytes = new byte[2];
|
||||||
|
|
||||||
|
msgBytes[0] = (byte) 0;
|
||||||
|
msgBytes[1] = (byte) command;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Server successfull reply
|
||||||
|
*/
|
||||||
|
public Socks4Message(final int cmd, final InetAddress ip, final int port) {
|
||||||
|
this(0, cmd, ip, port, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Client request
|
||||||
|
*/
|
||||||
|
public Socks4Message(final int cmd, final InetAddress ip, final int port,
|
||||||
|
final String user) {
|
||||||
|
this(SOCKS_VERSION, cmd, ip, port, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Most general constructor
|
||||||
|
*/
|
||||||
|
public Socks4Message(final int version, final int cmd,
|
||||||
|
final InetAddress ip, final int port, final String user) {
|
||||||
|
|
||||||
|
super(cmd, ip, port);
|
||||||
|
this.user = user;
|
||||||
|
this.version = version;
|
||||||
|
|
||||||
|
msgLength = user == null ? 8 : 9 + user.length();
|
||||||
|
msgBytes = new byte[msgLength];
|
||||||
|
|
||||||
|
msgBytes[0] = (byte) version;
|
||||||
|
msgBytes[1] = (byte) command;
|
||||||
|
msgBytes[2] = (byte) (port >> 8);
|
||||||
|
msgBytes[3] = (byte) port;
|
||||||
|
|
||||||
|
byte[] addr;
|
||||||
|
|
||||||
|
if (ip != null) {
|
||||||
|
addr = ip.getAddress();
|
||||||
|
} else {
|
||||||
|
addr = new byte[4];
|
||||||
|
addr[0] = addr[1] = addr[2] = addr[3] = 0;
|
||||||
|
}
|
||||||
|
System.arraycopy(addr, 0, msgBytes, 4, 4);
|
||||||
|
|
||||||
|
if (user != null) {
|
||||||
|
final byte[] buf = user.getBytes();
|
||||||
|
System.arraycopy(buf, 0, msgBytes, 8, buf.length);
|
||||||
|
msgBytes[msgBytes.length - 1] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise from the stream If clientMode is true attempts to read a
|
||||||
|
* server response otherwise reads a client request see read for more detail
|
||||||
|
*/
|
||||||
|
public Socks4Message(final InputStream in, final boolean clientMode)
|
||||||
|
throws IOException {
|
||||||
|
msgBytes = null;
|
||||||
|
read(in, clientMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void read(final InputStream in) throws IOException {
|
||||||
|
read(in, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void read(final InputStream in, final boolean clientMode)
|
||||||
|
throws IOException {
|
||||||
|
final DataInputStream d_in = new DataInputStream(in);
|
||||||
|
version = d_in.readUnsignedByte();
|
||||||
|
command = d_in.readUnsignedByte();
|
||||||
|
if (clientMode && (command != REPLY_OK)) {
|
||||||
|
String errMsg;
|
||||||
|
// FIXME: Range should be replaced with cases.
|
||||||
|
if ((command > REPLY_OK) && (command < REPLY_BAD_IDENTD)) {
|
||||||
|
errMsg = replyMessage[command - REPLY_OK];
|
||||||
|
} else {
|
||||||
|
errMsg = "Unknown Reply Code";
|
||||||
|
}
|
||||||
|
throw new SocksException(command, errMsg);
|
||||||
|
}
|
||||||
|
port = d_in.readUnsignedShort();
|
||||||
|
final byte[] addr = new byte[4];
|
||||||
|
d_in.readFully(addr);
|
||||||
|
ip = bytes2IP(addr);
|
||||||
|
host = ip.getHostName();
|
||||||
|
if (!clientMode) {
|
||||||
|
int b = in.read();
|
||||||
|
// FIXME: Hope there are no idiots with user name bigger than this
|
||||||
|
final byte[] userBytes = new byte[256];
|
||||||
|
int i = 0;
|
||||||
|
for (i = 0; (i < userBytes.length) && (b > 0); ++i) {
|
||||||
|
userBytes[i] = (byte) b;
|
||||||
|
b = in.read();
|
||||||
|
}
|
||||||
|
user = new String(userBytes, 0, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void write(final OutputStream out) throws IOException {
|
||||||
|
if (msgBytes == null) {
|
||||||
|
final Socks4Message msg;
|
||||||
|
msg = new Socks4Message(version, command, ip, port, user);
|
||||||
|
msgBytes = msg.msgBytes;
|
||||||
|
msgLength = msg.msgLength;
|
||||||
|
}
|
||||||
|
out.write(msgBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Class methods
|
||||||
|
static InetAddress bytes2IP(final byte[] addr) {
|
||||||
|
final String s = bytes2IPV4(addr, 0);
|
||||||
|
try {
|
||||||
|
return InetAddress.getByName(s);
|
||||||
|
} catch (final UnknownHostException uh_ex) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
|
||||||
|
static final String[] replyMessage = { "Request Granted",
|
||||||
|
"Request Rejected or Failed",
|
||||||
|
"Failed request, can't connect to Identd",
|
||||||
|
"Failed request, bad user name" };
|
||||||
|
|
||||||
|
static final int SOCKS_VERSION = 4;
|
||||||
|
|
||||||
|
public final static int REQUEST_CONNECT = 1;
|
||||||
|
public final static int REQUEST_BIND = 2;
|
||||||
|
|
||||||
|
public final static int REPLY_OK = 90;
|
||||||
|
public final static int REPLY_REJECTED = 91;
|
||||||
|
public final static int REPLY_NO_CONNECT = 92;
|
||||||
|
public final static int REPLY_BAD_IDENTD = 93;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,144 @@
|
||||||
|
package com.runjva.sourceforge.jsocks.protocol;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proxy which describes SOCKS4 proxy.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class Socks4Proxy extends SocksProxyBase implements Cloneable {
|
||||||
|
|
||||||
|
// Data members
|
||||||
|
String user;
|
||||||
|
|
||||||
|
// Public Constructors
|
||||||
|
// ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the SOCKS4 proxy
|
||||||
|
*
|
||||||
|
* @param p
|
||||||
|
* Proxy to use to connect to this proxy, allows proxy chaining.
|
||||||
|
* @param proxyHost
|
||||||
|
* Address of the proxy server.
|
||||||
|
* @param proxyPort
|
||||||
|
* Port of the proxy server
|
||||||
|
* @param user
|
||||||
|
* User name to use for identification purposes.
|
||||||
|
* @throws UnknownHostException
|
||||||
|
* If proxyHost can't be resolved.
|
||||||
|
*/
|
||||||
|
public Socks4Proxy(SocksProxyBase p, String proxyHost, int proxyPort,
|
||||||
|
String user) throws UnknownHostException {
|
||||||
|
super(p, proxyHost, proxyPort);
|
||||||
|
this.user = new String(user);
|
||||||
|
version = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the SOCKS4 proxy
|
||||||
|
*
|
||||||
|
* @param proxyHost
|
||||||
|
* Address of the proxy server.
|
||||||
|
* @param proxyPort
|
||||||
|
* Port of the proxy server
|
||||||
|
* @param user
|
||||||
|
* User name to use for identification purposes.
|
||||||
|
* @throws UnknownHostException
|
||||||
|
* If proxyHost can't be resolved.
|
||||||
|
*/
|
||||||
|
public Socks4Proxy(String proxyHost, int proxyPort, String user)
|
||||||
|
throws UnknownHostException {
|
||||||
|
this(null, proxyHost, proxyPort, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the SOCKS4 proxy
|
||||||
|
*
|
||||||
|
* @param p
|
||||||
|
* Proxy to use to connect to this proxy, allows proxy chaining.
|
||||||
|
* @param proxyIP
|
||||||
|
* Address of the proxy server.
|
||||||
|
* @param proxyPort
|
||||||
|
* Port of the proxy server
|
||||||
|
* @param user
|
||||||
|
* User name to use for identification purposes.
|
||||||
|
*/
|
||||||
|
public Socks4Proxy(SocksProxyBase p, InetAddress proxyIP, int proxyPort,
|
||||||
|
String user) {
|
||||||
|
super(p, proxyIP, proxyPort);
|
||||||
|
this.user = new String(user);
|
||||||
|
version = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the SOCKS4 proxy
|
||||||
|
*
|
||||||
|
* @param proxyIP
|
||||||
|
* Address of the proxy server.
|
||||||
|
* @param proxyPort
|
||||||
|
* Port of the proxy server
|
||||||
|
* @param user
|
||||||
|
* User name to use for identification purposes.
|
||||||
|
*/
|
||||||
|
public Socks4Proxy(InetAddress proxyIP, int proxyPort, String user) {
|
||||||
|
this(null, proxyIP, proxyPort, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public instance methods
|
||||||
|
// ========================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a clone of this proxy. Changes made to the clone should not
|
||||||
|
* affect this object.
|
||||||
|
*/
|
||||||
|
public Object clone() {
|
||||||
|
final Socks4Proxy newProxy = new Socks4Proxy(proxyIP, proxyPort, user);
|
||||||
|
newProxy.directHosts = (InetRange) directHosts.clone();
|
||||||
|
newProxy.chainProxy = chainProxy;
|
||||||
|
return newProxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public Static(Class) Methods
|
||||||
|
// ==============================
|
||||||
|
|
||||||
|
// Protected Methods
|
||||||
|
// =================
|
||||||
|
|
||||||
|
protected SocksProxyBase copy() {
|
||||||
|
final Socks4Proxy copy = new Socks4Proxy(proxyIP, proxyPort, user);
|
||||||
|
copy.directHosts = this.directHosts;
|
||||||
|
copy.chainProxy = chainProxy;
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ProxyMessage formMessage(int cmd, InetAddress ip, int port) {
|
||||||
|
switch (cmd) {
|
||||||
|
case SOCKS_CMD_CONNECT:
|
||||||
|
cmd = Socks4Message.REQUEST_CONNECT;
|
||||||
|
break;
|
||||||
|
case SOCKS_CMD_BIND:
|
||||||
|
cmd = Socks4Message.REQUEST_BIND;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new Socks4Message(cmd, ip, port, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ProxyMessage formMessage(int cmd, String host, int port)
|
||||||
|
throws UnknownHostException {
|
||||||
|
|
||||||
|
return formMessage(cmd, InetAddress.getByName(host), port);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ProxyMessage formMessage(InputStream in) throws SocksException,
|
||||||
|
IOException {
|
||||||
|
|
||||||
|
return new Socks4Message(in, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,485 @@
|
||||||
|
package com.runjva.sourceforge.jsocks.protocol;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InterruptedIOException;
|
||||||
|
import java.net.DatagramPacket;
|
||||||
|
import java.net.DatagramSocket;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Datagram socket to interract through the firewall.<BR>
|
||||||
|
* Can be used same way as the normal DatagramSocket. One should be carefull
|
||||||
|
* though with the datagram sizes used, as additional data is present in both
|
||||||
|
* incomming and outgoing datagrams.
|
||||||
|
* <p>
|
||||||
|
* SOCKS5 protocol allows to send host address as either:
|
||||||
|
* <ul>
|
||||||
|
* <li>IPV4, normal 4 byte address. (10 bytes header size)
|
||||||
|
* <li>IPV6, version 6 ip address (not supported by Java as for now). 22 bytes
|
||||||
|
* header size.
|
||||||
|
* <li>Host name,(7+length of the host name bytes header size).
|
||||||
|
* </ul>
|
||||||
|
* As with other Socks equivalents, direct addresses are handled transparently,
|
||||||
|
* that is data will be send directly when required by the proxy settings.
|
||||||
|
* <p>
|
||||||
|
* <b>NOTE:</b><br>
|
||||||
|
* Unlike other SOCKS Sockets, it <b>does not</b> support proxy chaining, and
|
||||||
|
* will throw an exception if proxy has a chain proxy attached. The reason for
|
||||||
|
* that is not my laziness, but rather the restrictions of the SOCKSv5 protocol.
|
||||||
|
* Basicaly SOCKSv5 proxy server, needs to know from which host:port datagrams
|
||||||
|
* will be send for association, and returns address to which datagrams should
|
||||||
|
* be send by the client, but it does not inform client from which host:port it
|
||||||
|
* is going to send datagrams, in fact there is even no guarantee they will be
|
||||||
|
* send at all and from the same address each time.
|
||||||
|
*/
|
||||||
|
public class Socks5DatagramSocket extends DatagramSocket {
|
||||||
|
|
||||||
|
InetAddress relayIP;
|
||||||
|
int relayPort;
|
||||||
|
Socks5Proxy proxy;
|
||||||
|
private boolean server_mode = false;
|
||||||
|
UDPEncapsulation encapsulation;
|
||||||
|
|
||||||
|
private Logger log = LoggerFactory.getLogger(Socks5DatagramSocket.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct Datagram socket for communication over SOCKS5 proxy server.
|
||||||
|
* This constructor uses default proxy, the one set with
|
||||||
|
* Proxy.setDefaultProxy() method. If default proxy is not set or it is set
|
||||||
|
* to version4 proxy, which does not support datagram forwarding, throws
|
||||||
|
* SocksException.
|
||||||
|
*/
|
||||||
|
public Socks5DatagramSocket() throws SocksException, IOException {
|
||||||
|
this(SocksProxyBase.defaultProxy, 0, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct Datagram socket for communication over SOCKS5 proxy server. And
|
||||||
|
* binds it to the specified local port. This constructor uses default
|
||||||
|
* proxy, the one set with Proxy.setDefaultProxy() method. If default proxy
|
||||||
|
* is not set or it is set to version4 proxy, which does not support
|
||||||
|
* datagram forwarding, throws SocksException.
|
||||||
|
*/
|
||||||
|
public Socks5DatagramSocket(int port) throws SocksException, IOException {
|
||||||
|
this(SocksProxyBase.defaultProxy, port, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct Datagram socket for communication over SOCKS5 proxy server. And
|
||||||
|
* binds it to the specified local port and address. This constructor uses
|
||||||
|
* default proxy, the one set with Proxy.setDefaultProxy() method. If
|
||||||
|
* default proxy is not set or it is set to version4 proxy, which does not
|
||||||
|
* support datagram forwarding, throws SocksException.
|
||||||
|
*/
|
||||||
|
public Socks5DatagramSocket(int port, InetAddress ip)
|
||||||
|
throws SocksException, IOException {
|
||||||
|
this(SocksProxyBase.defaultProxy, port, ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs datagram socket for communication over specified proxy. And
|
||||||
|
* binds it to the given local address and port. Address of null and port of
|
||||||
|
* 0, signify any availabale port/address. Might throw SocksException, if:
|
||||||
|
* <ol>
|
||||||
|
* <li>Given version of proxy does not support UDP_ASSOCIATE.
|
||||||
|
* <li>Proxy can't be reached.
|
||||||
|
* <li>Authorization fails.
|
||||||
|
* <li>Proxy does not want to perform udp forwarding, for any reason.
|
||||||
|
* </ol>
|
||||||
|
* Might throw IOException if binding datagram socket to given address/port
|
||||||
|
* fails. See java.net.DatagramSocket for more details.
|
||||||
|
*/
|
||||||
|
public Socks5DatagramSocket(SocksProxyBase p, int port, InetAddress ip)
|
||||||
|
throws SocksException, IOException {
|
||||||
|
|
||||||
|
super(port, ip);
|
||||||
|
|
||||||
|
if (p == null) {
|
||||||
|
throw new SocksException(SocksProxyBase.SOCKS_NO_PROXY);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(p instanceof Socks5Proxy)) {
|
||||||
|
final String s = "Datagram Socket needs Proxy version 5";
|
||||||
|
throw new SocksException(-1, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p.chainProxy != null) {
|
||||||
|
final String s = "Datagram Sockets do not support proxy chaining.";
|
||||||
|
throw new SocksException(SocksProxyBase.SOCKS_JUST_ERROR, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
proxy = (Socks5Proxy) p.copy();
|
||||||
|
|
||||||
|
final ProxyMessage msg = proxy.udpAssociate(super.getLocalAddress(),
|
||||||
|
super.getLocalPort());
|
||||||
|
|
||||||
|
relayIP = msg.ip;
|
||||||
|
if (relayIP.getHostAddress().equals("0.0.0.0")) {
|
||||||
|
// FIXME: What happens here?
|
||||||
|
relayIP = proxy.proxyIP;
|
||||||
|
}
|
||||||
|
relayPort = msg.port;
|
||||||
|
|
||||||
|
encapsulation = proxy.udp_encapsulation;
|
||||||
|
|
||||||
|
log.debug("Datagram Socket:{}:{}", getLocalAddress(), getLocalPort());
|
||||||
|
log.debug("Socks5Datagram: {}:{}", relayIP, relayPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by UDPRelayServer.
|
||||||
|
*/
|
||||||
|
Socks5DatagramSocket(boolean server_mode, UDPEncapsulation encapsulation,
|
||||||
|
InetAddress relayIP, int relayPort) throws IOException {
|
||||||
|
super();
|
||||||
|
this.server_mode = server_mode;
|
||||||
|
this.relayIP = relayIP;
|
||||||
|
this.relayPort = relayPort;
|
||||||
|
this.encapsulation = encapsulation;
|
||||||
|
this.proxy = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends the Datagram either through the proxy or directly depending on
|
||||||
|
* current proxy settings and destination address. <BR>
|
||||||
|
*
|
||||||
|
* <B> NOTE: </B> DatagramPacket size should be at least 10 bytes less than
|
||||||
|
* the systems limit.
|
||||||
|
*
|
||||||
|
* <P>
|
||||||
|
* See documentation on java.net.DatagramSocket for full details on how to
|
||||||
|
* use this method.
|
||||||
|
*
|
||||||
|
* @param dp
|
||||||
|
* Datagram to send.
|
||||||
|
* @throws IOException
|
||||||
|
* If error happens with I/O.
|
||||||
|
*/
|
||||||
|
public void send(DatagramPacket dp) throws IOException {
|
||||||
|
// If the host should be accessed directly, send it as is.
|
||||||
|
if (!server_mode && proxy.isDirect(dp.getAddress())) {
|
||||||
|
super.send(dp);
|
||||||
|
log.debug("Sending datagram packet directly:");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final byte[] head = formHeader(dp.getAddress(), dp.getPort());
|
||||||
|
byte[] buf = new byte[head.length + dp.getLength()];
|
||||||
|
final byte[] data = dp.getData();
|
||||||
|
|
||||||
|
// Merge head and data
|
||||||
|
System.arraycopy(head, 0, buf, 0, head.length);
|
||||||
|
// System.arraycopy(data,dp.getOffset(),buf,head.length,dp.getLength());
|
||||||
|
System.arraycopy(data, 0, buf, head.length, dp.getLength());
|
||||||
|
|
||||||
|
if (encapsulation != null) {
|
||||||
|
buf = encapsulation.udpEncapsulate(buf, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
super.send(new DatagramPacket(buf, buf.length, relayIP, relayPort));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method allows to send datagram packets with address type DOMAINNAME.
|
||||||
|
* SOCKS5 allows to specify host as names rather than ip addresses.Using
|
||||||
|
* this method one can send udp datagrams through the proxy, without having
|
||||||
|
* to know the ip address of the destination host.
|
||||||
|
* <p>
|
||||||
|
* If proxy specified for that socket has an option resolveAddrLocally set
|
||||||
|
* to true host will be resolved, and the datagram will be send with address
|
||||||
|
* type IPV4, if resolve fails, UnknownHostException is thrown.
|
||||||
|
*
|
||||||
|
* @param dp
|
||||||
|
* Datagram to send, it should contain valid port and data
|
||||||
|
* @param host
|
||||||
|
* Host name to which datagram should be send.
|
||||||
|
* @throws IOException
|
||||||
|
* If error happens with I/O, or the host can't be resolved when
|
||||||
|
* proxy settings say that hosts should be resolved locally.
|
||||||
|
* @see Socks5Proxy#resolveAddrLocally(boolean)
|
||||||
|
*/
|
||||||
|
public void send(DatagramPacket dp, String host) throws IOException {
|
||||||
|
if (proxy.isDirect(host)) {
|
||||||
|
dp.setAddress(InetAddress.getByName(host));
|
||||||
|
super.send(dp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((proxy).resolveAddrLocally) {
|
||||||
|
dp.setAddress(InetAddress.getByName(host));
|
||||||
|
}
|
||||||
|
|
||||||
|
final byte[] head = formHeader(host, dp.getPort());
|
||||||
|
byte[] buf = new byte[head.length + dp.getLength()];
|
||||||
|
final byte[] data = dp.getData();
|
||||||
|
// Merge head and data
|
||||||
|
System.arraycopy(head, 0, buf, 0, head.length);
|
||||||
|
// System.arraycopy(data,dp.getOffset(),buf,head.length,dp.getLength());
|
||||||
|
System.arraycopy(data, 0, buf, head.length, dp.getLength());
|
||||||
|
|
||||||
|
if (encapsulation != null) {
|
||||||
|
buf = encapsulation.udpEncapsulate(buf, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
super.send(new DatagramPacket(buf, buf.length, relayIP, relayPort));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receives udp packet. If packet have arrived from the proxy relay server,
|
||||||
|
* it is processed and address and port of the packet are set to the address
|
||||||
|
* and port of sending host.<BR>
|
||||||
|
* If the packet arrived from anywhere else it is not changed.<br>
|
||||||
|
* <B> NOTE: </B> DatagramPacket size should be at least 10 bytes bigger
|
||||||
|
* than the largest packet you expect (this is for IPV4 addresses). For
|
||||||
|
* hostnames and IPV6 it is even more.
|
||||||
|
*
|
||||||
|
* @param dp
|
||||||
|
* Datagram in which all relevent information will be copied.
|
||||||
|
*/
|
||||||
|
public void receive(DatagramPacket dp) throws IOException {
|
||||||
|
super.receive(dp);
|
||||||
|
|
||||||
|
if (server_mode) {
|
||||||
|
// Drop all datagrams not from relayIP/relayPort
|
||||||
|
final int init_length = dp.getLength();
|
||||||
|
final int initTimeout = getSoTimeout();
|
||||||
|
final long startTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
while (!relayIP.equals(dp.getAddress())
|
||||||
|
|| (relayPort != dp.getPort())) {
|
||||||
|
|
||||||
|
// Restore datagram size
|
||||||
|
dp.setLength(init_length);
|
||||||
|
|
||||||
|
// If there is a non-infinit timeout on this socket
|
||||||
|
// Make sure that it happens no matter how often unexpected
|
||||||
|
// packets arrive.
|
||||||
|
if (initTimeout != 0) {
|
||||||
|
final long passed = System.currentTimeMillis() - startTime;
|
||||||
|
final int newTimeout = initTimeout - (int) passed;
|
||||||
|
|
||||||
|
if (newTimeout <= 0) {
|
||||||
|
throw new InterruptedIOException(
|
||||||
|
"In Socks5DatagramSocket->receive()");
|
||||||
|
}
|
||||||
|
setSoTimeout(newTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
super.receive(dp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore timeout settings
|
||||||
|
if (initTimeout != 0) {
|
||||||
|
setSoTimeout(initTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (!relayIP.equals(dp.getAddress())
|
||||||
|
|| (relayPort != dp.getPort())) {
|
||||||
|
return; // Recieved direct packet
|
||||||
|
// If the datagram is not from the relay server, return it it as is.
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] data;
|
||||||
|
data = dp.getData();
|
||||||
|
|
||||||
|
if (encapsulation != null) {
|
||||||
|
data = encapsulation.udpEncapsulate(data, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: What is this?
|
||||||
|
final int offset = 0; // Java 1.1
|
||||||
|
// int offset = dp.getOffset(); //Java 1.2
|
||||||
|
|
||||||
|
final ByteArrayInputStream bIn = new ByteArrayInputStream(data, offset,
|
||||||
|
dp.getLength());
|
||||||
|
|
||||||
|
final ProxyMessage msg = new Socks5Message(bIn);
|
||||||
|
dp.setPort(msg.port);
|
||||||
|
dp.setAddress(msg.getInetAddress());
|
||||||
|
|
||||||
|
// what wasn't read by the Message is the data
|
||||||
|
final int data_length = bIn.available();
|
||||||
|
// Shift data to the left
|
||||||
|
System.arraycopy(data, offset + dp.getLength() - data_length, data,
|
||||||
|
offset, data_length);
|
||||||
|
|
||||||
|
dp.setLength(data_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns port assigned by the proxy, to which datagrams are relayed. It is
|
||||||
|
* not the same port to which other party should send datagrams.
|
||||||
|
*
|
||||||
|
* @return Port assigned by socks server to which datagrams are send for
|
||||||
|
* association.
|
||||||
|
*/
|
||||||
|
public int getLocalPort() {
|
||||||
|
if (server_mode) {
|
||||||
|
return super.getLocalPort();
|
||||||
|
}
|
||||||
|
return relayPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Address assigned by the proxy, to which datagrams are send for relay. It
|
||||||
|
* is not necesseraly the same address, to which other party should send
|
||||||
|
* datagrams.
|
||||||
|
*
|
||||||
|
* @return Address to which datagrams are send for association.
|
||||||
|
*/
|
||||||
|
public InetAddress getLocalAddress() {
|
||||||
|
if (server_mode) {
|
||||||
|
return super.getLocalAddress();
|
||||||
|
}
|
||||||
|
return relayIP;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes datagram socket, and proxy connection.
|
||||||
|
*/
|
||||||
|
public void close() {
|
||||||
|
if (!server_mode) {
|
||||||
|
proxy.endSession();
|
||||||
|
}
|
||||||
|
super.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method checks wether proxy still runs udp forwarding service for
|
||||||
|
* this socket.
|
||||||
|
* <p>
|
||||||
|
* This methods checks wether the primary connection to proxy server is
|
||||||
|
* active. If it is, chances are that proxy continues to forward datagrams
|
||||||
|
* being send from this socket. If it was closed, most likely datagrams are
|
||||||
|
* no longer being forwarded by the server.
|
||||||
|
* <p>
|
||||||
|
* Proxy might decide to stop forwarding datagrams, in which case it should
|
||||||
|
* close primary connection. This method allows to check, wether this have
|
||||||
|
* been done.
|
||||||
|
* <p>
|
||||||
|
* You can specify timeout for which we should be checking EOF condition on
|
||||||
|
* the primary connection. Timeout is in milliseconds. Specifying 0 as
|
||||||
|
* timeout implies infinity, in which case method will block, until
|
||||||
|
* connection to proxy is closed or an error happens, and then return false.
|
||||||
|
* <p>
|
||||||
|
* One possible scenario is to call isProxyactive(0) in separate thread, and
|
||||||
|
* once it returned notify other threads about this event.
|
||||||
|
*
|
||||||
|
* @param timeout
|
||||||
|
* For how long this method should block, before returning.
|
||||||
|
* @return true if connection to proxy is active, false if eof or error
|
||||||
|
* condition have been encountered on the connection.
|
||||||
|
*/
|
||||||
|
public boolean isProxyAlive(int timeout) {
|
||||||
|
if (server_mode) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (proxy != null) {
|
||||||
|
try {
|
||||||
|
proxy.proxySocket.setSoTimeout(timeout);
|
||||||
|
|
||||||
|
final int eof = proxy.in.read();
|
||||||
|
if (eof < 0) {
|
||||||
|
return false; // EOF encountered.
|
||||||
|
} else {
|
||||||
|
log.warn("This really should not happen");
|
||||||
|
return true; // This really should not happen
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (final InterruptedIOException iioe) {
|
||||||
|
return true; // read timed out.
|
||||||
|
} catch (final IOException ioe) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PRIVATE METHODS
|
||||||
|
// ////////////////
|
||||||
|
|
||||||
|
private byte[] formHeader(InetAddress ip, int port) {
|
||||||
|
final Socks5Message request = new Socks5Message(0, ip, port);
|
||||||
|
request.data[0] = 0;
|
||||||
|
return request.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] formHeader(String host, int port) {
|
||||||
|
final Socks5Message request = new Socks5Message(0, host, port);
|
||||||
|
request.data[0] = 0;
|
||||||
|
return request.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ======================================================================
|
||||||
|
*
|
||||||
|
* //Mainly Test functions //////////////////////
|
||||||
|
*
|
||||||
|
* private String bytes2String(byte[] b){ String s=""; char[] hex_digit = {
|
||||||
|
* '0','1','2','3','4','5','6','7','8','9', 'A','B','C','D','E','F'};
|
||||||
|
* for(int i=0;i<b.length;++i){ int i1 = (b[i] & 0xF0) >> 4; int i2 = b[i] &
|
||||||
|
* 0xF; s+=hex_digit[i1]; s+=hex_digit[i2]; s+=" "; } return s; } private
|
||||||
|
* static final void debug(String s){ if(DEBUG) System.out.print(s); }
|
||||||
|
*
|
||||||
|
* private static final boolean DEBUG = true;
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* public static void usage(){ System.err.print(
|
||||||
|
* "Usage: java Socks.SocksDatagramSocket host port [socksHost socksPort]\n"
|
||||||
|
* ); }
|
||||||
|
*
|
||||||
|
* static final int defaultProxyPort = 1080; //Default Port static final
|
||||||
|
* String defaultProxyHost = "www-proxy"; //Default proxy
|
||||||
|
*
|
||||||
|
* public static void main(String args[]){ int port; String host; int
|
||||||
|
* proxyPort; String proxyHost; InetAddress ip;
|
||||||
|
*
|
||||||
|
* if(args.length > 1 && args.length < 5){ try{
|
||||||
|
*
|
||||||
|
* host = args[0]; port = Integer.parseInt(args[1]);
|
||||||
|
*
|
||||||
|
* proxyPort =(args.length > 3)? Integer.parseInt(args[3]) :
|
||||||
|
* defaultProxyPort;
|
||||||
|
*
|
||||||
|
* host = args[0]; ip = InetAddress.getByName(host);
|
||||||
|
*
|
||||||
|
* proxyHost =(args.length > 2)? args[2] : defaultProxyHost;
|
||||||
|
*
|
||||||
|
* Proxy.setDefaultProxy(proxyHost,proxyPort); Proxy p =
|
||||||
|
* Proxy.getDefaultProxy(); p.addDirect("lux");
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* DatagramSocket ds = new Socks5DatagramSocket();
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* BufferedReader in = new BufferedReader( new
|
||||||
|
* InputStreamReader(System.in)); String s;
|
||||||
|
*
|
||||||
|
* System.out.print("Enter line:"); s = in.readLine();
|
||||||
|
*
|
||||||
|
* while(s != null){ byte[] data = (s+"\r\n").getBytes(); DatagramPacket dp
|
||||||
|
* = new DatagramPacket(data,0,data.length, ip,port);
|
||||||
|
* System.out.println("Sending to: "+ip+":"+port); ds.send(dp); dp = new
|
||||||
|
* DatagramPacket(new byte[1024],1024);
|
||||||
|
*
|
||||||
|
* System.out.println("Trying to recieve on port:"+ ds.getLocalPort());
|
||||||
|
* ds.receive(dp); System.out.print("Recieved:\n"+
|
||||||
|
* "From:"+dp.getAddress()+":"+dp.getPort()+ "\n\n"+ new
|
||||||
|
* String(dp.getData(),dp.getOffset(),dp.getLength())+"\n" );
|
||||||
|
* System.out.print("Enter line:"); s = in.readLine();
|
||||||
|
*
|
||||||
|
* } ds.close(); System.exit(1);
|
||||||
|
*
|
||||||
|
* }catch(SocksException s_ex){ System.err.println("SocksException:"+s_ex);
|
||||||
|
* s_ex.printStackTrace(); System.exit(1); }catch(IOException io_ex){
|
||||||
|
* io_ex.printStackTrace(); System.exit(1); }catch(NumberFormatException
|
||||||
|
* num_ex){ usage(); num_ex.printStackTrace(); System.exit(1); }
|
||||||
|
*
|
||||||
|
* }else{ usage(); } }
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,330 @@
|
||||||
|
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;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SOCKS5 request/response message.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class Socks5Message extends ProxyMessage {
|
||||||
|
/** Address type of given message */
|
||||||
|
public int addrType;
|
||||||
|
|
||||||
|
byte[] data;
|
||||||
|
|
||||||
|
private Logger log = LoggerFactory.getLogger(Socks5Message.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
log.debug("Doing ATYP_DOMAINNAME");
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,295 @@
|
||||||
|
package com.runjva.sourceforge.jsocks.protocol;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.net.SocketException;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SOCKS5 Proxy.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class Socks5Proxy extends SocksProxyBase implements Cloneable {
|
||||||
|
|
||||||
|
// Data members
|
||||||
|
private Hashtable<Integer, Authentication> authMethods = new Hashtable<Integer, Authentication>();
|
||||||
|
private int selectedMethod;
|
||||||
|
|
||||||
|
boolean resolveAddrLocally = true;
|
||||||
|
UDPEncapsulation udp_encapsulation = null;
|
||||||
|
|
||||||
|
// Public Constructors
|
||||||
|
// ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates SOCKS5 proxy.
|
||||||
|
*
|
||||||
|
* @param p
|
||||||
|
* Proxy to use to connect to this proxy, allows proxy chaining.
|
||||||
|
* @param proxyHost
|
||||||
|
* Host on which a Proxy server runs.
|
||||||
|
* @param proxyPort
|
||||||
|
* Port on which a Proxy server listens for connections.
|
||||||
|
* @throws UnknownHostException
|
||||||
|
* If proxyHost can't be resolved.
|
||||||
|
*/
|
||||||
|
public Socks5Proxy(SocksProxyBase p, String proxyHost, int proxyPort)
|
||||||
|
throws UnknownHostException {
|
||||||
|
|
||||||
|
super(p, proxyHost, proxyPort);
|
||||||
|
version = 5;
|
||||||
|
setAuthenticationMethod(0, new AuthenticationNone());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates SOCKS5 proxy.
|
||||||
|
*
|
||||||
|
* @param proxyHost
|
||||||
|
* Host on which a Proxy server runs.
|
||||||
|
* @param proxyPort
|
||||||
|
* Port on which a Proxy server listens for connections.
|
||||||
|
* @throws UnknownHostException
|
||||||
|
* If proxyHost can't be resolved.
|
||||||
|
*/
|
||||||
|
public Socks5Proxy(String proxyHost, int proxyPort)
|
||||||
|
throws UnknownHostException {
|
||||||
|
this(null, proxyHost, proxyPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates SOCKS5 proxy.
|
||||||
|
*
|
||||||
|
* @param p
|
||||||
|
* Proxy to use to connect to this proxy, allows proxy chaining.
|
||||||
|
* @param proxyIP
|
||||||
|
* Host on which a Proxy server runs.
|
||||||
|
* @param proxyPort
|
||||||
|
* Port on which a Proxy server listens for connections.
|
||||||
|
*/
|
||||||
|
public Socks5Proxy(SocksProxyBase p, InetAddress proxyIP, int proxyPort) {
|
||||||
|
super(p, proxyIP, proxyPort);
|
||||||
|
version = 5;
|
||||||
|
setAuthenticationMethod(0, new AuthenticationNone());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates SOCKS5 proxy.
|
||||||
|
*
|
||||||
|
* @param proxyIP
|
||||||
|
* Host on which a Proxy server runs.
|
||||||
|
* @param proxyPort
|
||||||
|
* Port on which a Proxy server listens for connections.
|
||||||
|
*/
|
||||||
|
public Socks5Proxy(InetAddress proxyIP, int proxyPort) {
|
||||||
|
this(null, proxyIP, proxyPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public instance methods
|
||||||
|
// ========================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wether to resolve address locally or to let proxy do so.
|
||||||
|
* <p>
|
||||||
|
* SOCKS5 protocol allows to send host names rather then IPs in the
|
||||||
|
* requests, this option controls wether the hostnames should be send to the
|
||||||
|
* proxy server as names, or should they be resolved locally.
|
||||||
|
*
|
||||||
|
* @param doResolve
|
||||||
|
* Wether to perform resolution locally.
|
||||||
|
* @return Previous settings.
|
||||||
|
*/
|
||||||
|
public boolean resolveAddrLocally(boolean doResolve) {
|
||||||
|
final boolean old = resolveAddrLocally;
|
||||||
|
resolveAddrLocally = doResolve;
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current setting on how the addresses should be handled.
|
||||||
|
*
|
||||||
|
* @return Current setting for address resolution.
|
||||||
|
* @see Socks5Proxy#resolveAddrLocally(boolean doResolve)
|
||||||
|
*/
|
||||||
|
public boolean resolveAddrLocally() {
|
||||||
|
return resolveAddrLocally;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds another authentication method.
|
||||||
|
*
|
||||||
|
* @param methodId
|
||||||
|
* Authentication method id, see rfc1928
|
||||||
|
* @param method
|
||||||
|
* Implementation of Authentication
|
||||||
|
* @see Authentication
|
||||||
|
*/
|
||||||
|
public boolean setAuthenticationMethod(int methodId, Authentication method) {
|
||||||
|
if ((methodId < 0) || (methodId > 255)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (method == null) {
|
||||||
|
// Want to remove a particular method
|
||||||
|
return (authMethods.remove(new Integer(methodId)) != null);
|
||||||
|
} else {// Add the method, or rewrite old one
|
||||||
|
authMethods.put(new Integer(methodId), method);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get authentication method, which corresponds to given method id
|
||||||
|
*
|
||||||
|
* @param methodId
|
||||||
|
* Authentication method id.
|
||||||
|
* @return Implementation for given method or null, if one was not set.
|
||||||
|
*/
|
||||||
|
public Authentication getAuthenticationMethod(int methodId) {
|
||||||
|
final Object method = authMethods.get(new Integer(methodId));
|
||||||
|
if (method == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (Authentication) method;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a clone of this Proxy. clone() returns an
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public Object clone() {
|
||||||
|
final Socks5Proxy newProxy = new Socks5Proxy(proxyIP, proxyPort);
|
||||||
|
|
||||||
|
final Object o = this.authMethods.clone();
|
||||||
|
newProxy.authMethods = (Hashtable<Integer, Authentication>) o;
|
||||||
|
|
||||||
|
newProxy.directHosts = (InetRange) directHosts.clone();
|
||||||
|
newProxy.resolveAddrLocally = resolveAddrLocally;
|
||||||
|
newProxy.chainProxy = chainProxy;
|
||||||
|
return newProxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public Static(Class) Methods
|
||||||
|
// ==============================
|
||||||
|
|
||||||
|
// Protected Methods
|
||||||
|
// =================
|
||||||
|
|
||||||
|
protected SocksProxyBase copy() {
|
||||||
|
final Socks5Proxy copy = new Socks5Proxy(proxyIP, proxyPort);
|
||||||
|
|
||||||
|
copy.authMethods = this.authMethods; // same Hash, no copy
|
||||||
|
copy.directHosts = this.directHosts;
|
||||||
|
copy.chainProxy = this.chainProxy;
|
||||||
|
copy.resolveAddrLocally = this.resolveAddrLocally;
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected void startSession() throws SocksException {
|
||||||
|
super.startSession();
|
||||||
|
Authentication auth;
|
||||||
|
final Socket ps = proxySocket; // The name is too long
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
final byte nMethods = (byte) authMethods.size(); // Number of
|
||||||
|
// methods
|
||||||
|
|
||||||
|
final byte[] buf = new byte[2 + nMethods]; // 2 is for VER,NMETHODS
|
||||||
|
buf[0] = (byte) version;
|
||||||
|
buf[1] = nMethods; // Number of methods
|
||||||
|
int i = 2;
|
||||||
|
|
||||||
|
final Enumeration<Integer> ids = authMethods.keys();
|
||||||
|
while (ids.hasMoreElements()) {
|
||||||
|
buf[i++] = (byte) ids.nextElement().intValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
out.write(buf);
|
||||||
|
out.flush();
|
||||||
|
|
||||||
|
final int versionNumber = in.read();
|
||||||
|
selectedMethod = in.read();
|
||||||
|
|
||||||
|
if ((versionNumber < 0) || (selectedMethod < 0)) {
|
||||||
|
// EOF condition was reached
|
||||||
|
endSession();
|
||||||
|
final String s = "Connection to proxy lost.";
|
||||||
|
throw new SocksException(SOCKS_PROXY_IO_ERROR, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (versionNumber < version) {
|
||||||
|
// What should we do??
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedMethod == 0xFF) { // No method selected
|
||||||
|
ps.close();
|
||||||
|
throw (new SocksException(SOCKS_AUTH_NOT_SUPPORTED));
|
||||||
|
}
|
||||||
|
|
||||||
|
auth = getAuthenticationMethod(selectedMethod);
|
||||||
|
if (auth == null) {
|
||||||
|
// This shouldn't happen, unless method was removed by other
|
||||||
|
// thread, or the server stuffed up
|
||||||
|
final String s = "Specified Authentication not found!";
|
||||||
|
throw new SocksException(SOCKS_JUST_ERROR, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Object[] in_out;
|
||||||
|
in_out = auth.doSocksAuthentication(selectedMethod, ps);
|
||||||
|
|
||||||
|
if (in_out == null) {
|
||||||
|
// Authentication failed by some reason
|
||||||
|
throw (new SocksException(SOCKS_AUTH_FAILURE));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Most authentication methods are expected to return simply the
|
||||||
|
* input/output streams associated with the socket. However if the
|
||||||
|
* auth. method requires some kind of encryption/decryption being
|
||||||
|
* done on the connection it should provide classes to handle I/O.
|
||||||
|
*/
|
||||||
|
|
||||||
|
in = (InputStream) in_out[0];
|
||||||
|
out = (OutputStream) in_out[1];
|
||||||
|
if (in_out.length > 2) {
|
||||||
|
udp_encapsulation = (UDPEncapsulation) in_out[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (final SocksException s_ex) {
|
||||||
|
throw s_ex;
|
||||||
|
} catch (final UnknownHostException uh_ex) {
|
||||||
|
throw new SocksException(SOCKS_PROXY_NO_CONNECT, uh_ex);
|
||||||
|
} catch (final SocketException so_ex) {
|
||||||
|
throw new SocksException(SOCKS_PROXY_NO_CONNECT, so_ex);
|
||||||
|
} catch (final IOException io_ex) {
|
||||||
|
throw new SocksException(SOCKS_PROXY_IO_ERROR, io_ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ProxyMessage formMessage(int cmd, InetAddress ip, int port) {
|
||||||
|
return new Socks5Message(cmd, ip, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ProxyMessage formMessage(int cmd, String host, int port)
|
||||||
|
throws UnknownHostException {
|
||||||
|
if (resolveAddrLocally) {
|
||||||
|
return formMessage(cmd, InetAddress.getByName(host), port);
|
||||||
|
} else {
|
||||||
|
return new Socks5Message(cmd, host, port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ProxyMessage formMessage(InputStream in) throws SocksException,
|
||||||
|
IOException {
|
||||||
|
return new Socks5Message(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,111 @@
|
||||||
|
package com.runjva.sourceforge.jsocks.protocol;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception thrown by various socks classes to indicate errors with protocol or
|
||||||
|
* unsuccessfull server responses.
|
||||||
|
*/
|
||||||
|
public class SocksException extends java.io.IOException {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a SocksException with given errorcode.
|
||||||
|
* <p>
|
||||||
|
* Tries to look up message which corresponds to this error code.
|
||||||
|
*
|
||||||
|
* @param errCode
|
||||||
|
* Error code for this exception.
|
||||||
|
*/
|
||||||
|
public SocksException(int errCode) {
|
||||||
|
this.errCode = errCode;
|
||||||
|
lookupErrorString(errCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void lookupErrorString(int errCode) {
|
||||||
|
if ((errCode >> 16) == 0) {
|
||||||
|
if (errCode <= serverReplyMessage.length) {
|
||||||
|
errString = serverReplyMessage[errCode];
|
||||||
|
} else {
|
||||||
|
errString = UNASSIGNED_ERROR_MESSAGE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Local error
|
||||||
|
errCode = (errCode >> 16) - 1;
|
||||||
|
if (errCode <= localErrorMessage.length) {
|
||||||
|
errString = localErrorMessage[errCode];
|
||||||
|
} else {
|
||||||
|
errString = UNASSIGNED_ERROR_MESSAGE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a SocksException with given error code, and a Throwable cause
|
||||||
|
*
|
||||||
|
* @param errCode
|
||||||
|
* @param t
|
||||||
|
* Nested exception for debugging purposes.
|
||||||
|
*/
|
||||||
|
public SocksException(int errCode, Throwable t) {
|
||||||
|
super(t); // Java 1.6+
|
||||||
|
this.errCode = errCode;
|
||||||
|
lookupErrorString(errCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a SocksException with given error code and message.
|
||||||
|
*
|
||||||
|
* @param errCode
|
||||||
|
* Error code.
|
||||||
|
* @param errString
|
||||||
|
* Error Message.
|
||||||
|
*/
|
||||||
|
public SocksException(int errCode, String errString) {
|
||||||
|
this.errCode = errCode;
|
||||||
|
this.errString = errString;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SocksException(int errCode, String string, Throwable t) {
|
||||||
|
super(string, t); // Java 1.6+
|
||||||
|
this.errCode = errCode;
|
||||||
|
this.errString = string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the error code associated with this exception.
|
||||||
|
*
|
||||||
|
* @return Error code associated with this exception.
|
||||||
|
*/
|
||||||
|
public int getErrorCode() {
|
||||||
|
return errCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get human readable representation of this exception.
|
||||||
|
*
|
||||||
|
* @return String represntation of this exception.
|
||||||
|
*/
|
||||||
|
public String toString() {
|
||||||
|
return errString;
|
||||||
|
}
|
||||||
|
|
||||||
|
static final String UNASSIGNED_ERROR_MESSAGE = "Unknown error message";
|
||||||
|
|
||||||
|
static final String serverReplyMessage[] = { "Succeeded",
|
||||||
|
"General SOCKS server failure",
|
||||||
|
"Connection not allowed by ruleset", "Network unreachable",
|
||||||
|
"Host unreachable", "Connection refused", "TTL expired",
|
||||||
|
"Command not supported", "Address type not supported" };
|
||||||
|
|
||||||
|
static final String localErrorMessage[] = { "SOCKS server not specified",
|
||||||
|
"Unable to contact SOCKS server", "IO error",
|
||||||
|
"None of Authentication methods are supported",
|
||||||
|
"Authentication failed", "General SOCKS fault" };
|
||||||
|
|
||||||
|
String errString;
|
||||||
|
int errCode;
|
||||||
|
|
||||||
|
}// End of SocksException class
|
||||||
|
|
|
@ -0,0 +1,543 @@
|
||||||
|
package com.runjva.sourceforge.jsocks.protocol;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InterruptedIOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract class Proxy, base for classes Socks4Proxy and Socks5Proxy. Defines
|
||||||
|
* methods for specifying default proxy, to be used by all classes of this
|
||||||
|
* package.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public abstract class SocksProxyBase {
|
||||||
|
|
||||||
|
// Data members
|
||||||
|
protected InetRange directHosts = new InetRange();
|
||||||
|
|
||||||
|
protected InetAddress proxyIP = null;
|
||||||
|
protected String proxyHost = null;
|
||||||
|
protected int proxyPort;
|
||||||
|
protected Socket proxySocket = null;
|
||||||
|
|
||||||
|
protected InputStream in;
|
||||||
|
protected OutputStream out;
|
||||||
|
|
||||||
|
protected int version;
|
||||||
|
|
||||||
|
protected SocksProxyBase chainProxy = null;
|
||||||
|
|
||||||
|
// Protected static/class variables
|
||||||
|
protected static SocksProxyBase defaultProxy = null;
|
||||||
|
|
||||||
|
// Constructors
|
||||||
|
// ====================
|
||||||
|
SocksProxyBase(SocksProxyBase chainProxy, String proxyHost, int proxyPort)
|
||||||
|
throws UnknownHostException {
|
||||||
|
this.chainProxy = chainProxy;
|
||||||
|
this.proxyHost = proxyHost;
|
||||||
|
|
||||||
|
if (chainProxy == null) {
|
||||||
|
this.proxyIP = InetAddress.getByName(proxyHost);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.proxyPort = proxyPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
SocksProxyBase(String proxyHost, int proxyPort) throws UnknownHostException {
|
||||||
|
this(null, proxyHost, proxyPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
SocksProxyBase(SocksProxyBase chainProxy, InetAddress proxyIP, int proxyPort) {
|
||||||
|
this.chainProxy = chainProxy;
|
||||||
|
this.proxyIP = proxyIP;
|
||||||
|
this.proxyPort = proxyPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
SocksProxyBase(InetAddress proxyIP, int proxyPort) {
|
||||||
|
this(null, proxyIP, proxyPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
SocksProxyBase(SocksProxyBase p) {
|
||||||
|
this.proxyIP = p.proxyIP;
|
||||||
|
this.proxyPort = p.proxyPort;
|
||||||
|
this.version = p.version;
|
||||||
|
this.directHosts = p.directHosts;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public instance methods
|
||||||
|
// ========================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the port on which proxy server is running.
|
||||||
|
*
|
||||||
|
* @return Proxy port.
|
||||||
|
*/
|
||||||
|
public int getPort() {
|
||||||
|
return proxyPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the ip address of the proxy server host.
|
||||||
|
*
|
||||||
|
* @return Proxy InetAddress.
|
||||||
|
*/
|
||||||
|
public InetAddress getInetAddress() {
|
||||||
|
return proxyIP;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds given ip to the list of direct addresses. This machine will be
|
||||||
|
* accessed without using proxy.
|
||||||
|
*/
|
||||||
|
public void addDirect(InetAddress ip) {
|
||||||
|
directHosts.add(ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds host to the list of direct addresses. This machine will be accessed
|
||||||
|
* without using proxy.
|
||||||
|
*/
|
||||||
|
public boolean addDirect(String host) {
|
||||||
|
return directHosts.add(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds given range of addresses to the lsit of direct addresses, machines
|
||||||
|
* within this range will be accessed without using proxy.
|
||||||
|
*/
|
||||||
|
public void addDirect(InetAddress from, InetAddress to) {
|
||||||
|
directHosts.add(from, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets given InetRange as the list of direct address, previous list will be
|
||||||
|
* discarded, any changes done previously with addDirect(Inetaddress) will
|
||||||
|
* be lost. The machines in this range will be accessed without using proxy.
|
||||||
|
*
|
||||||
|
* @param ir
|
||||||
|
* InetRange which should be used to look up direct addresses.
|
||||||
|
* @see InetRange
|
||||||
|
*/
|
||||||
|
public void setDirect(InetRange ir) {
|
||||||
|
directHosts = ir;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the list of direct hosts.
|
||||||
|
*
|
||||||
|
* @return Current range of direct address as InetRange object.
|
||||||
|
* @see InetRange
|
||||||
|
*/
|
||||||
|
public InetRange getDirect() {
|
||||||
|
return directHosts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check wether the given host is on the list of direct address.
|
||||||
|
*
|
||||||
|
* @param host
|
||||||
|
* Host name to check.
|
||||||
|
* @return true if the given host is specified as the direct addresses.
|
||||||
|
*/
|
||||||
|
public boolean isDirect(String host) {
|
||||||
|
return directHosts.contains(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check wether the given host is on the list of direct addresses.
|
||||||
|
*
|
||||||
|
* @param host
|
||||||
|
* Host address to check.
|
||||||
|
* @return true if the given host is specified as the direct address.
|
||||||
|
*/
|
||||||
|
public boolean isDirect(InetAddress host) {
|
||||||
|
return directHosts.contains(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the proxy which should be used to connect to given proxy.
|
||||||
|
*
|
||||||
|
* @param chainProxy
|
||||||
|
* Proxy to use to connect to this proxy.
|
||||||
|
*/
|
||||||
|
public void setChainProxy(SocksProxyBase chainProxy) {
|
||||||
|
this.chainProxy = chainProxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get proxy which is used to connect to this proxy.
|
||||||
|
*
|
||||||
|
* @return Proxy which is used to connect to this proxy, or null if proxy is
|
||||||
|
* to be contacted directly.
|
||||||
|
*/
|
||||||
|
public SocksProxyBase getChainProxy() {
|
||||||
|
return chainProxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get string representation of this proxy.
|
||||||
|
*
|
||||||
|
* @returns string in the form:proxyHost:proxyPort \t Version versionNumber
|
||||||
|
*/
|
||||||
|
public String toString() {
|
||||||
|
return ("" + proxyIP.getHostName() + ":" + proxyPort + "\tVersion " + version);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public Static(Class) Methods
|
||||||
|
// ==============================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets SOCKS4 proxy as default.
|
||||||
|
*
|
||||||
|
* @param hostName
|
||||||
|
* Host name on which SOCKS4 server is running.
|
||||||
|
* @param port
|
||||||
|
* Port on which SOCKS4 server is running.
|
||||||
|
* @param user
|
||||||
|
* Username to use for communications with proxy.
|
||||||
|
*/
|
||||||
|
public static void setDefaultProxy(String hostName, int port, String user)
|
||||||
|
throws UnknownHostException {
|
||||||
|
defaultProxy = new Socks4Proxy(hostName, port, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets SOCKS4 proxy as default.
|
||||||
|
*
|
||||||
|
* @param ipAddress
|
||||||
|
* Host address on which SOCKS4 server is running.
|
||||||
|
* @param port
|
||||||
|
* Port on which SOCKS4 server is running.
|
||||||
|
* @param user
|
||||||
|
* Username to use for communications with proxy.
|
||||||
|
*/
|
||||||
|
public static void setDefaultProxy(InetAddress ipAddress, int port,
|
||||||
|
String user) {
|
||||||
|
defaultProxy = new Socks4Proxy(ipAddress, port, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets SOCKS5 proxy as default. Default proxy only supports
|
||||||
|
* no-authentication.
|
||||||
|
*
|
||||||
|
* @param hostName
|
||||||
|
* Host name on which SOCKS5 server is running.
|
||||||
|
* @param port
|
||||||
|
* Port on which SOCKS5 server is running.
|
||||||
|
*/
|
||||||
|
public static void setDefaultProxy(String hostName, int port)
|
||||||
|
throws UnknownHostException {
|
||||||
|
defaultProxy = new Socks5Proxy(hostName, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets SOCKS5 proxy as default. Default proxy only supports
|
||||||
|
* no-authentication.
|
||||||
|
*
|
||||||
|
* @param ipAddress
|
||||||
|
* Host address on which SOCKS5 server is running.
|
||||||
|
* @param port
|
||||||
|
* Port on which SOCKS5 server is running.
|
||||||
|
*/
|
||||||
|
public static void setDefaultProxy(InetAddress ipAddress, int port) {
|
||||||
|
defaultProxy = new Socks5Proxy(ipAddress, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets default proxy.
|
||||||
|
*
|
||||||
|
* @param p
|
||||||
|
* Proxy to use as default proxy.
|
||||||
|
*/
|
||||||
|
public static void setDefaultProxy(SocksProxyBase p) {
|
||||||
|
defaultProxy = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current default proxy.
|
||||||
|
*
|
||||||
|
* @return Current default proxy, or null if none is set.
|
||||||
|
*/
|
||||||
|
public static SocksProxyBase getDefaultProxy() {
|
||||||
|
return defaultProxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses strings in the form: host[:port:user:password], and creates proxy
|
||||||
|
* from information obtained from parsing.
|
||||||
|
* <p>
|
||||||
|
* Defaults: port = 1080.<br>
|
||||||
|
* If user specified but not password, creates Socks4Proxy, if user not
|
||||||
|
* specified creates Socks5Proxy, if both user and password are speciefied
|
||||||
|
* creates Socks5Proxy with user/password authentication.
|
||||||
|
*
|
||||||
|
* @param proxy_entry
|
||||||
|
* String in the form host[:port:user:password]
|
||||||
|
* @return Proxy created from the string, null if entry was somehow
|
||||||
|
* invalid(host unknown for example, or empty string)
|
||||||
|
*/
|
||||||
|
public static SocksProxyBase parseProxy(String proxy_entry) {
|
||||||
|
|
||||||
|
String proxy_host;
|
||||||
|
int proxy_port = 1080;
|
||||||
|
String proxy_user = null;
|
||||||
|
String proxy_password = null;
|
||||||
|
SocksProxyBase proxy;
|
||||||
|
|
||||||
|
final java.util.StringTokenizer st = new java.util.StringTokenizer(
|
||||||
|
proxy_entry, ":");
|
||||||
|
if (st.countTokens() < 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
proxy_host = st.nextToken();
|
||||||
|
if (st.hasMoreTokens()) {
|
||||||
|
try {
|
||||||
|
proxy_port = Integer.parseInt(st.nextToken().trim());
|
||||||
|
} catch (final NumberFormatException nfe) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (st.hasMoreTokens()) {
|
||||||
|
proxy_user = st.nextToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (st.hasMoreTokens()) {
|
||||||
|
proxy_password = st.nextToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (proxy_user == null) {
|
||||||
|
proxy = new Socks5Proxy(proxy_host, proxy_port);
|
||||||
|
} else if (proxy_password == null) {
|
||||||
|
proxy = new Socks4Proxy(proxy_host, proxy_port, proxy_user);
|
||||||
|
} else {
|
||||||
|
proxy = new Socks5Proxy(proxy_host, proxy_port);
|
||||||
|
final UserPasswordAuthentication upa = new UserPasswordAuthentication(
|
||||||
|
proxy_user, proxy_password);
|
||||||
|
|
||||||
|
((Socks5Proxy) proxy).setAuthenticationMethod(
|
||||||
|
UserPasswordAuthentication.METHOD_ID, upa);
|
||||||
|
}
|
||||||
|
} catch (final UnknownHostException uhe) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return proxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Protected Methods
|
||||||
|
// =================
|
||||||
|
|
||||||
|
protected void startSession() throws SocksException {
|
||||||
|
try {
|
||||||
|
if (chainProxy == null) {
|
||||||
|
proxySocket = new Socket(proxyIP, proxyPort);
|
||||||
|
} else if (proxyIP != null) {
|
||||||
|
proxySocket = new SocksSocket(chainProxy, proxyIP, proxyPort);
|
||||||
|
} else {
|
||||||
|
proxySocket = new SocksSocket(chainProxy, proxyHost, proxyPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
in = proxySocket.getInputStream();
|
||||||
|
out = proxySocket.getOutputStream();
|
||||||
|
} catch (final SocksException se) {
|
||||||
|
throw se;
|
||||||
|
} catch (final IOException io_ex) {
|
||||||
|
throw new SocksException(SOCKS_PROXY_IO_ERROR, "" + io_ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a copy of this proxy for use by individual threads.
|
||||||
|
*
|
||||||
|
* @return proxy
|
||||||
|
*/
|
||||||
|
protected abstract SocksProxyBase copy();
|
||||||
|
|
||||||
|
protected abstract ProxyMessage formMessage(int cmd, InetAddress ip,
|
||||||
|
int port);
|
||||||
|
|
||||||
|
protected abstract ProxyMessage formMessage(int cmd, String host, int port)
|
||||||
|
throws UnknownHostException;
|
||||||
|
|
||||||
|
protected abstract ProxyMessage formMessage(InputStream in)
|
||||||
|
throws SocksException, IOException;
|
||||||
|
|
||||||
|
protected ProxyMessage connect(InetAddress ip, int port)
|
||||||
|
throws SocksException {
|
||||||
|
try {
|
||||||
|
startSession();
|
||||||
|
final ProxyMessage request = formMessage(SOCKS_CMD_CONNECT, ip,
|
||||||
|
port);
|
||||||
|
return exchange(request);
|
||||||
|
} catch (final SocksException se) {
|
||||||
|
endSession();
|
||||||
|
throw se;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ProxyMessage connect(String host, int port)
|
||||||
|
throws UnknownHostException, SocksException {
|
||||||
|
try {
|
||||||
|
startSession();
|
||||||
|
final ProxyMessage request = formMessage(SOCKS_CMD_CONNECT, host,
|
||||||
|
port);
|
||||||
|
return exchange(request);
|
||||||
|
} catch (final SocksException se) {
|
||||||
|
endSession();
|
||||||
|
throw se;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ProxyMessage bind(InetAddress ip, int port) throws SocksException {
|
||||||
|
try {
|
||||||
|
startSession();
|
||||||
|
final ProxyMessage request = formMessage(SOCKS_CMD_BIND, ip, port);
|
||||||
|
return exchange(request);
|
||||||
|
} catch (final SocksException se) {
|
||||||
|
endSession();
|
||||||
|
throw se;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ProxyMessage bind(String host, int port)
|
||||||
|
throws UnknownHostException, SocksException {
|
||||||
|
try {
|
||||||
|
startSession();
|
||||||
|
final ProxyMessage request = formMessage(SOCKS_CMD_BIND, host, port);
|
||||||
|
return exchange(request);
|
||||||
|
} catch (final SocksException se) {
|
||||||
|
endSession();
|
||||||
|
throw se;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ProxyMessage accept() throws IOException, SocksException {
|
||||||
|
ProxyMessage msg;
|
||||||
|
try {
|
||||||
|
msg = formMessage(in);
|
||||||
|
} catch (final InterruptedIOException iioe) {
|
||||||
|
throw iioe;
|
||||||
|
} catch (final IOException io_ex) {
|
||||||
|
endSession();
|
||||||
|
throw new SocksException(SOCKS_PROXY_IO_ERROR,
|
||||||
|
"While Trying accept:" + io_ex);
|
||||||
|
}
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ProxyMessage udpAssociate(InetAddress ip, int port)
|
||||||
|
throws SocksException {
|
||||||
|
try {
|
||||||
|
startSession();
|
||||||
|
final ProxyMessage request = formMessage(SOCKS_CMD_UDP_ASSOCIATE,
|
||||||
|
ip, port);
|
||||||
|
if (request != null) {
|
||||||
|
return exchange(request);
|
||||||
|
}
|
||||||
|
} catch (final SocksException se) {
|
||||||
|
endSession();
|
||||||
|
throw se;
|
||||||
|
}
|
||||||
|
// Only get here if request was null
|
||||||
|
endSession();
|
||||||
|
throw new SocksException(SOCKS_METHOD_NOTSUPPORTED,
|
||||||
|
"This version of proxy does not support UDP associate, use version 5");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ProxyMessage udpAssociate(String host, int port)
|
||||||
|
throws UnknownHostException, SocksException {
|
||||||
|
try {
|
||||||
|
startSession();
|
||||||
|
final ProxyMessage request = formMessage(SOCKS_CMD_UDP_ASSOCIATE,
|
||||||
|
host, port);
|
||||||
|
if (request != null) {
|
||||||
|
return exchange(request);
|
||||||
|
}
|
||||||
|
} catch (final SocksException se) {
|
||||||
|
endSession();
|
||||||
|
throw se;
|
||||||
|
}
|
||||||
|
// Only get here if request was null
|
||||||
|
endSession();
|
||||||
|
throw new SocksException(SOCKS_METHOD_NOTSUPPORTED,
|
||||||
|
"This version of proxy does not support UDP associate, use version 5");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void endSession() {
|
||||||
|
try {
|
||||||
|
if (proxySocket != null) {
|
||||||
|
proxySocket.close();
|
||||||
|
}
|
||||||
|
proxySocket = null;
|
||||||
|
} catch (final IOException io_ex) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*Sends the request to SOCKS server
|
||||||
|
*/
|
||||||
|
protected void sendMsg(ProxyMessage msg) throws SocksException, IOException {
|
||||||
|
msg.write(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the reply from the SOCKS server
|
||||||
|
*/
|
||||||
|
protected ProxyMessage readMsg() throws SocksException, IOException {
|
||||||
|
return formMessage(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*Sends the request reads reply and returns it throws exception if
|
||||||
|
* something wrong with IO or the reply code is not zero
|
||||||
|
*/
|
||||||
|
protected ProxyMessage exchange(ProxyMessage request) throws SocksException {
|
||||||
|
ProxyMessage reply;
|
||||||
|
try {
|
||||||
|
request.write(out);
|
||||||
|
reply = formMessage(in);
|
||||||
|
} catch (final SocksException s_ex) {
|
||||||
|
throw s_ex;
|
||||||
|
} catch (final IOException ioe) {
|
||||||
|
throw (new SocksException(SOCKS_PROXY_IO_ERROR, "" + ioe));
|
||||||
|
}
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private methods
|
||||||
|
// ===============
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
|
||||||
|
public static final int SOCKS_SUCCESS = 0;
|
||||||
|
public static final int SOCKS_FAILURE = 1;
|
||||||
|
public static final int SOCKS_BADCONNECT = 2;
|
||||||
|
public static final int SOCKS_BADNETWORK = 3;
|
||||||
|
public static final int SOCKS_HOST_UNREACHABLE = 4;
|
||||||
|
public static final int SOCKS_CONNECTION_REFUSED = 5;
|
||||||
|
public static final int SOCKS_TTL_EXPIRE = 6;
|
||||||
|
public static final int SOCKS_CMD_NOT_SUPPORTED = 7;
|
||||||
|
public static final int SOCKS_ADDR_NOT_SUPPORTED = 8;
|
||||||
|
|
||||||
|
public static final int SOCKS_NO_PROXY = 1 << 16;
|
||||||
|
public static final int SOCKS_PROXY_NO_CONNECT = 2 << 16;
|
||||||
|
public static final int SOCKS_PROXY_IO_ERROR = 3 << 16;
|
||||||
|
public static final int SOCKS_AUTH_NOT_SUPPORTED = 4 << 16;
|
||||||
|
public static final int SOCKS_AUTH_FAILURE = 5 << 16;
|
||||||
|
public static final int SOCKS_JUST_ERROR = 6 << 16;
|
||||||
|
|
||||||
|
public static final int SOCKS_DIRECT_FAILED = 7 << 16;
|
||||||
|
public static final int SOCKS_METHOD_NOTSUPPORTED = 8 << 16;
|
||||||
|
|
||||||
|
static final int SOCKS_CMD_CONNECT = 0x1;
|
||||||
|
static final int SOCKS_CMD_BIND = 0x2;
|
||||||
|
static final int SOCKS_CMD_UDP_ASSOCIATE = 0x3;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,238 @@
|
||||||
|
package com.runjva.sourceforge.jsocks.protocol;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.ServerSocket;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.net.SocketException;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SocksServerSocket allows to accept connections from one particular host
|
||||||
|
* through the SOCKS4 or SOCKS5 proxy.
|
||||||
|
*/
|
||||||
|
public class SocksServerSocket extends ServerSocket {
|
||||||
|
// Data members
|
||||||
|
protected SocksProxyBase proxy;
|
||||||
|
protected String localHost;
|
||||||
|
protected InetAddress localIP;
|
||||||
|
protected int localPort;
|
||||||
|
|
||||||
|
boolean doing_direct = false;
|
||||||
|
InetAddress remoteAddr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates ServerSocket capable of accepting one connection through the
|
||||||
|
* firewall, uses default Proxy.
|
||||||
|
*
|
||||||
|
* @param host
|
||||||
|
* Host from which the connection should be recieved.
|
||||||
|
*@param port
|
||||||
|
* Port number of the primary connection.
|
||||||
|
*/
|
||||||
|
public SocksServerSocket(String host, int port) throws SocksException,
|
||||||
|
UnknownHostException, IOException {
|
||||||
|
this(SocksProxyBase.defaultProxy, host, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*Creates ServerSocket capable of accepting one connection through the
|
||||||
|
* firewall, uses given proxy.
|
||||||
|
*
|
||||||
|
* @param p
|
||||||
|
* Proxy object to use.
|
||||||
|
*@param host
|
||||||
|
* Host from which the connection should be recieved.
|
||||||
|
*@param port
|
||||||
|
* Port number of the primary connection.
|
||||||
|
*/
|
||||||
|
public SocksServerSocket(SocksProxyBase p, String host, int port)
|
||||||
|
throws SocksException, UnknownHostException, IOException {
|
||||||
|
|
||||||
|
super(0);
|
||||||
|
if (p == null) {
|
||||||
|
throw new SocksException(SocksProxyBase.SOCKS_NO_PROXY);
|
||||||
|
}
|
||||||
|
// proxy=p;
|
||||||
|
proxy = p.copy();
|
||||||
|
if (proxy.isDirect(host)) {
|
||||||
|
remoteAddr = InetAddress.getByName(host);
|
||||||
|
proxy = null;
|
||||||
|
doDirect();
|
||||||
|
} else {
|
||||||
|
processReply(proxy.bind(host, port));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates ServerSocket capable of accepting one connection through the
|
||||||
|
* firewall, uses default Proxy.
|
||||||
|
*
|
||||||
|
* @param ip
|
||||||
|
* Host from which the connection should be recieved.
|
||||||
|
*@param port
|
||||||
|
* Port number of the primary connection.
|
||||||
|
*/
|
||||||
|
public SocksServerSocket(InetAddress ip, int port) throws SocksException,
|
||||||
|
IOException {
|
||||||
|
this(SocksProxyBase.defaultProxy, ip, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*Creates ServerSocket capable of accepting one connection through the
|
||||||
|
* firewall, uses given proxy.
|
||||||
|
*
|
||||||
|
* @param p
|
||||||
|
* Proxy object to use.
|
||||||
|
*@param ip
|
||||||
|
* Host from which the connection should be recieved.
|
||||||
|
*@param port
|
||||||
|
* Port number of the primary connection.
|
||||||
|
*/
|
||||||
|
public SocksServerSocket(SocksProxyBase p, InetAddress ip, int port)
|
||||||
|
throws SocksException, IOException {
|
||||||
|
super(0);
|
||||||
|
|
||||||
|
if (p == null) {
|
||||||
|
throw new SocksException(SocksProxyBase.SOCKS_NO_PROXY);
|
||||||
|
}
|
||||||
|
this.proxy = p.copy();
|
||||||
|
|
||||||
|
if (proxy.isDirect(ip)) {
|
||||||
|
remoteAddr = ip;
|
||||||
|
doDirect();
|
||||||
|
} else {
|
||||||
|
processReply(proxy.bind(ip, port));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accepts the incoming connection.
|
||||||
|
*/
|
||||||
|
public Socket accept() throws IOException {
|
||||||
|
Socket s;
|
||||||
|
|
||||||
|
if (!doing_direct) {
|
||||||
|
if (proxy == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final ProxyMessage msg = proxy.accept();
|
||||||
|
s = msg.ip == null ? new SocksSocket(msg.host, msg.port, proxy)
|
||||||
|
: new SocksSocket(msg.ip, msg.port, proxy);
|
||||||
|
// Set timeout back to 0
|
||||||
|
proxy.proxySocket.setSoTimeout(0);
|
||||||
|
if (ProxyServer.vpnService != null)
|
||||||
|
ProxyServer.vpnService.protect(proxy.proxySocket);
|
||||||
|
|
||||||
|
} else { // Direct Connection
|
||||||
|
|
||||||
|
// Mimic the proxy behaviour,
|
||||||
|
// only accept connections from the speciefed host.
|
||||||
|
while (true) {
|
||||||
|
s = super.accept();
|
||||||
|
if (s.getInetAddress().equals(remoteAddr)) {
|
||||||
|
// got the connection from the right host
|
||||||
|
// Close listenning socket.
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
s.close(); // Drop all connections from other hosts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
proxy = null;
|
||||||
|
// Return accepted socket
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the connection to proxy if socket have not been accepted, if the
|
||||||
|
* direct connection is used, closes direct ServerSocket. If the client
|
||||||
|
* socket have been allready accepted, does nothing.
|
||||||
|
*/
|
||||||
|
public void close() throws IOException {
|
||||||
|
super.close();
|
||||||
|
if (proxy != null) {
|
||||||
|
proxy.endSession();
|
||||||
|
}
|
||||||
|
proxy = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the name of the host proxy is using to listen for incoming
|
||||||
|
* connection.
|
||||||
|
* <P>
|
||||||
|
* Usefull when address is returned by proxy as the hostname.
|
||||||
|
*
|
||||||
|
* @return the hostname of the address proxy is using to listen for incoming
|
||||||
|
* connection.
|
||||||
|
*/
|
||||||
|
public String getHost() {
|
||||||
|
return localHost;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get address assigned by proxy to listen for incomming connections, or the
|
||||||
|
* local machine address if doing direct connection.
|
||||||
|
*/
|
||||||
|
public InetAddress getInetAddress() {
|
||||||
|
if (localIP == null) {
|
||||||
|
try {
|
||||||
|
localIP = InetAddress.getByName(localHost);
|
||||||
|
} catch (final UnknownHostException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return localIP;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get port assigned by proxy to listen for incoming connections, or the
|
||||||
|
* port chosen by local system, if accepting directly.
|
||||||
|
*/
|
||||||
|
public int getLocalPort() {
|
||||||
|
return localPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Timeout.
|
||||||
|
*
|
||||||
|
* @param timeout
|
||||||
|
* Amount of time in milliseconds, accept should wait for
|
||||||
|
* incoming connection before failing with exception. Zero
|
||||||
|
* timeout implies infinity.
|
||||||
|
*/
|
||||||
|
public void setSoTimeout(int timeout) throws SocketException {
|
||||||
|
super.setSoTimeout(timeout);
|
||||||
|
if (!doing_direct) {
|
||||||
|
proxy.proxySocket.setSoTimeout(timeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private Methods
|
||||||
|
// ////////////////
|
||||||
|
|
||||||
|
private void processReply(ProxyMessage reply) throws SocksException {
|
||||||
|
localPort = reply.port;
|
||||||
|
/*
|
||||||
|
* If the server have assigned same host as it was contacted on it might
|
||||||
|
* return an address of all zeros
|
||||||
|
*/
|
||||||
|
if (reply.host.equals("0.0.0.0")) {
|
||||||
|
localIP = proxy.proxyIP;
|
||||||
|
localHost = localIP.getHostName();
|
||||||
|
} else {
|
||||||
|
localHost = reply.host;
|
||||||
|
localIP = reply.ip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doDirect() {
|
||||||
|
doing_direct = true;
|
||||||
|
localPort = super.getLocalPort();
|
||||||
|
localIP = super.getInetAddress();
|
||||||
|
localHost = localIP.getHostName();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,389 @@
|
||||||
|
package com.runjva.sourceforge.jsocks.protocol;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.net.SocketException;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SocksSocket tryies to look very similar to normal Socket, while allowing
|
||||||
|
* connections through the SOCKS4 or 5 proxy. To use this class you will have to
|
||||||
|
* identify proxy you need to use, Proxy class allows you to set default proxy,
|
||||||
|
* which will be used by all Socks aware sockets. You can also create either
|
||||||
|
* Socks4Proxy or Socks5Proxy, and use them by passing to the appropriate
|
||||||
|
* constructors.
|
||||||
|
* <P>
|
||||||
|
* Using Socks package can be as easy as that:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* <tt>
|
||||||
|
*
|
||||||
|
* import Socks.*;
|
||||||
|
* ....
|
||||||
|
*
|
||||||
|
* try{
|
||||||
|
* //Specify SOCKS5 proxy
|
||||||
|
* Proxy.setDefaultProxy("socks-proxy",1080);
|
||||||
|
*
|
||||||
|
* //OR you still use SOCKS4
|
||||||
|
* //Code below uses SOCKS4 proxy
|
||||||
|
* //Proxy.setDefaultProxy("socks-proxy",1080,userName);
|
||||||
|
*
|
||||||
|
* Socket s = SocksSocket("some.host.of.mine",13);
|
||||||
|
* readTimeFromSock(s);
|
||||||
|
* }catch(SocksException sock_ex){
|
||||||
|
* //Usually it will turn in more or less meaningfull message
|
||||||
|
* System.err.println("SocksException:"+sock_ex);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* </tt>
|
||||||
|
* </pre>
|
||||||
|
*<P>
|
||||||
|
* However if the need exist for more control, like resolving addresses
|
||||||
|
* remotely, or using some non-trivial authentication schemes, it can be done.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class SocksSocket extends Socket {
|
||||||
|
// Data members
|
||||||
|
protected SocksProxyBase proxy;
|
||||||
|
protected String localHost, remoteHost;
|
||||||
|
protected InetAddress localIP, remoteIP;
|
||||||
|
protected int localPort, remotePort;
|
||||||
|
|
||||||
|
private Socket directSock = null;
|
||||||
|
private Logger log = LoggerFactory.getLogger(SocksSocket.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tryies to connect to given host and port using default proxy. If no
|
||||||
|
* default proxy speciefied it throws SocksException with error code
|
||||||
|
* SOCKS_NO_PROXY.
|
||||||
|
*
|
||||||
|
* @param host
|
||||||
|
* Machine to connect to.
|
||||||
|
* @param port
|
||||||
|
* Port to which to connect.
|
||||||
|
* @see SocksSocket#SocksSocket(SocksProxyBase,String,int)
|
||||||
|
* @see Socks5Proxy#resolveAddrLocally
|
||||||
|
*/
|
||||||
|
public SocksSocket(String host, int port) throws SocksException,
|
||||||
|
UnknownHostException {
|
||||||
|
this(SocksProxyBase.defaultProxy, host, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connects to host port using given proxy server.
|
||||||
|
*
|
||||||
|
* @param p
|
||||||
|
* Proxy to use.
|
||||||
|
* @param host
|
||||||
|
* Machine to connect to.
|
||||||
|
* @param port
|
||||||
|
* Port to which to connect.
|
||||||
|
* @throws UnknownHostException
|
||||||
|
* If one of the following happens:
|
||||||
|
* <ol>
|
||||||
|
*
|
||||||
|
* <li>Proxy settings say that address should be resolved
|
||||||
|
* locally, but this fails.
|
||||||
|
* <li>Proxy settings say that the host should be contacted
|
||||||
|
* directly but host name can't be resolved.
|
||||||
|
* </ol>
|
||||||
|
* @throws SocksException
|
||||||
|
* If one of the following happens:
|
||||||
|
* <ul>
|
||||||
|
* <li>Proxy is is null.
|
||||||
|
* <li>Proxy settings say that the host should be contacted
|
||||||
|
* directly but this fails.
|
||||||
|
* <li>Socks Server can't be contacted.
|
||||||
|
* <li>Authentication fails.
|
||||||
|
* <li>Connection is not allowed by the SOCKS proxy.
|
||||||
|
* <li>SOCKS proxy can't establish the connection.
|
||||||
|
* <li>Any IO error occured.
|
||||||
|
* <li>Any protocol error occured.
|
||||||
|
* </ul>
|
||||||
|
* @throws IOexception
|
||||||
|
* if anything is wrong with I/O.
|
||||||
|
* @see Socks5Proxy#resolveAddrLocally
|
||||||
|
*/
|
||||||
|
public SocksSocket(SocksProxyBase p, String host, int port)
|
||||||
|
throws SocksException, UnknownHostException {
|
||||||
|
|
||||||
|
if (p == null) {
|
||||||
|
throw new SocksException(SocksProxyBase.SOCKS_NO_PROXY);
|
||||||
|
}
|
||||||
|
// proxy=p;
|
||||||
|
proxy = p.copy();
|
||||||
|
remoteHost = host;
|
||||||
|
remotePort = port;
|
||||||
|
if (proxy.isDirect(host)) {
|
||||||
|
remoteIP = InetAddress.getByName(host);
|
||||||
|
doDirect();
|
||||||
|
} else {
|
||||||
|
processReply(proxy.connect(host, port));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tryies to connect to given ip and port using default proxy. If no default
|
||||||
|
* proxy speciefied it throws SocksException with error code SOCKS_NO_PROXY.
|
||||||
|
*
|
||||||
|
* @param ip
|
||||||
|
* Machine to connect to.
|
||||||
|
* @param port
|
||||||
|
* Port to which to connect.
|
||||||
|
* @see SocksSocket#SocksSocket(SocksProxyBase,String,int)
|
||||||
|
*/
|
||||||
|
public SocksSocket(InetAddress ip, int port) throws SocksException {
|
||||||
|
this(SocksProxyBase.defaultProxy, ip, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connects to given ip and port using given Proxy server.
|
||||||
|
*
|
||||||
|
* @param p
|
||||||
|
* Proxy to use.
|
||||||
|
* @param ip
|
||||||
|
* Machine to connect to.
|
||||||
|
* @param port
|
||||||
|
* Port to which to connect.
|
||||||
|
*/
|
||||||
|
public SocksSocket(SocksProxyBase p, InetAddress ip, int port)
|
||||||
|
throws SocksException {
|
||||||
|
if (p == null) {
|
||||||
|
throw new SocksException(SocksProxyBase.SOCKS_NO_PROXY);
|
||||||
|
}
|
||||||
|
this.proxy = p.copy();
|
||||||
|
this.remoteIP = ip;
|
||||||
|
this.remotePort = port;
|
||||||
|
this.remoteHost = ip.getHostName();
|
||||||
|
if (proxy.isDirect(remoteIP)) {
|
||||||
|
doDirect();
|
||||||
|
} else {
|
||||||
|
processReply(proxy.connect(ip, port));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* These 2 constructors are used by the SocksServerSocket. This socket
|
||||||
|
* simply overrides remoteHost, remotePort
|
||||||
|
*/
|
||||||
|
protected SocksSocket(String host, int port, SocksProxyBase proxy) {
|
||||||
|
this.remotePort = port;
|
||||||
|
this.proxy = proxy;
|
||||||
|
this.localIP = proxy.proxySocket.getLocalAddress();
|
||||||
|
this.localPort = proxy.proxySocket.getLocalPort();
|
||||||
|
this.remoteHost = host;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected SocksSocket(InetAddress ip, int port, SocksProxyBase proxy) {
|
||||||
|
remoteIP = ip;
|
||||||
|
remotePort = port;
|
||||||
|
this.proxy = proxy;
|
||||||
|
this.localIP = proxy.proxySocket.getLocalAddress();
|
||||||
|
this.localPort = proxy.proxySocket.getLocalPort();
|
||||||
|
remoteHost = remoteIP.getHostName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as Socket
|
||||||
|
*/
|
||||||
|
public void close() throws IOException {
|
||||||
|
if (proxy != null) {
|
||||||
|
proxy.endSession();
|
||||||
|
}
|
||||||
|
proxy = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as Socket
|
||||||
|
*/
|
||||||
|
public InputStream getInputStream() {
|
||||||
|
return proxy.in;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as Socket
|
||||||
|
*/
|
||||||
|
public OutputStream getOutputStream() {
|
||||||
|
return proxy.out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as Socket
|
||||||
|
*/
|
||||||
|
public int getPort() {
|
||||||
|
return remotePort;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns remote host name, it is usefull in cases when addresses are
|
||||||
|
* resolved by proxy, and we can't create InetAddress object.
|
||||||
|
*
|
||||||
|
* @return The name of the host this socket is connected to.
|
||||||
|
*/
|
||||||
|
public String getHost() {
|
||||||
|
return remoteHost;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get remote host as InetAddress object, might return null if addresses are
|
||||||
|
* resolved by proxy, and it is not possible to resolve it locally
|
||||||
|
*
|
||||||
|
* @return Ip address of the host this socket is connected to, or null if
|
||||||
|
* address was returned by the proxy as DOMAINNAME and can't be
|
||||||
|
* resolved locally.
|
||||||
|
*/
|
||||||
|
public InetAddress getInetAddress() {
|
||||||
|
if (remoteIP == null) {
|
||||||
|
try {
|
||||||
|
remoteIP = InetAddress.getByName(remoteHost);
|
||||||
|
} catch (final UnknownHostException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return remoteIP;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the port assigned by the proxy for the socket, not the port on locall
|
||||||
|
* machine as in Socket.
|
||||||
|
*
|
||||||
|
* @return Port of the socket used on the proxy server.
|
||||||
|
*/
|
||||||
|
public int getLocalPort() {
|
||||||
|
return localPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get address assigned by proxy to make a remote connection, it might be
|
||||||
|
* different from the host specified for the proxy. Can return null if socks
|
||||||
|
* server returned this address as hostname and it can't be resolved
|
||||||
|
* locally, use getLocalHost() then.
|
||||||
|
*
|
||||||
|
* @return Address proxy is using to make a connection.
|
||||||
|
*/
|
||||||
|
public InetAddress getLocalAddress() {
|
||||||
|
if (localIP == null) {
|
||||||
|
try {
|
||||||
|
localIP = InetAddress.getByName(localHost);
|
||||||
|
} catch (final UnknownHostException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return localIP;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get name of the host, proxy has assigned to make a remote connection for
|
||||||
|
* this socket. This method is usefull when proxy have returned address as
|
||||||
|
* hostname, and we can't resolve it on this machine.
|
||||||
|
*
|
||||||
|
* @return The name of the host proxy is using to make a connection.
|
||||||
|
*/
|
||||||
|
public String getLocalHost() {
|
||||||
|
return localHost;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as socket.
|
||||||
|
*/
|
||||||
|
public void setSoLinger(boolean on, int val) throws SocketException {
|
||||||
|
proxy.proxySocket.setSoLinger(on, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as socket.
|
||||||
|
*/
|
||||||
|
public int getSoLinger(int timeout) throws SocketException {
|
||||||
|
return proxy.proxySocket.getSoLinger();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as socket.
|
||||||
|
*/
|
||||||
|
public void setSoTimeout(int timeout) throws SocketException {
|
||||||
|
proxy.proxySocket.setSoTimeout(timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as socket.
|
||||||
|
*/
|
||||||
|
public int getSoTimeout(int timeout) throws SocketException {
|
||||||
|
return proxy.proxySocket.getSoTimeout();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as socket.
|
||||||
|
*/
|
||||||
|
public void setTcpNoDelay(boolean on) throws SocketException {
|
||||||
|
proxy.proxySocket.setTcpNoDelay(on);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as socket.
|
||||||
|
*/
|
||||||
|
public boolean getTcpNoDelay() throws SocketException {
|
||||||
|
return proxy.proxySocket.getTcpNoDelay();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get string representation of the socket.
|
||||||
|
*/
|
||||||
|
public String toString() {
|
||||||
|
if (directSock != null) {
|
||||||
|
return "Direct connection:" + directSock;
|
||||||
|
}
|
||||||
|
StringBuffer sb = new StringBuffer();
|
||||||
|
sb.append("Proxy:");
|
||||||
|
sb.append(proxy);
|
||||||
|
sb.append(";");
|
||||||
|
sb.append("addr:");
|
||||||
|
sb.append(remoteHost);
|
||||||
|
sb.append(",port:");
|
||||||
|
sb.append(remotePort);
|
||||||
|
sb.append(",localport:");
|
||||||
|
sb.append(localPort);
|
||||||
|
return sb.toString();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private Methods
|
||||||
|
// ////////////////
|
||||||
|
|
||||||
|
private void processReply(ProxyMessage reply) throws SocksException {
|
||||||
|
localPort = reply.port;
|
||||||
|
/*
|
||||||
|
* If the server have assigned same host as it was contacted on it might
|
||||||
|
* return an address of all zeros
|
||||||
|
*/
|
||||||
|
if (reply.host.equals("0.0.0.0")) {
|
||||||
|
localIP = proxy.proxyIP;
|
||||||
|
localHost = localIP.getHostName();
|
||||||
|
} else {
|
||||||
|
localHost = reply.host;
|
||||||
|
localIP = reply.ip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doDirect() throws SocksException {
|
||||||
|
try {
|
||||||
|
log.debug("IP: {}_{}", remoteIP, remotePort);
|
||||||
|
directSock = new Socket(remoteIP, remotePort);
|
||||||
|
proxy.out = directSock.getOutputStream();
|
||||||
|
proxy.in = directSock.getInputStream();
|
||||||
|
proxy.proxySocket = directSock;
|
||||||
|
localIP = directSock.getLocalAddress();
|
||||||
|
localPort = directSock.getLocalPort();
|
||||||
|
} catch (final IOException io_ex) {
|
||||||
|
final int errCode = SocksProxyBase.SOCKS_DIRECT_FAILED;
|
||||||
|
throw new SocksException(errCode, "Direct connect failed:", io_ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package com.runjva.sourceforge.jsocks.protocol;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface provides for datagram encapsulation for SOCKSv5 protocol.
|
||||||
|
* <p>
|
||||||
|
* SOCKSv5 allows for datagrams to be encapsulated for purposes of integrity
|
||||||
|
* and/or authenticity. How it should be done is aggreed during the
|
||||||
|
* authentication stage, and is authentication dependent. This interface is
|
||||||
|
* provided to allow this encapsulation.
|
||||||
|
*
|
||||||
|
* @see Authentication
|
||||||
|
*/
|
||||||
|
public interface UDPEncapsulation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method should provide any authentication depended transformation on
|
||||||
|
* datagrams being send from/to the client.
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* Datagram data (including any SOCKS related bytes), to be
|
||||||
|
* encapsulated/decapsulated.
|
||||||
|
* @param out
|
||||||
|
* Wether the data is being send out. If true method should
|
||||||
|
* encapsulate/encrypt data, otherwise it should decapsulate/
|
||||||
|
* decrypt data.
|
||||||
|
* @throw IOException if for some reason data can be transformed correctly.
|
||||||
|
* @return Should return byte array containing data after transformation. It
|
||||||
|
* is possible to return same array as input, if transformation only
|
||||||
|
* involves bit mangling, and no additional data is being added or
|
||||||
|
* removed.
|
||||||
|
*/
|
||||||
|
byte[] udpEncapsulate(byte[] data, boolean out) throws java.io.IOException;
|
||||||
|
}
|
|
@ -0,0 +1,231 @@
|
||||||
|
package com.runjva.sourceforge.jsocks.protocol;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InterruptedIOException;
|
||||||
|
import java.net.DatagramPacket;
|
||||||
|
import java.net.DatagramSocket;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.runjva.sourceforge.jsocks.server.ServerAuthenticator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UDP Relay server, used by ProxyServer to perform udp forwarding.
|
||||||
|
*/
|
||||||
|
class UDPRelayServer implements Runnable {
|
||||||
|
|
||||||
|
DatagramSocket client_sock;
|
||||||
|
DatagramSocket remote_sock;
|
||||||
|
|
||||||
|
Socket controlConnection;
|
||||||
|
|
||||||
|
int relayPort;
|
||||||
|
InetAddress relayIP;
|
||||||
|
|
||||||
|
Thread pipe_thread1, pipe_thread2;
|
||||||
|
Thread master_thread;
|
||||||
|
|
||||||
|
ServerAuthenticator auth;
|
||||||
|
|
||||||
|
long lastReadTime;
|
||||||
|
|
||||||
|
static Logger log = LoggerFactory.getLogger(UDPRelayServer.class);
|
||||||
|
static SocksProxyBase proxy = null;
|
||||||
|
static int datagramSize = 0xFFFF;// 64K, a bit more than max udp size
|
||||||
|
static int iddleTimeout = 180000;// 3 minutes
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs UDP relay server to communicate with client on given ip and
|
||||||
|
* port.
|
||||||
|
*
|
||||||
|
* @param clientIP
|
||||||
|
* Address of the client from whom datagrams will be recieved and
|
||||||
|
* to whom they will be forwarded.
|
||||||
|
* @param clientPort
|
||||||
|
* Clients port.
|
||||||
|
* @param master_thread
|
||||||
|
* Thread which will be interrupted, when UDP relay server
|
||||||
|
* stoppes for some reason.
|
||||||
|
* @param controlConnection
|
||||||
|
* Socket which will be closed, before interrupting the master
|
||||||
|
* thread, it is introduced due to a bug in windows JVM which
|
||||||
|
* does not throw InterruptedIOException in threads which block
|
||||||
|
* in I/O operation.
|
||||||
|
*/
|
||||||
|
public UDPRelayServer(InetAddress clientIP, int clientPort,
|
||||||
|
Thread master_thread, Socket controlConnection,
|
||||||
|
ServerAuthenticator auth) throws IOException {
|
||||||
|
|
||||||
|
this.master_thread = master_thread;
|
||||||
|
this.controlConnection = controlConnection;
|
||||||
|
this.auth = auth;
|
||||||
|
|
||||||
|
client_sock = new Socks5DatagramSocket(true,
|
||||||
|
auth.getUdpEncapsulation(), clientIP, clientPort);
|
||||||
|
|
||||||
|
relayPort = client_sock.getLocalPort();
|
||||||
|
relayIP = client_sock.getLocalAddress();
|
||||||
|
|
||||||
|
if (relayIP.getHostAddress().equals("0.0.0.0")) {
|
||||||
|
relayIP = InetAddress.getLocalHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (proxy == null) {
|
||||||
|
remote_sock = new DatagramSocket();
|
||||||
|
} else {
|
||||||
|
remote_sock = new Socks5DatagramSocket(proxy, 0, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public methods
|
||||||
|
// ///////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the timeout for UDPRelay server.<br>
|
||||||
|
* Zero timeout implies infinity.<br>
|
||||||
|
* Default timeout is 3 minutes.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static public void setTimeout(int timeout) {
|
||||||
|
iddleTimeout = timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the size of the datagrams used in the UDPRelayServer.<br>
|
||||||
|
* Default size is 64K, a bit more than maximum possible size of the
|
||||||
|
* datagram.
|
||||||
|
*/
|
||||||
|
static public void setDatagramSize(int size) {
|
||||||
|
datagramSize = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Port to which client should send datagram for association.
|
||||||
|
*/
|
||||||
|
public int getRelayPort() {
|
||||||
|
return relayPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IP address to which client should send datagrams for association.
|
||||||
|
*/
|
||||||
|
public InetAddress getRelayIP() {
|
||||||
|
return relayIP;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts udp relay server. Spawns two threads of execution and returns.
|
||||||
|
*/
|
||||||
|
public void start() throws IOException {
|
||||||
|
remote_sock.setSoTimeout(iddleTimeout);
|
||||||
|
client_sock.setSoTimeout(iddleTimeout);
|
||||||
|
|
||||||
|
log.info("Starting UDP relay server on {}:{}", relayIP, relayPort);
|
||||||
|
log.info("Remote socket {}:{}", remote_sock.getLocalAddress(),
|
||||||
|
remote_sock.getLocalPort());
|
||||||
|
|
||||||
|
pipe_thread1 = new Thread(this, "pipe1");
|
||||||
|
pipe_thread2 = new Thread(this, "pipe2");
|
||||||
|
|
||||||
|
lastReadTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
pipe_thread1.start();
|
||||||
|
pipe_thread2.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops Relay server.
|
||||||
|
* <p>
|
||||||
|
* Does not close control connection, does not interrupt master_thread.
|
||||||
|
*/
|
||||||
|
public synchronized void stop() {
|
||||||
|
master_thread = null;
|
||||||
|
controlConnection = null;
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Runnable interface
|
||||||
|
// //////////////////
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
if (Thread.currentThread().getName().equals("pipe1")) {
|
||||||
|
pipe(remote_sock, client_sock, false);
|
||||||
|
} else {
|
||||||
|
pipe(client_sock, remote_sock, true);
|
||||||
|
}
|
||||||
|
} catch (final IOException ioe) {
|
||||||
|
} finally {
|
||||||
|
abort();
|
||||||
|
log.info("UDP Pipe thread " + Thread.currentThread().getName()
|
||||||
|
+ " stopped.");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private methods
|
||||||
|
// ///////////////
|
||||||
|
private synchronized void abort() {
|
||||||
|
if (pipe_thread1 == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("Aborting UDP Relay Server");
|
||||||
|
|
||||||
|
remote_sock.close();
|
||||||
|
client_sock.close();
|
||||||
|
|
||||||
|
if (controlConnection != null) {
|
||||||
|
try {
|
||||||
|
controlConnection.close();
|
||||||
|
} catch (final IOException ioe) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (master_thread != null) {
|
||||||
|
master_thread.interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
pipe_thread1.interrupt();
|
||||||
|
pipe_thread2.interrupt();
|
||||||
|
|
||||||
|
pipe_thread1 = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void pipe(DatagramSocket from, DatagramSocket to, boolean out)
|
||||||
|
throws IOException {
|
||||||
|
final byte[] data = new byte[datagramSize];
|
||||||
|
final DatagramPacket dp = new DatagramPacket(data, data.length);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
from.receive(dp);
|
||||||
|
lastReadTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
if (auth.checkRequest(dp, out)) {
|
||||||
|
to.send(dp);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (final UnknownHostException uhe) {
|
||||||
|
log.info("Dropping datagram for unknown host");
|
||||||
|
} catch (final InterruptedIOException iioe) {
|
||||||
|
// log("Interrupted: "+iioe);
|
||||||
|
// If we were interrupted by other thread.
|
||||||
|
if (iddleTimeout == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If last datagram was received, long time ago, return.
|
||||||
|
final long timeSinceRead = System.currentTimeMillis()
|
||||||
|
- lastReadTime;
|
||||||
|
if (timeSinceRead >= iddleTimeout - 100) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dp.setLength(data.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
package com.runjva.sourceforge.jsocks.protocol;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SOCKS5 User Password authentication scheme.
|
||||||
|
*/
|
||||||
|
public class UserPasswordAuthentication implements Authentication {
|
||||||
|
|
||||||
|
/** SOCKS ID for User/Password authentication method */
|
||||||
|
public final static int METHOD_ID = 2;
|
||||||
|
|
||||||
|
String userName, password;
|
||||||
|
byte[] request;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an instance of UserPasswordAuthentication.
|
||||||
|
*
|
||||||
|
* @param userName
|
||||||
|
* User Name to send to SOCKS server.
|
||||||
|
* @param password
|
||||||
|
* Password to send to SOCKS server.
|
||||||
|
*/
|
||||||
|
public UserPasswordAuthentication(String userName, String password) {
|
||||||
|
this.userName = userName;
|
||||||
|
this.password = password;
|
||||||
|
formRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the user name.
|
||||||
|
*
|
||||||
|
* @return User name.
|
||||||
|
*/
|
||||||
|
public String getUser() {
|
||||||
|
return userName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get password
|
||||||
|
*
|
||||||
|
* @return Password
|
||||||
|
*/
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does User/Password authentication as defined in rfc1929.
|
||||||
|
*
|
||||||
|
* @return An array containnig in, out streams, or null if authentication
|
||||||
|
* fails.
|
||||||
|
*/
|
||||||
|
public Object[] doSocksAuthentication(int methodId,
|
||||||
|
java.net.Socket proxySocket) throws java.io.IOException {
|
||||||
|
|
||||||
|
if (methodId != METHOD_ID) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final java.io.InputStream in = proxySocket.getInputStream();
|
||||||
|
final java.io.OutputStream out = proxySocket.getOutputStream();
|
||||||
|
|
||||||
|
out.write(request);
|
||||||
|
final int version = in.read();
|
||||||
|
if (version < 0) {
|
||||||
|
return null; // Server closed connection
|
||||||
|
}
|
||||||
|
final int status = in.read();
|
||||||
|
if (status != 0) {
|
||||||
|
return null; // Server closed connection, or auth failed.
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Object[] { in, out };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private methods
|
||||||
|
// ////////////////
|
||||||
|
|
||||||
|
/** Convert UserName password in to binary form, ready to be send to server */
|
||||||
|
private void formRequest() {
|
||||||
|
final byte[] user_bytes = userName.getBytes();
|
||||||
|
final byte[] password_bytes = password.getBytes();
|
||||||
|
|
||||||
|
request = new byte[3 + user_bytes.length + password_bytes.length];
|
||||||
|
request[0] = (byte) 1;
|
||||||
|
request[1] = (byte) user_bytes.length;
|
||||||
|
System.arraycopy(user_bytes, 0, request, 2, user_bytes.length);
|
||||||
|
request[2 + user_bytes.length] = (byte) password_bytes.length;
|
||||||
|
System.arraycopy(password_bytes, 0, request, 3 + user_bytes.length,
|
||||||
|
password_bytes.length);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,176 @@
|
||||||
|
package com.runjva.sourceforge.jsocks.server;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.InterruptedIOException;
|
||||||
|
import java.net.ConnectException;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Ident provides means to obtain user name of the owner of the socket on
|
||||||
|
* remote machine, providing remote machine runs identd daemon.
|
||||||
|
* <p>
|
||||||
|
* To use it: <tt><pre>
|
||||||
|
Socket s = ss.accept();
|
||||||
|
Ident id = new Ident(s);
|
||||||
|
if(id.successful) goUseUser(id.userName);
|
||||||
|
else handleIdentError(id.errorCode,id.errorMessage)
|
||||||
|
</pre></tt>
|
||||||
|
*/
|
||||||
|
public class Ident {
|
||||||
|
|
||||||
|
Logger log = LoggerFactory.getLogger(Ident.class);
|
||||||
|
|
||||||
|
/** Error Message can be null. */
|
||||||
|
public String errorMessage;
|
||||||
|
|
||||||
|
/** Host type as returned by daemon, can be null, if error happened */
|
||||||
|
public String hostType;
|
||||||
|
|
||||||
|
/** User name as returned by the identd daemon, or null, if it failed */
|
||||||
|
public String userName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this is true then userName and hostType contain valid values. Else
|
||||||
|
* errorCode contain the error code, and errorMessage contains the
|
||||||
|
* corresponding message.
|
||||||
|
*/
|
||||||
|
public boolean successful;
|
||||||
|
|
||||||
|
/** Error code */
|
||||||
|
public int errorCode;
|
||||||
|
|
||||||
|
/** Identd on port 113 can't be contacted */
|
||||||
|
public static final int ERR_NO_CONNECT = 1;
|
||||||
|
|
||||||
|
/** Connection timed out */
|
||||||
|
public static final int ERR_TIMEOUT = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identd daemon responded with ERROR, in this case errorMessage contains
|
||||||
|
* the string explanation, as send by the daemon.
|
||||||
|
*/
|
||||||
|
public static final int ERR_PROTOCOL = 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When parsing server response protocol error happened.
|
||||||
|
*/
|
||||||
|
public static final int ERR_PROTOCOL_INCORRECT = 4;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum amount of time we should wait before dropping the connection to
|
||||||
|
* identd server.Setting it to 0 implies infinit timeout.
|
||||||
|
*/
|
||||||
|
public static final int connectionTimeout = 10000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor tries to connect to Identd daemon on the host of the given
|
||||||
|
* socket, and retrieve user name of the owner of given socket connection on
|
||||||
|
* remote machine. After constructor returns public fields are initialised
|
||||||
|
* to whatever the server returned.
|
||||||
|
* <p>
|
||||||
|
* If user name was successfully retrieved successful is set to true, and
|
||||||
|
* userName and hostType are set to whatever server returned. If however for
|
||||||
|
* some reason user name was not obtained, successful is set to false and
|
||||||
|
* errorCode contains the code explaining the reason of failure, and
|
||||||
|
* errorMessage contains human readable explanation.
|
||||||
|
* <p>
|
||||||
|
* Constructor may block, for a while.
|
||||||
|
*
|
||||||
|
* @param s
|
||||||
|
* Socket whose ownership on remote end should be obtained.
|
||||||
|
*/
|
||||||
|
public Ident(Socket s) {
|
||||||
|
Socket sock = null;
|
||||||
|
successful = false; // We are pessimistic
|
||||||
|
|
||||||
|
try {
|
||||||
|
sock = new Socket(s.getInetAddress(), 113);
|
||||||
|
sock.setSoTimeout(connectionTimeout);
|
||||||
|
final byte[] request = ("" + s.getPort() + " , " + s.getLocalPort() + "\r\n")
|
||||||
|
.getBytes();
|
||||||
|
|
||||||
|
sock.getOutputStream().write(request);
|
||||||
|
|
||||||
|
final BufferedReader in = new BufferedReader(new InputStreamReader(
|
||||||
|
sock.getInputStream()));
|
||||||
|
|
||||||
|
parseResponse(in.readLine());
|
||||||
|
|
||||||
|
} catch (final InterruptedIOException iioe) {
|
||||||
|
errorCode = ERR_TIMEOUT;
|
||||||
|
errorMessage = "Connection to identd timed out.";
|
||||||
|
} catch (final ConnectException ce) {
|
||||||
|
errorCode = ERR_NO_CONNECT;
|
||||||
|
errorMessage = "Connection to identd server failed.";
|
||||||
|
|
||||||
|
} catch (final IOException ioe) {
|
||||||
|
errorCode = ERR_NO_CONNECT;
|
||||||
|
errorMessage = "" + ioe;
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
if (sock != null) {
|
||||||
|
sock.close();
|
||||||
|
}
|
||||||
|
} catch (final IOException ioe) {
|
||||||
|
log.warn("Could not close socket", ioe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseResponse(String response) {
|
||||||
|
if (response == null) {
|
||||||
|
errorCode = ERR_PROTOCOL_INCORRECT;
|
||||||
|
errorMessage = "Identd server closed connection.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final StringTokenizer st = new StringTokenizer(response, ":");
|
||||||
|
if (st.countTokens() < 3) {
|
||||||
|
errorCode = ERR_PROTOCOL_INCORRECT;
|
||||||
|
errorMessage = "Can't parse server response.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
st.nextToken(); // Discard first token, it's basically what we have send
|
||||||
|
final String command = st.nextToken().trim().toUpperCase();
|
||||||
|
|
||||||
|
if (command.equals("USERID") && (st.countTokens() >= 2)) {
|
||||||
|
successful = true;
|
||||||
|
hostType = st.nextToken().trim();
|
||||||
|
userName = st.nextToken("").substring(1);// Get all that is left
|
||||||
|
} else if (command.equals("ERROR")) {
|
||||||
|
errorCode = ERR_PROTOCOL;
|
||||||
|
errorMessage = st.nextToken();
|
||||||
|
} else {
|
||||||
|
errorCode = ERR_PROTOCOL_INCORRECT;
|
||||||
|
System.out.println("Opa!");
|
||||||
|
errorMessage = "Can't parse server response.";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// /////////////////////////////////////////////
|
||||||
|
// USED for Testing
|
||||||
|
/*
|
||||||
|
* public static void main(String[] args) throws IOException{
|
||||||
|
*
|
||||||
|
* Socket s = null; s = new Socket("gp101-16", 1391);
|
||||||
|
*
|
||||||
|
* Ident id = new Ident(s); if(id.successful){
|
||||||
|
* System.out.println("User: "+id.userName);
|
||||||
|
* System.out.println("HostType: "+id.hostType); }else{
|
||||||
|
* System.out.println("ErrorCode: "+id.errorCode);
|
||||||
|
* System.out.println("ErrorMessage: "+id.errorMessage);
|
||||||
|
*
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* if(s!= null) s.close(); } //
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,182 @@
|
||||||
|
package com.runjva.sourceforge.jsocks.server;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
import com.runjva.sourceforge.jsocks.protocol.InetRange;
|
||||||
|
import com.runjva.sourceforge.jsocks.protocol.ProxyMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An implementation of socks.ServerAuthentication which provides simple
|
||||||
|
* authentication based on the host from which the connection is made and the
|
||||||
|
* name of the user on the remote machine, as reported by identd daemon on the
|
||||||
|
* remote machine.
|
||||||
|
* <p>
|
||||||
|
* It can also be used to provide authentication based only on the contacting
|
||||||
|
* host address.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class IdentAuthenticator extends ServerAuthenticatorBase {
|
||||||
|
/** Vector of InetRanges */
|
||||||
|
Vector<InetRange> hosts;
|
||||||
|
|
||||||
|
/** Vector of user hashes */
|
||||||
|
Vector<Hashtable<?, ?>> users;
|
||||||
|
|
||||||
|
String user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs empty IdentAuthenticator.
|
||||||
|
*/
|
||||||
|
public IdentAuthenticator() {
|
||||||
|
hosts = new Vector<InetRange>();
|
||||||
|
users = new Vector<Hashtable<?, ?>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to create instances returned from startSession.
|
||||||
|
*
|
||||||
|
* @param in
|
||||||
|
* Input stream.
|
||||||
|
* @param out
|
||||||
|
* OutputStream.
|
||||||
|
* @param user
|
||||||
|
* Username associated with this connection,could be null if name
|
||||||
|
* was not required.
|
||||||
|
*/
|
||||||
|
IdentAuthenticator(InputStream in, OutputStream out, String user) {
|
||||||
|
super(in, out);
|
||||||
|
this.user = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds range of addresses from which connection is allowed. Hashtable users
|
||||||
|
* should contain user names as keys and anything as values (value is not
|
||||||
|
* used and will be ignored).
|
||||||
|
*
|
||||||
|
* @param hostRange
|
||||||
|
* Range of ip addresses from which connection is allowed.
|
||||||
|
* @param users
|
||||||
|
* Hashtable of users for whom connection is allowed, or null to
|
||||||
|
* indicate that anybody is allowed to connect from the hosts
|
||||||
|
* within given range.
|
||||||
|
*/
|
||||||
|
public synchronized void add(InetRange hostRange, Hashtable<?, ?> users) {
|
||||||
|
this.hosts.addElement(hostRange);
|
||||||
|
this.users.addElement(users);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Grants permission only to those users, who connect from one of the hosts
|
||||||
|
* registered with add(InetRange,Hashtable) and whose names, as reported by
|
||||||
|
* identd daemon, are listed for the host the connection came from.
|
||||||
|
*/
|
||||||
|
public ServerAuthenticator startSession(Socket s) throws IOException {
|
||||||
|
|
||||||
|
final int ind = getRangeIndex(s.getInetAddress());
|
||||||
|
String user = null;
|
||||||
|
|
||||||
|
// System.out.println("getRangeReturned:"+ind);
|
||||||
|
|
||||||
|
if (ind < 0) {
|
||||||
|
return null; // Host is not on the list.
|
||||||
|
}
|
||||||
|
|
||||||
|
final ServerAuthenticator serverAuthenticator = super.startSession(s);
|
||||||
|
final ServerAuthenticatorBase auth = (ServerAuthenticatorBase) serverAuthenticator;
|
||||||
|
|
||||||
|
// System.out.println("super.startSession() returned:"+auth);
|
||||||
|
if (auth == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// do the authentication
|
||||||
|
|
||||||
|
final Hashtable<?, ?> user_names = users.elementAt(ind);
|
||||||
|
|
||||||
|
if (user_names != null) { // If need to do authentication
|
||||||
|
Ident ident;
|
||||||
|
ident = new Ident(s);
|
||||||
|
// If can't obtain user name, fail
|
||||||
|
if (!ident.successful) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// If user name is not listed for this address, fail
|
||||||
|
if (!user_names.containsKey(ident.userName)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
user = ident.userName;
|
||||||
|
}
|
||||||
|
return new IdentAuthenticator(auth.in, auth.out, user);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For SOCKS5 requests allways returns true. For SOCKS4 requests checks
|
||||||
|
* wether the user name supplied in the request corresponds to the name
|
||||||
|
* obtained from the ident daemon.
|
||||||
|
*/
|
||||||
|
public boolean checkRequest(ProxyMessage msg, java.net.Socket s) {
|
||||||
|
// If it's version 5 request, or if anybody is permitted, return true;
|
||||||
|
if ((msg.version == 5) || (user == null)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg.version != 4) {
|
||||||
|
return false; // Who knows?
|
||||||
|
}
|
||||||
|
|
||||||
|
return user.equals(msg.user);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get String representaion of the IdentAuthenticator. */
|
||||||
|
public String toString() {
|
||||||
|
String s = "";
|
||||||
|
|
||||||
|
for (int i = 0; i < hosts.size(); ++i) {
|
||||||
|
s += "(Range:" + hosts.elementAt(i) + "," + //
|
||||||
|
" Users:" + userNames(i) + ") ";
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private Methods
|
||||||
|
// ////////////////
|
||||||
|
private int getRangeIndex(InetAddress ip) {
|
||||||
|
int index = 0;
|
||||||
|
final Enumeration<InetRange> enumx = hosts.elements();
|
||||||
|
while (enumx.hasMoreElements()) {
|
||||||
|
final InetRange ir = enumx.nextElement();
|
||||||
|
if (ir.contains(ip)) {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
return -1; // Not found
|
||||||
|
}
|
||||||
|
|
||||||
|
private String userNames(int i) {
|
||||||
|
if (users.elementAt(i) == null) {
|
||||||
|
return "Everybody is permitted.";
|
||||||
|
}
|
||||||
|
|
||||||
|
final Enumeration<?> enumx = ((Hashtable<?, ?>) users.elementAt(i))
|
||||||
|
.keys();
|
||||||
|
if (!enumx.hasMoreElements()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
String s = enumx.nextElement().toString();
|
||||||
|
while (enumx.hasMoreElements()) {
|
||||||
|
s += "; " + enumx.nextElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,126 @@
|
||||||
|
package com.runjva.sourceforge.jsocks.server;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.DatagramPacket;
|
||||||
|
import java.net.Socket;
|
||||||
|
|
||||||
|
import com.runjva.sourceforge.jsocks.protocol.ProxyMessage;
|
||||||
|
import com.runjva.sourceforge.jsocks.protocol.UDPEncapsulation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Classes implementing this interface should provide socks server with
|
||||||
|
* authentication and authorization of users.
|
||||||
|
**/
|
||||||
|
public interface ServerAuthenticator {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called when a new connection accepted by the server.
|
||||||
|
* <p>
|
||||||
|
* At this point no data have been extracted from the connection. It is
|
||||||
|
* responsibility of this method to ensure that the next byte in the stream
|
||||||
|
* after this method have been called is the first byte of the socks request
|
||||||
|
* message. For SOCKSv4 there is no authentication data and the first byte
|
||||||
|
* in the stream is part of the request. With SOCKSv5 however there is an
|
||||||
|
* authentication data first. It is expected that implementaions will
|
||||||
|
* process this authentication data.
|
||||||
|
* <p>
|
||||||
|
* If authentication was successful an instance of ServerAuthentication
|
||||||
|
* should be returned, it later will be used by the server to perform
|
||||||
|
* authorization and some other things. If authentication fails null should
|
||||||
|
* be returned, or an exception may be thrown.
|
||||||
|
*
|
||||||
|
* @param s
|
||||||
|
* Accepted Socket.
|
||||||
|
* @return An instance of ServerAuthenticator to be used for this connection
|
||||||
|
* or null
|
||||||
|
*/
|
||||||
|
ServerAuthenticator startSession(Socket s) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method should return input stream which should be used on the
|
||||||
|
* accepted socket.
|
||||||
|
* <p>
|
||||||
|
* SOCKSv5 allows to have multiple authentication methods, and these methods
|
||||||
|
* might require some kind of transformations being made on the data.
|
||||||
|
* <p>
|
||||||
|
* This method is called on the object returned from the startSession
|
||||||
|
* function.
|
||||||
|
*/
|
||||||
|
InputStream getInputStream();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method should return output stream to use to write to the accepted
|
||||||
|
* socket.
|
||||||
|
* <p>
|
||||||
|
* SOCKSv5 allows to have multiple authentication methods, and these methods
|
||||||
|
* might require some kind of transformations being made on the data.
|
||||||
|
* <p>
|
||||||
|
* This method is called on the object returned from the startSession
|
||||||
|
* function.
|
||||||
|
*/
|
||||||
|
OutputStream getOutputStream();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method should return UDPEncapsulation, which should be used on the
|
||||||
|
* datagrams being send in/out.
|
||||||
|
* <p>
|
||||||
|
* If no transformation should be done on the datagrams, this method should
|
||||||
|
* return null.
|
||||||
|
* <p>
|
||||||
|
* This method is called on the object returned from the startSession
|
||||||
|
* function.
|
||||||
|
*/
|
||||||
|
|
||||||
|
UDPEncapsulation getUdpEncapsulation();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called when a request have been read.
|
||||||
|
* <p>
|
||||||
|
* Implementation should decide wether to grant request or not. Returning
|
||||||
|
* true implies granting the request, false means request should be
|
||||||
|
* rejected.
|
||||||
|
* <p>
|
||||||
|
* This method is called on the object returned from the startSession
|
||||||
|
* function.
|
||||||
|
*
|
||||||
|
* @param msg
|
||||||
|
* Request message.
|
||||||
|
* @return true to grant request, false to reject it.
|
||||||
|
*/
|
||||||
|
boolean checkRequest(ProxyMessage msg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called when datagram is received by the server.
|
||||||
|
* <p>
|
||||||
|
* Implementaions should decide wether it should be forwarded or dropped. It
|
||||||
|
* is expecteed that implementation will use datagram address and port
|
||||||
|
* information to make a decision, as well as anything else. Address and
|
||||||
|
* port of the datagram are always correspond to remote machine. It is
|
||||||
|
* either destination or source address. If out is true address is
|
||||||
|
* destination address, else it is a source address, address of the machine
|
||||||
|
* from which datagram have been received for the client.
|
||||||
|
* <p>
|
||||||
|
* Implementaions should return true if the datagram is to be forwarded, and
|
||||||
|
* false if the datagram should be dropped.
|
||||||
|
* <p>
|
||||||
|
* This method is called on the object returned from the startSession
|
||||||
|
* function.
|
||||||
|
*
|
||||||
|
* @param out
|
||||||
|
* If true the datagram is being send out(from the client),
|
||||||
|
* otherwise it is an incoming datagram.
|
||||||
|
* @return True to forward datagram false drop it silently.
|
||||||
|
*/
|
||||||
|
boolean checkRequest(DatagramPacket dp, boolean out);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called when session is completed. Either due to normal
|
||||||
|
* termination or due to any error condition.
|
||||||
|
* <p>
|
||||||
|
* This method is called on the object returned from the startSession
|
||||||
|
* function.
|
||||||
|
*/
|
||||||
|
void endSession();
|
||||||
|
}
|
|
@ -0,0 +1,187 @@
|
||||||
|
package com.runjva.sourceforge.jsocks.server;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.PushbackInputStream;
|
||||||
|
import java.net.Socket;
|
||||||
|
|
||||||
|
import com.runjva.sourceforge.jsocks.protocol.ProxyMessage;
|
||||||
|
import com.runjva.sourceforge.jsocks.protocol.UDPEncapsulation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An implementation of ServerAuthenticator, which does <b>not</b> do any
|
||||||
|
* authentication.
|
||||||
|
* <P>
|
||||||
|
* <FONT size="+3" color ="FF0000"> Warning!!</font><br>
|
||||||
|
* Should not be used on machines which are not behind the firewall.
|
||||||
|
* <p>
|
||||||
|
* It is only provided to make implementing other authentication schemes easier.
|
||||||
|
* <br>
|
||||||
|
* For Example: <tt><pre>
|
||||||
|
class MyAuth extends socks.server.ServerAuthenticator{
|
||||||
|
...
|
||||||
|
public ServerAuthenticator startSession(java.net.Socket s){
|
||||||
|
if(!checkHost(s.getInetAddress()) return null;
|
||||||
|
return super.startSession(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean checkHost(java.net.Inetaddress addr){
|
||||||
|
boolean allow;
|
||||||
|
//Do it somehow
|
||||||
|
return allow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</pre></tt>
|
||||||
|
*/
|
||||||
|
public abstract class ServerAuthenticatorBase implements ServerAuthenticator {
|
||||||
|
|
||||||
|
static final byte[] socks5response = { 5, 0 };
|
||||||
|
|
||||||
|
InputStream in;
|
||||||
|
OutputStream out;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates new instance of the ServerAuthenticatorNone.
|
||||||
|
*/
|
||||||
|
public ServerAuthenticatorBase() {
|
||||||
|
this.in = null;
|
||||||
|
this.out = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs new ServerAuthenticatorNone object suitable for returning from
|
||||||
|
* the startSession function.
|
||||||
|
*
|
||||||
|
* @param in
|
||||||
|
* Input stream to return from getInputStream method.
|
||||||
|
* @param out
|
||||||
|
* Output stream to return from getOutputStream method.
|
||||||
|
*/
|
||||||
|
public ServerAuthenticatorBase(InputStream in, OutputStream out) {
|
||||||
|
this.in = in;
|
||||||
|
this.out = out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Grants access to everyone.Removes authentication related bytes from the
|
||||||
|
* stream, when a SOCKS5 connection is being made, selects an authentication
|
||||||
|
* NONE.
|
||||||
|
*/
|
||||||
|
public ServerAuthenticator startSession(Socket s) throws IOException {
|
||||||
|
|
||||||
|
final PushbackInputStream in = new PushbackInputStream(s
|
||||||
|
.getInputStream());
|
||||||
|
final OutputStream out = s.getOutputStream();
|
||||||
|
|
||||||
|
final int version = in.read();
|
||||||
|
if (version == 5) {
|
||||||
|
if (!selectSocks5Authentication(in, out, 0)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else if (version == 4) {
|
||||||
|
// Else it is the request message already, version 4
|
||||||
|
in.unread(version);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ServerAuthenticatorNone(in, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get input stream.
|
||||||
|
*
|
||||||
|
* @return Input stream speciefied in the constructor.
|
||||||
|
*/
|
||||||
|
public InputStream getInputStream() {
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get output stream.
|
||||||
|
*
|
||||||
|
* @return Output stream speciefied in the constructor.
|
||||||
|
*/
|
||||||
|
public OutputStream getOutputStream() {
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allways returns null.
|
||||||
|
*
|
||||||
|
* @return null
|
||||||
|
*/
|
||||||
|
public UDPEncapsulation getUdpEncapsulation() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allways returns true.
|
||||||
|
*/
|
||||||
|
public boolean checkRequest(ProxyMessage msg) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allways returns true.
|
||||||
|
*/
|
||||||
|
public boolean checkRequest(java.net.DatagramPacket dp, boolean out) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does nothing.
|
||||||
|
*/
|
||||||
|
public void endSession() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convinience routine for selecting SOCKSv5 authentication.
|
||||||
|
* <p>
|
||||||
|
* This method reads in authentication methods that client supports, checks
|
||||||
|
* wether it supports given method. If it does, the notification method is
|
||||||
|
* written back to client, that this method have been chosen for
|
||||||
|
* authentication. If given method was not found, authentication failure
|
||||||
|
* message is send to client ([5,FF]).
|
||||||
|
*
|
||||||
|
* @param in
|
||||||
|
* Input stream, version byte should be removed from the stream
|
||||||
|
* before calling this method.
|
||||||
|
* @param out
|
||||||
|
* Output stream.
|
||||||
|
* @param methodId
|
||||||
|
* Method which should be selected.
|
||||||
|
* @return true if methodId was found, false otherwise.
|
||||||
|
*/
|
||||||
|
static public boolean selectSocks5Authentication(InputStream in,
|
||||||
|
OutputStream out, int methodId) throws IOException {
|
||||||
|
|
||||||
|
final int num_methods = in.read();
|
||||||
|
if (num_methods <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final byte method_ids[] = new byte[num_methods];
|
||||||
|
final byte response[] = new byte[2];
|
||||||
|
boolean found = false;
|
||||||
|
|
||||||
|
response[0] = (byte) 5; // SOCKS version
|
||||||
|
response[1] = (byte) 0xFF; // Not found, we are pessimistic
|
||||||
|
|
||||||
|
int bread = 0; // bytes read so far
|
||||||
|
while (bread < num_methods) {
|
||||||
|
bread += in.read(method_ids, bread, num_methods - bread);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < num_methods; ++i) {
|
||||||
|
if (method_ids[i] == methodId) {
|
||||||
|
found = true;
|
||||||
|
response[1] = (byte) methodId;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out.write(response);
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.runjva.sourceforge.jsocks.server;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simplest possible ServerAuthenticator implementation. Extends common base.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class ServerAuthenticatorNone extends ServerAuthenticatorBase {
|
||||||
|
|
||||||
|
public ServerAuthenticatorNone(InputStream in, OutputStream out) {
|
||||||
|
super(in, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
package com.runjva.sourceforge.jsocks.server;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.Socket;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class implements SOCKS5 User/Password authentication scheme as defined
|
||||||
|
* in rfc1929,the server side of it. (see docs/rfc1929.txt)
|
||||||
|
*/
|
||||||
|
public class UserPasswordAuthenticator extends ServerAuthenticatorBase {
|
||||||
|
|
||||||
|
static final int METHOD_ID = 2;
|
||||||
|
|
||||||
|
UserValidation validator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new UserPasswordAuthentication object, with given
|
||||||
|
* UserVlaidation scheme.
|
||||||
|
*
|
||||||
|
* @param v
|
||||||
|
* UserValidation to use for validating users.
|
||||||
|
*/
|
||||||
|
public UserPasswordAuthenticator(UserValidation validator) {
|
||||||
|
this.validator = validator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServerAuthenticator startSession(Socket s) throws IOException {
|
||||||
|
final InputStream in = s.getInputStream();
|
||||||
|
final OutputStream out = s.getOutputStream();
|
||||||
|
|
||||||
|
if (in.read() != 5) {
|
||||||
|
return null; // Drop non version 5 messages.
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!selectSocks5Authentication(in, out, METHOD_ID)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!doUserPasswordAuthentication(s, in, out)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ServerAuthenticatorNone(in, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private Methods
|
||||||
|
// ////////////////
|
||||||
|
|
||||||
|
private boolean doUserPasswordAuthentication(Socket s, InputStream in,
|
||||||
|
OutputStream out) throws IOException {
|
||||||
|
final int version = in.read();
|
||||||
|
if (version != 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int ulen = in.read();
|
||||||
|
if (ulen < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final byte[] user = new byte[ulen];
|
||||||
|
in.read(user);
|
||||||
|
final int plen = in.read();
|
||||||
|
if (plen < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final byte[] password = new byte[plen];
|
||||||
|
in.read(password);
|
||||||
|
|
||||||
|
if (validator.isUserValid(new String(user), new String(password), s)) {
|
||||||
|
// System.out.println("user valid");
|
||||||
|
out.write(new byte[] { 1, 0 });
|
||||||
|
} else {
|
||||||
|
// System.out.println("user invalid");
|
||||||
|
out.write(new byte[] { 1, 1 });
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package com.runjva.sourceforge.jsocks.server;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface which provides for user validation, based on user name password and
|
||||||
|
* where it connects from.
|
||||||
|
*/
|
||||||
|
public interface UserValidation {
|
||||||
|
/**
|
||||||
|
* Implementations of this interface are expected to use some or all of the
|
||||||
|
* information provided plus any information they can extract from other
|
||||||
|
* sources to decide wether given user should be allowed access to SOCKS
|
||||||
|
* server, or whatever you use it for.
|
||||||
|
*
|
||||||
|
* @return true to indicate user is valid, false otherwise.
|
||||||
|
* @param username
|
||||||
|
* User whom implementation should validate.
|
||||||
|
* @param password
|
||||||
|
* Password this user provided.
|
||||||
|
* @param connection
|
||||||
|
* Socket which user used to connect to the server.
|
||||||
|
*/
|
||||||
|
boolean isUserValid(String username, String password,
|
||||||
|
java.net.Socket connection);
|
||||||
|
}
|
|
@ -13,9 +13,11 @@ import org.torproject.android.service.TorService;
|
||||||
import org.torproject.android.service.TorServiceConstants;
|
import org.torproject.android.service.TorServiceConstants;
|
||||||
import org.torproject.android.service.TorServiceUtils;
|
import org.torproject.android.service.TorServiceUtils;
|
||||||
import org.torproject.android.settings.SettingsPreferences;
|
import org.torproject.android.settings.SettingsPreferences;
|
||||||
|
import org.torproject.android.vpn.OrbotVpnService;
|
||||||
import org.torproject.android.wizard.ChooseLocaleWizardActivity;
|
import org.torproject.android.wizard.ChooseLocaleWizardActivity;
|
||||||
import org.torproject.android.wizard.TipsAndTricks;
|
import org.torproject.android.wizard.TipsAndTricks;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.app.ProgressDialog;
|
import android.app.ProgressDialog;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
|
@ -32,6 +34,8 @@ import android.content.pm.PackageManager;
|
||||||
import android.content.pm.PackageManager.NameNotFoundException;
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.net.VpnService;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
|
@ -412,6 +416,10 @@ public class Orbot extends ActionBarActivity implements TorConstants, OnLongClic
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
else if (item.getItemId() == R.id.menu_vpn)
|
||||||
|
{
|
||||||
|
this.startVpnService();
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -782,8 +790,17 @@ public class Orbot extends ActionBarActivity implements TorConstants, OnLongClic
|
||||||
startActivityForResult(new Intent(this, SettingsPreferences.class), 1);
|
startActivityForResult(new Intent(this, SettingsPreferences.class), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final static int REQUEST_VPN = 8888;
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
||||||
|
public void startVpnService () {
|
||||||
|
Intent intent = VpnService.prepare(this);
|
||||||
|
if (intent != null) {
|
||||||
|
startActivityForResult(intent, REQUEST_VPN);
|
||||||
|
} else {
|
||||||
|
onActivityResult(REQUEST_VPN, RESULT_OK, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -827,6 +844,11 @@ public class Orbot extends ActionBarActivity implements TorConstants, OnLongClic
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (request == REQUEST_VPN && response == RESULT_OK)
|
||||||
|
{
|
||||||
|
Intent intent = new Intent(this, OrbotVpnService.class);
|
||||||
|
startService(intent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1207,7 +1229,6 @@ public class Orbot extends ActionBarActivity implements TorConstants, OnLongClic
|
||||||
|
|
||||||
private final ServiceConnection mConnection = new ServiceConnection() {
|
private final ServiceConnection mConnection = new ServiceConnection() {
|
||||||
|
|
||||||
|
|
||||||
public void onServiceConnected(ComponentName className,
|
public void onServiceConnected(ComponentName className,
|
||||||
IBinder service) {
|
IBinder service) {
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,239 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.torproject.android.vpn;
|
||||||
|
|
||||||
|
import java.net.DatagramSocket;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.channels.DatagramChannel;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import org.torproject.android.service.TorServiceConstants;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.VpnService;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Message;
|
||||||
|
import android.os.ParcelFileDescriptor;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.runjva.sourceforge.jsocks.protocol.ProxyServer;
|
||||||
|
import com.runjva.sourceforge.jsocks.server.ServerAuthenticatorNone;
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
||||||
|
public class OrbotVpnService extends VpnService implements Handler.Callback {
|
||||||
|
private static final String TAG = "OrbotVpnService";
|
||||||
|
|
||||||
|
private PendingIntent mConfigureIntent;
|
||||||
|
|
||||||
|
private Handler mHandler;
|
||||||
|
private Thread mThread;
|
||||||
|
|
||||||
|
private String mSessionName = "OrbotVPN";
|
||||||
|
private ParcelFileDescriptor mInterface;
|
||||||
|
|
||||||
|
private int mSocksProxyPort = 9999;
|
||||||
|
private ProxyServer mProxyServer;
|
||||||
|
|
||||||
|
private boolean mKeepRunning = true;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
|
// The handler is only used to show messages.
|
||||||
|
if (mHandler == null) {
|
||||||
|
mHandler = new Handler(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop the previous session by interrupting the thread.
|
||||||
|
if (mThread != null) {
|
||||||
|
mThread.interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
startSocksBypass ();
|
||||||
|
setupTun2Socks();
|
||||||
|
|
||||||
|
return START_STICKY;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startSocksBypass ()
|
||||||
|
{
|
||||||
|
Thread thread = new Thread ()
|
||||||
|
{
|
||||||
|
public void run ()
|
||||||
|
{
|
||||||
|
|
||||||
|
try {
|
||||||
|
mProxyServer = new ProxyServer(new ServerAuthenticatorNone(null, null));
|
||||||
|
mProxyServer.setVpnService(OrbotVpnService.this);
|
||||||
|
mProxyServer.start(mSocksProxyPort, 5, InetAddress.getLocalHost());
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
thread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
if (mThread != null) {
|
||||||
|
mKeepRunning = false;
|
||||||
|
mThread.interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mProxyServer != null)
|
||||||
|
mProxyServer.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean handleMessage(Message message) {
|
||||||
|
if (message != null) {
|
||||||
|
Toast.makeText(this, message.what, Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void setupTun2Socks() {
|
||||||
|
|
||||||
|
if (mInterface == null)
|
||||||
|
{
|
||||||
|
// Set the locale to English (or probably any other language that^M
|
||||||
|
// uses Hindu-Arabic (aka Latin) numerals).^M
|
||||||
|
// We have found that VpnService.Builder does something locale-dependent^M
|
||||||
|
// internally that causes errors when the locale uses its own numerals^M
|
||||||
|
// (i.e., Farsi and Arabic).^M
|
||||||
|
Locale.setDefault(new Locale("en"));
|
||||||
|
|
||||||
|
Builder builder = new Builder();
|
||||||
|
|
||||||
|
builder.setMtu(3000);
|
||||||
|
builder.addAddress("10.0.0.1",8);
|
||||||
|
builder.setSession("OrbotVPN");
|
||||||
|
builder.addRoute("0.0.0.0",0);
|
||||||
|
builder.addRoute("10.0.0.0",8);
|
||||||
|
//builder.addDnsServer("10.0.0.2");
|
||||||
|
builder.addDnsServer("127.0.0.1");
|
||||||
|
// Close the old interface since the parameters have been changed.
|
||||||
|
try {
|
||||||
|
mInterface.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Create a new interface using the builder and save the parameters.
|
||||||
|
mInterface = builder.setSession(mSessionName)
|
||||||
|
.setConfigureIntent(mConfigureIntent)
|
||||||
|
.establish();
|
||||||
|
|
||||||
|
Tun2Socks.Start(mInterface, 3000, "10.0.0.2", "255.255.255.0", "localhost:" + TorServiceConstants.PORT_SOCKS_DEFAULT, null, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void debugPacket(ByteBuffer packet)
|
||||||
|
{
|
||||||
|
|
||||||
|
int buffer = packet.get();
|
||||||
|
int version;
|
||||||
|
int headerlength;
|
||||||
|
version = buffer >> 4;
|
||||||
|
headerlength = buffer & 0x0F;
|
||||||
|
headerlength *= 4;
|
||||||
|
Log.d(TAG, "IP Version:"+version);
|
||||||
|
Log.d(TAG, "Header Length:"+headerlength);
|
||||||
|
|
||||||
|
String status = "";
|
||||||
|
status += "Header Length:"+headerlength;
|
||||||
|
|
||||||
|
buffer = packet.get(); //DSCP + EN
|
||||||
|
buffer = packet.getChar(); //Total Length
|
||||||
|
|
||||||
|
Log.d(TAG, "Total Length:"+buffer);
|
||||||
|
|
||||||
|
buffer = packet.getChar(); //Identification
|
||||||
|
Log.d(TAG, "Identification:"+buffer);
|
||||||
|
|
||||||
|
buffer = packet.getChar(); //Flags + Fragment Offset
|
||||||
|
buffer = packet.get(); //Time to Live
|
||||||
|
buffer = packet.get(); //Protocol
|
||||||
|
|
||||||
|
Log.d(TAG, "Protocol:"+buffer);
|
||||||
|
|
||||||
|
status += " Protocol:"+buffer;
|
||||||
|
|
||||||
|
buffer = packet.getChar(); //Header checksum
|
||||||
|
|
||||||
|
String sourceIP = "";
|
||||||
|
buffer = packet.get(); //Source IP 1st Octet
|
||||||
|
sourceIP += buffer;
|
||||||
|
sourceIP += ".";
|
||||||
|
|
||||||
|
buffer = packet.get(); //Source IP 2nd Octet
|
||||||
|
sourceIP += buffer;
|
||||||
|
sourceIP += ".";
|
||||||
|
|
||||||
|
buffer = packet.get(); //Source IP 3rd Octet
|
||||||
|
sourceIP += buffer;
|
||||||
|
sourceIP += ".";
|
||||||
|
|
||||||
|
buffer = packet.get(); //Source IP 4th Octet
|
||||||
|
sourceIP += buffer;
|
||||||
|
|
||||||
|
Log.d(TAG, "Source IP:"+sourceIP);
|
||||||
|
|
||||||
|
status += " Source IP:"+sourceIP;
|
||||||
|
|
||||||
|
String destIP = "";
|
||||||
|
buffer = packet.get(); //Destination IP 1st Octet
|
||||||
|
destIP += buffer;
|
||||||
|
destIP += ".";
|
||||||
|
|
||||||
|
buffer = packet.get(); //Destination IP 2nd Octet
|
||||||
|
destIP += buffer;
|
||||||
|
destIP += ".";
|
||||||
|
|
||||||
|
buffer = packet.get(); //Destination IP 3rd Octet
|
||||||
|
destIP += buffer;
|
||||||
|
destIP += ".";
|
||||||
|
|
||||||
|
buffer = packet.get(); //Destination IP 4th Octet
|
||||||
|
destIP += buffer;
|
||||||
|
|
||||||
|
Log.d(TAG, "Destination IP:"+destIP);
|
||||||
|
|
||||||
|
status += " Destination IP:"+destIP;
|
||||||
|
/*
|
||||||
|
msgObj = mHandler.obtainMessage();
|
||||||
|
msgObj.obj = status;
|
||||||
|
mHandler.sendMessage(msgObj);
|
||||||
|
*/
|
||||||
|
|
||||||
|
//Log.d(TAG, "version:"+packet.getInt());
|
||||||
|
//Log.d(TAG, "version:"+packet.getInt());
|
||||||
|
//Log.d(TAG, "version:"+packet.getInt());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,124 @@
|
||||||
|
package org.torproject.android.vpn;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2013, Psiphon Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.net.DatagramSocket;
|
||||||
|
import java.net.Socket;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.ParcelFileDescriptor;
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
|
||||||
|
public class Tun2Socks
|
||||||
|
{
|
||||||
|
public static interface IProtectSocket
|
||||||
|
{
|
||||||
|
boolean doVpnProtect(Socket socket);
|
||||||
|
boolean doVpnProtect(DatagramSocket socket);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
private static Thread mThread;
|
||||||
|
private static ParcelFileDescriptor mVpnInterfaceFileDescriptor;
|
||||||
|
private static int mVpnInterfaceMTU;
|
||||||
|
private static String mVpnIpAddress;
|
||||||
|
private static String mVpnNetMask;
|
||||||
|
private static String mSocksServerAddress;
|
||||||
|
private static String mUdpgwServerAddress;
|
||||||
|
private static boolean mUdpgwTransparentDNS;
|
||||||
|
|
||||||
|
// Note: this class isn't a singleton, but you can't run more
|
||||||
|
// than one instance due to the use of global state (the lwip
|
||||||
|
// module, etc.) in the native code.
|
||||||
|
|
||||||
|
public static synchronized void Start(
|
||||||
|
ParcelFileDescriptor vpnInterfaceFileDescriptor,
|
||||||
|
int vpnInterfaceMTU,
|
||||||
|
String vpnIpAddress,
|
||||||
|
String vpnNetMask,
|
||||||
|
String socksServerAddress,
|
||||||
|
String udpgwServerAddress,
|
||||||
|
boolean udpgwTransparentDNS)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
Stop();
|
||||||
|
|
||||||
|
mVpnInterfaceFileDescriptor = vpnInterfaceFileDescriptor;
|
||||||
|
mVpnInterfaceMTU = vpnInterfaceMTU;
|
||||||
|
mVpnIpAddress = vpnIpAddress;
|
||||||
|
mVpnNetMask = vpnNetMask;
|
||||||
|
mSocksServerAddress = socksServerAddress;
|
||||||
|
mUdpgwServerAddress = udpgwServerAddress;
|
||||||
|
mUdpgwTransparentDNS = udpgwTransparentDNS;
|
||||||
|
|
||||||
|
mThread = new Thread(new Runnable()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void run()
|
||||||
|
{
|
||||||
|
runTun2Socks(
|
||||||
|
mVpnInterfaceFileDescriptor.detachFd(),
|
||||||
|
mVpnInterfaceMTU,
|
||||||
|
mVpnIpAddress,
|
||||||
|
mVpnNetMask,
|
||||||
|
mSocksServerAddress,
|
||||||
|
mUdpgwServerAddress,
|
||||||
|
mUdpgwTransparentDNS ? 1 : 0);
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mThread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized void Stop()
|
||||||
|
{
|
||||||
|
if (mThread != null)
|
||||||
|
{
|
||||||
|
terminateTun2Socks();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
mThread.join();
|
||||||
|
}
|
||||||
|
catch (InterruptedException e)
|
||||||
|
{
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
mThread = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private native static int runTun2Socks(
|
||||||
|
int vpnInterfaceFileDescriptor,
|
||||||
|
int vpnInterfaceMTU,
|
||||||
|
String vpnIpAddress,
|
||||||
|
String vpnNetMask,
|
||||||
|
String socksServerAddress,
|
||||||
|
String udpgwServerAddress,
|
||||||
|
int udpgwTransparentDNS);
|
||||||
|
|
||||||
|
private native static void terminateTun2Socks();
|
||||||
|
|
||||||
|
static
|
||||||
|
{
|
||||||
|
System.loadLibrary("tun2socks");
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue