diff options
Diffstat (limited to 'src/main/java/com/btr/proxy/search/desktop/osx/OsxProxySearchStrategy.java')
-rw-r--r-- | src/main/java/com/btr/proxy/search/desktop/osx/OsxProxySearchStrategy.java | 325 |
1 files changed, 325 insertions, 0 deletions
diff --git a/src/main/java/com/btr/proxy/search/desktop/osx/OsxProxySearchStrategy.java b/src/main/java/com/btr/proxy/search/desktop/osx/OsxProxySearchStrategy.java new file mode 100644 index 0000000..3d67ae7 --- /dev/null +++ b/src/main/java/com/btr/proxy/search/desktop/osx/OsxProxySearchStrategy.java @@ -0,0 +1,325 @@ +package com.btr.proxy.search.desktop.osx; + +import java.io.File; +import java.io.IOException; +import java.net.NetworkInterface; +import java.net.ProxySelector; +import java.net.SocketException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.List; + +import com.btr.proxy.search.ProxySearchStrategy; +import com.btr.proxy.search.browser.ie.IELocalByPassFilter; +import com.btr.proxy.search.wpad.WpadProxySearchStrategy; +import com.btr.proxy.selector.direct.NoProxySelector; +import com.btr.proxy.selector.fixed.FixedProxySelector; +import com.btr.proxy.selector.fixed.FixedSocksSelector; +import com.btr.proxy.selector.misc.ProtocolDispatchSelector; +import com.btr.proxy.selector.whitelist.ProxyBypassListSelector; +import com.btr.proxy.util.Logger; +import com.btr.proxy.util.Logger.LogLevel; +import com.btr.proxy.util.PListParser; +import com.btr.proxy.util.PListParser.Dict; +import com.btr.proxy.util.PListParser.XmlParseException; +import com.btr.proxy.util.ProxyException; +import com.btr.proxy.util.ProxyUtil; +import com.btr.proxy.util.UriFilter; + +/***************************************************************************** + * Loads the OSX system proxy settings from the settings file. + * <p> + * All settings are stored in OSX in a special XML file format. + * These settings file are named plist files and contain nested dictionaries, arrays and values. + * </p><p> + * To parse this file we use a parser that is derived from a plist parser that + * comes with the xmlwise XML parser package: + * </p><p> + * http://code.google.com/p/xmlwise/ + * </p><p> + * I modified that parser to work with the default Java XML parsing library. + * </p><p> + * The plist file is located on OSX at: + * </p><p> + * /Library/Preferences/SystemConfiguration/preferences.plist + * </p> + * + * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2011 + ****************************************************************************/ + +public class OsxProxySearchStrategy implements ProxySearchStrategy { + + public static final String OVERRIDE_SETTINGS_FILE = "com.btr.proxy.osx.settingsFile"; + public static final String OVERRIDE_ACCEPTED_DEVICES = "com.btr.proxy.osx.acceptedDevices"; + + private static final String SETTINGS_FILE = "/Library/Preferences/SystemConfiguration/preferences.plist"; + + /************************************************************************* + * ProxySelector + * @see java.net.ProxySelector#ProxySelector() + ************************************************************************/ + + public OsxProxySearchStrategy() { + super(); + } + + /************************************************************************* + * Loads the proxy settings and initializes a proxy selector for the OSX + * proxy settings. + * @return a configured ProxySelector, null if none is found. + * @throws ProxyException on file reading error. + ************************************************************************/ + + public ProxySelector getProxySelector() throws ProxyException { + + Logger.log(getClass(), LogLevel.TRACE, "Detecting OSX proxy settings"); + + try { + List<String> acceptedInterfaces = getNetworkInterfaces(); + + Dict settings = PListParser.load(getSettingsFile()); + Object currentSet = settings.getAtPath("/CurrentSet"); + if (currentSet == null) { + throw new ProxyException("CurrentSet not defined"); + } + + Dict networkSet = (Dict) settings.getAtPath(String.valueOf(currentSet)); + List<?> serviceOrder = (List<?>) networkSet.getAtPath("/Network/Global/IPv4/ServiceOrder"); + if (serviceOrder == null || serviceOrder.size() == 0) { + throw new ProxyException("ServiceOrder not defined"); + } + + // Look at the Services in priority order and pick the first one that was + // also accepted above + Dict proxySettings = null; + for (int i = 0; i < serviceOrder.size() && proxySettings == null; i++) { + Object candidateService = serviceOrder.get(i); + Object networkService = networkSet.getAtPath("/Network/Service/"+candidateService+"/__LINK__"); + if (networkService == null ) { + throw new ProxyException("NetworkService not defined."); + } + Dict selectedServiceSettings = (Dict) settings.getAtPath(""+networkService); + String interfaceName = (String) selectedServiceSettings.getAtPath("/Interface/DeviceName"); + if (acceptedInterfaces.contains(interfaceName)) { + Logger.log(getClass(), LogLevel.TRACE, "Looking up proxies for device " + interfaceName); + proxySettings = (Dict) selectedServiceSettings.getAtPath("/Proxies"); + } + } + if (proxySettings == null) { + return NoProxySelector.getInstance(); + } + + return buildSelector(proxySettings); + } catch (XmlParseException e) { + throw new ProxyException(e); + } catch (IOException e) { + throw new ProxyException(e); + } + } + + /************************************************************************* + * Build a selector from the given settings. + * @param proxySettings to parse + * @return the configured selector + * @throws ProxyException on error + ************************************************************************/ + + private ProxySelector buildSelector(Dict proxySettings) throws ProxyException { + ProtocolDispatchSelector ps = new ProtocolDispatchSelector(); + installSelectorForProtocol(proxySettings, ps, "HTTP"); + installSelectorForProtocol(proxySettings, ps, "HTTPS"); + installSelectorForProtocol(proxySettings, ps, "FTP"); + installSelectorForProtocol(proxySettings, ps, "Gopher"); + installSelectorForProtocol(proxySettings, ps, "RTSP"); + installSocksProxy(proxySettings, ps); + + ProxySelector result = ps; + result = installPacProxyIfAvailable(proxySettings, result); + result = autodetectProxyIfAvailable(proxySettings, result); + + result = installExceptionList(proxySettings, result); + result = installSimpleHostFilter(proxySettings, result); + return result; + } + + /************************************************************************* + * Create a list of Ethernet interfaces that are connected + * @return + * @throws SocketException + ************************************************************************/ + + private List<String> getNetworkInterfaces() throws SocketException { + String override = System.getProperty(OVERRIDE_ACCEPTED_DEVICES); + if (override != null && override.length() > 0) { + return Arrays.asList(override.split(";")); + } + + List<String> acceptedInterfaces = new ArrayList<String>(); + Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); + while (interfaces.hasMoreElements()) { + NetworkInterface ni = interfaces.nextElement(); + if (isInterfaceAllowed(ni)) { + acceptedInterfaces.add(ni.getName()); + } + } + return acceptedInterfaces; + } + + /************************************************************************* + * Check if a given network interface is interesting for us. + * @param ni the interface to check + * @return true if accepted else false. + * @throws SocketException on error. + ************************************************************************/ + + private boolean isInterfaceAllowed(NetworkInterface ni) throws SocketException { + return !ni.isLoopback() && + !ni.isPointToPoint() && // Not sure if we should filter the point to point interfaces? + !ni.isVirtual() && + ni.isUp(); + } + + /************************************************************************* + * @return + ************************************************************************/ + + private File getSettingsFile() { + File result = new File(SETTINGS_FILE); + String overrideFile = System.getProperty(OVERRIDE_SETTINGS_FILE); + if (overrideFile != null) { + return new File(overrideFile); + } + return result; + } + + /************************************************************************* + * @param proxySettings + * @param result + * @return + ************************************************************************/ + + private ProxySelector installSimpleHostFilter( + Dict proxySettings, ProxySelector result) { + if (isActive(proxySettings.get("ExcludeSimpleHostnames"))) { + List<UriFilter> localBypassFilter = new ArrayList<UriFilter>(); + localBypassFilter.add(new IELocalByPassFilter()); + result = new ProxyBypassListSelector(localBypassFilter, result); + } + return result; + } + + /************************************************************************* + * @param proxySettings + * @param result + * @return + ************************************************************************/ + + private ProxySelector installExceptionList( + Dict proxySettings, ProxySelector result) { + List<?> proxyExceptions = (List<?>) proxySettings.get("ExceptionsList"); + if (proxyExceptions != null && proxyExceptions.size() > 0) { + Logger.log(getClass(), LogLevel.TRACE, "OSX uses proxy bypass list: {0}", proxyExceptions); + String noProxyList = toCommaSeparatedString(proxyExceptions); + result = new ProxyBypassListSelector(noProxyList, result); + } + return result; + } + + /************************************************************************* + * Convert a list to a comma separated list. + * @param proxyExceptions list of elements. + * @return a comma separated string of the list's content. + ************************************************************************/ + + private String toCommaSeparatedString(List<?> proxyExceptions) { + StringBuilder result = new StringBuilder(); + for (Object object : proxyExceptions) { + if (result.length() > 0) { + result.append(","); + } + result.append(object); + } + return result.toString(); + } + + /************************************************************************* + * @param proxySettings + * @param result + * @return + * @throws ProxyException + ************************************************************************/ + + private ProxySelector autodetectProxyIfAvailable( + Dict proxySettings, ProxySelector result) + throws ProxyException { + if (isActive(proxySettings.get("ProxyAutoDiscoveryEnable"))) { + ProxySelector wp = new WpadProxySearchStrategy().getProxySelector(); + if (wp != null) { + result = wp; + } + } + return result; + } + + /************************************************************************* + * @param proxySettings + * @param result + * @return + ************************************************************************/ + + private ProxySelector installPacProxyIfAvailable(Dict proxySettings, + ProxySelector result) { + if (isActive(proxySettings.get("ProxyAutoConfigEnable"))) { + String url = (String) proxySettings.get("ProxyAutoConfigURLString"); + result = ProxyUtil.buildPacSelectorForUrl(url); + } + return result; + } + + /************************************************************************* + * Build a socks proxy and set it for the socks protocol. + * @param proxySettings to read the config values from. + * @param ps the ProtocolDispatchSelector to install the new proxy on. + ************************************************************************/ + + private void installSocksProxy(Dict proxySettings, + ProtocolDispatchSelector ps) { + if (isActive(proxySettings.get("SOCKSEnable"))) { + String proxyHost = (String) proxySettings.get("SOCKSProxy"); + int proxyPort = (Integer) proxySettings.get("SOCKSPort"); + ps.setSelector("socks", new FixedSocksSelector(proxyHost, proxyPort)); + Logger.log(getClass(), LogLevel.TRACE, "OSX socks proxy is {0}:{1}", proxyHost, proxyPort); + } + } + + /************************************************************************* + * Installs a proxy selector for the given protocoll on the ProtocolDispatchSelector + * @param proxySettings to read the config for the procotol from. + * @param ps the ProtocolDispatchSelector to install the new selector on. + * @param protocol to use. + ************************************************************************/ + + private void installSelectorForProtocol(Dict proxySettings, + ProtocolDispatchSelector ps, String protocol) { + String prefix = protocol.trim(); + if (isActive(proxySettings.get(prefix+"Enable"))) { + String proxyHost = (String) proxySettings.get(prefix+"Proxy"); + int proxyPort = (Integer) proxySettings.get(prefix+"Port"); + FixedProxySelector fp = new FixedProxySelector(proxyHost, proxyPort); + ps.setSelector(protocol.toLowerCase(), fp); + Logger.log(getClass(), LogLevel.TRACE, "OSX uses for {0} the proxy {1}:{2}", protocol, proxyHost, proxyPort); + } + } + + /************************************************************************* + * Checks if the given value is set to "on". + * @param value the value to test. + * @return true if it is set else false. + ************************************************************************/ + + private boolean isActive(Object value) { + return Integer.valueOf(1).equals(value); + } + +} |