tor-android/src/com/runjva/sourceforge/jsocks/protocol/InetRange.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]);
}
}
}