493 lines
12 KiB
Java
493 lines
12 KiB
Java
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]);
|
|
}
|
|
}
|
|
|
|
}
|