diff options
Diffstat (limited to 'src/main/java/com/btr/proxy/selector/whitelist')
7 files changed, 465 insertions, 0 deletions
diff --git a/src/main/java/com/btr/proxy/selector/whitelist/DefaultWhiteListParser.java b/src/main/java/com/btr/proxy/selector/whitelist/DefaultWhiteListParser.java new file mode 100644 index 0000000..0f949e1 --- /dev/null +++ b/src/main/java/com/btr/proxy/selector/whitelist/DefaultWhiteListParser.java @@ -0,0 +1,78 @@ +package com.btr.proxy.selector.whitelist; + +import java.util.ArrayList; +import java.util.List; + +import com.btr.proxy.selector.whitelist.HostnameFilter.Mode; +import com.btr.proxy.util.UriFilter; + +/***************************************************************************** + * Default implementation for an white list parser. This will support the most + * common forms of filters found in white lists. + * The white list is a comma (or space) separated list of domain names or IP addresses. + * The following section shows some examples. + * + * .mynet.com - Filters all host names ending with .mynet.com + * *.mynet.com - Filters all host names ending with .mynet.com + * www.mynet.* - Filters all host names starting with www.mynet. + * 123.12.32.1 - Filters the IP 123.12.32.1 + * 123.12.32.1/255 - Filters the IP range + * http://www.mynet.com - Filters only HTTP protocol not FTP and no HTTPS + * + * Example of a list: + * + * .mynet.com, *.my-other-net.org, 123.55.23.222, 123.55.23.0/24 + * + * Some info about this topic can be found here: + * http://kb.mozillazine.org/No_proxy_for + * http://technet.microsoft.com/en-us/library/dd361953.aspx + * + * Note that this implementation does not cover all variations of all browsers + * but should cover the most used formats. + * + * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009 + ****************************************************************************/ + +public class DefaultWhiteListParser implements WhiteListParser { + + /************************************************************************* + * parseWhiteList + * @see com.btr.proxy.selector.whitelist.WhiteListParser#parseWhiteList(java.lang.String) + ************************************************************************/ + + public List<UriFilter> parseWhiteList(String whiteList) { + List<UriFilter> result = new ArrayList<UriFilter>(); + + String[] token = whiteList.split("[, ]+"); + for (int i = 0; i < token.length; i++) { + String tkn = token[i].trim(); + if (isIP4SubnetFilter(tkn)) { + result.add(new IpRangeFilter(tkn)); + continue; + } else + if (tkn.endsWith("*")) { + tkn = tkn.substring(0, tkn.length()-1); + result.add(new HostnameFilter(Mode.BEGINS_WITH, tkn)); + continue; + } else + if (tkn.trim().startsWith("*")) { + tkn = tkn.substring(1); + result.add(new HostnameFilter(Mode.ENDS_WITH, tkn)); + } else { + result.add(new HostnameFilter(Mode.ENDS_WITH, tkn)); + } + } + + return result; + } + + /************************************************************************* + * @param token + * @return + ************************************************************************/ + + private boolean isIP4SubnetFilter(String token) { + return IPv4WithSubnetChecker.isValid(token); + } + +} diff --git a/src/main/java/com/btr/proxy/selector/whitelist/HostnameFilter.java b/src/main/java/com/btr/proxy/selector/whitelist/HostnameFilter.java new file mode 100644 index 0000000..176eaeb --- /dev/null +++ b/src/main/java/com/btr/proxy/selector/whitelist/HostnameFilter.java @@ -0,0 +1,93 @@ +package com.btr.proxy.selector.whitelist; + +import java.net.URI; +import com.btr.proxy.util.UriFilter; + +/***************************************************************************** + * Tests if a host name of a given URI matches some criteria. + * + * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009 + ****************************************************************************/ + +public class HostnameFilter implements UriFilter { + + private static final String PROTOCOL_ENDING = "://"; + + public enum Mode {BEGINS_WITH, ENDS_WITH, REGEX} + + private String matchTo; + private String protocolFilter; + private Mode mode; + + /************************************************************************* + * Constructor + * @param mode the filter mode. + * @param matchTo the match criteria. + ************************************************************************/ + + public HostnameFilter(Mode mode, String matchTo) { + super(); + this.mode = mode; + this.matchTo = matchTo.toLowerCase(); + + extractProtocolFilter(); + } + + /************************************************************************* + * Extracts the protocol if one is given to initialize the protocol matcher. + ************************************************************************/ + + private void extractProtocolFilter() { + int protocolIndex = this.matchTo.indexOf(PROTOCOL_ENDING); + if (protocolIndex != -1) { + this.protocolFilter = this.matchTo.substring(0, protocolIndex); + this.matchTo = this.matchTo.substring(protocolIndex+PROTOCOL_ENDING.length()); + } + } + + /************************************************************************* + * accept + * @see com.btr.proxy.util.UriFilter#accept(java.net.URI) + ************************************************************************/ + + public boolean accept(URI uri) { + if (uri == null || uri.getAuthority() == null) { + return false; + } + + if (!isProtocolMatching(uri)) { + return false; + } + + String host = uri.getAuthority(); + + // Strip away port. + int index = host.indexOf(':'); + if (index != -1) { + host = host.substring(0, index); + } + + switch (this.mode) { + case BEGINS_WITH : + return host.toLowerCase().startsWith(this.matchTo); + case ENDS_WITH : + return host.toLowerCase().endsWith(this.matchTo); + case REGEX : + return host.toLowerCase().matches(this.matchTo); + } + return false; + } + + /************************************************************************* + * Applies the protocol filter if available to see if we have a match. + * @param uri to test for a correct protocol. + * @return true if passed else false. + ************************************************************************/ + + private boolean isProtocolMatching(URI uri) { + return this.protocolFilter == null + || uri.getScheme() == null + || uri.getScheme().equalsIgnoreCase(this.protocolFilter); + } + +} diff --git a/src/main/java/com/btr/proxy/selector/whitelist/IPv4WithSubnetChecker.java b/src/main/java/com/btr/proxy/selector/whitelist/IPv4WithSubnetChecker.java new file mode 100644 index 0000000..b8e713e --- /dev/null +++ b/src/main/java/com/btr/proxy/selector/whitelist/IPv4WithSubnetChecker.java @@ -0,0 +1,29 @@ +package com.btr.proxy.selector.whitelist; + +import java.util.regex.Pattern; + +/***************************************************************************** + * Checks if the given string is a IP4 range subnet definition + * of the format 192.168.0/24 + * Based on a contribution by Jan Engler + * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009 + ****************************************************************************/ + +public class IPv4WithSubnetChecker { + + private static Pattern IP_SUB_PATTERN = Pattern.compile( + "^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." + + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." + + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." + + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])/(\\d|([12]\\d|3[0-2]))$"); + + /************************************************************************* + * Tests if a given string is of in the correct format for an IP4 subnet mask. + * @param possibleIPAddress to test for valid format. + * @return true if valid else false. + ************************************************************************/ + + public static boolean isValid(String possibleIPAddress) { + return IP_SUB_PATTERN.matcher(possibleIPAddress).matches(); + } +} diff --git a/src/main/java/com/btr/proxy/selector/whitelist/IpRangeFilter.java b/src/main/java/com/btr/proxy/selector/whitelist/IpRangeFilter.java new file mode 100644 index 0000000..293f520 --- /dev/null +++ b/src/main/java/com/btr/proxy/selector/whitelist/IpRangeFilter.java @@ -0,0 +1,83 @@ +package com.btr.proxy.selector.whitelist; + +import java.net.InetAddress; +import java.net.URI; +import java.net.UnknownHostException; + +import com.btr.proxy.util.UriFilter; + +/***************************************************************************** + * Filters an URI by inspecting it's IP address is in a given range. + * The range as must be defined in CIDR notation. + * e.g. 192.0.2.1/24, + * + * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009 + ****************************************************************************/ + +public class IpRangeFilter implements UriFilter { + + private byte[] matchTo; + int numOfBits; + + /************************************************************************* + * Constructor + * @param matchTo the match subnet in CIDR notation. + ************************************************************************/ + + public IpRangeFilter(String matchTo) { + super(); + + String[] parts = matchTo.split("/"); + if (parts.length != 2) { + throw new IllegalArgumentException("IP range is not valid:"+matchTo); + } + + try { + InetAddress address = InetAddress.getByName(parts[0].trim()); + this.matchTo = address.getAddress(); + } catch (UnknownHostException e) { + throw new IllegalArgumentException("IP range is not valid:"+matchTo); + } + + this.numOfBits = Integer.parseInt(parts[1].trim()); + } + + /************************************************************************* + * accept + * @see com.btr.proxy.util.UriFilter#accept(java.net.URI) + ************************************************************************/ + + public boolean accept(URI uri) { + if (uri == null || uri.getHost() == null) { + return false; + } + try { + InetAddress address = InetAddress.getByName(uri.getHost()); + byte[] addr = address.getAddress(); + + // Comparing IP6 against IP4? + if (addr.length != this.matchTo.length) { + return false; + } + + int bit = 0; + for (int nibble = 0; nibble < addr.length; nibble++) { + for (int nibblePos = 7; nibblePos >= 0; nibblePos--) { + int mask = 1 << nibblePos; + if ((this.matchTo[nibble] & mask) != (addr[nibble] & mask)) { + return false; + } + bit++; + if (bit >= this.numOfBits) { + return true; + } + } + } + + } catch (UnknownHostException e) { + // In this case we can not get the IP do not match. + } + return false; + } + +} diff --git a/src/main/java/com/btr/proxy/selector/whitelist/ProxyBypassListSelector.java b/src/main/java/com/btr/proxy/selector/whitelist/ProxyBypassListSelector.java new file mode 100644 index 0000000..81e2a7d --- /dev/null +++ b/src/main/java/com/btr/proxy/selector/whitelist/ProxyBypassListSelector.java @@ -0,0 +1,84 @@ +package com.btr.proxy.selector.whitelist; + +import java.io.IOException; +import java.net.Proxy; +import java.net.ProxySelector; +import java.net.SocketAddress; +import java.net.URI; +import java.util.List; + +import com.btr.proxy.util.ProxyUtil; +import com.btr.proxy.util.UriFilter; + +/***************************************************************************** + * Special purpose ProxySelector used as Facade on top of a normal ProxySelector. + * A wrapper that will first check the URI against a white list and if it matches + * it will return DIRECT else it will pass the URI to an delegate for inspection. + * + * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009 + ****************************************************************************/ + +public class ProxyBypassListSelector extends ProxySelector { + + private ProxySelector delegate; + private List<UriFilter> whiteListFilter; + + + /************************************************************************* + * Constructor + * @param whiteListFilter a list of filters for whitelist URLs. + * @param proxySelector the proxy selector to use. + ************************************************************************/ + + public ProxyBypassListSelector(List<UriFilter> whiteListFilter, ProxySelector proxySelector) { + super(); + if (whiteListFilter == null) { + throw new NullPointerException("Whitelist must not be null."); + } + if (proxySelector == null) { + throw new NullPointerException("ProxySelector must not be null."); + } + + this.delegate = proxySelector; + this.whiteListFilter = whiteListFilter; + } + + + /************************************************************************* + * Constructor + * @param whiteList a list of filters for whitelist URLs as comma/space separated string. + * @param proxySelector the proxy selector to use. + ************************************************************************/ + + public ProxyBypassListSelector(String whiteList, ProxySelector proxySelector) { + this(new DefaultWhiteListParser().parseWhiteList(whiteList), proxySelector); + } + + /************************************************************************* + * connectFailed + * @see java.net.ProxySelector#connectFailed(java.net.URI, java.net.SocketAddress, java.io.IOException) + ************************************************************************/ + @Override + public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { + this.delegate.connectFailed(uri, sa, ioe); + } + + /************************************************************************* + * select + * @see java.net.ProxySelector#select(java.net.URI) + ************************************************************************/ + + @Override + public List<Proxy> select(URI uri) { + + // If in white list, use DIRECT connection. + for (UriFilter filter : this.whiteListFilter) { + if (filter.accept(uri)) { + return ProxyUtil.noProxyList(); + } + } + + return this.delegate.select(uri); + } + +} diff --git a/src/main/java/com/btr/proxy/selector/whitelist/UseProxyWhiteListSelector.java b/src/main/java/com/btr/proxy/selector/whitelist/UseProxyWhiteListSelector.java new file mode 100644 index 0000000..1aface9 --- /dev/null +++ b/src/main/java/com/btr/proxy/selector/whitelist/UseProxyWhiteListSelector.java @@ -0,0 +1,74 @@ +package com.btr.proxy.selector.whitelist; + +import java.io.IOException; +import java.net.Proxy; +import java.net.ProxySelector; +import java.net.SocketAddress; +import java.net.URI; +import java.util.List; + +import com.btr.proxy.util.ProxyUtil; +import com.btr.proxy.util.UriFilter; + +/***************************************************************************** + * Special purpose ProxySelector used as Facade on top of a normal ProxySelector. + * A wrapper that will first check the URI against a white list and if it matches + * it will use a proxy as provided by the delegate ProxySelector else it will + * return DIRECT. + * + * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009 + ****************************************************************************/ + +public class UseProxyWhiteListSelector extends ProxySelector { + + private ProxySelector delegate; + private List<UriFilter> whiteListFilter; + + /************************************************************************* + * Constructor + * @param proxySelector the proxy selector to use. + ************************************************************************/ + + public UseProxyWhiteListSelector(String whiteList, ProxySelector proxySelector) { + super(); + if (whiteList == null) { + throw new NullPointerException("Whitelist must not be null."); + } + if (proxySelector == null) { + throw new NullPointerException("ProxySelector must not be null."); + } + + this.delegate = proxySelector; + + WhiteListParser parser = new DefaultWhiteListParser(); + this.whiteListFilter = parser.parseWhiteList(whiteList); + } + + /************************************************************************* + * connectFailed + * @see java.net.ProxySelector#connectFailed(java.net.URI, java.net.SocketAddress, java.io.IOException) + ************************************************************************/ + @Override + public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { + this.delegate.connectFailed(uri, sa, ioe); + } + + /************************************************************************* + * select + * @see java.net.ProxySelector#select(java.net.URI) + ************************************************************************/ + + @Override + public List<Proxy> select(URI uri) { + + // If in white list, use proxy selector. + for (UriFilter filter : this.whiteListFilter) { + if (filter.accept(uri)) { + return this.delegate.select(uri); + } + } + + return ProxyUtil.noProxyList(); + } + +} diff --git a/src/main/java/com/btr/proxy/selector/whitelist/WhiteListParser.java b/src/main/java/com/btr/proxy/selector/whitelist/WhiteListParser.java new file mode 100644 index 0000000..7cd5fd5 --- /dev/null +++ b/src/main/java/com/btr/proxy/selector/whitelist/WhiteListParser.java @@ -0,0 +1,24 @@ +package com.btr.proxy.selector.whitelist; + +import java.util.List; + +import com.btr.proxy.util.UriFilter; + +/***************************************************************************** + * Interface for an white list parser. This will take an white list string and + * parse it into a list of UriFilter rules. + * + * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009 + ****************************************************************************/ + +public interface WhiteListParser { + + /************************************************************************* + * Parses a list of host name and IP filters into UriFilter objects. + * @param whiteList the string to parse. + * @return a list of UriFilters + ************************************************************************/ + + public List<UriFilter> parseWhiteList(String whiteList); + +} |