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]); } } }