summaryrefslogtreecommitdiffstats
path: root/src/main/java/com/btr/proxy/search
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/btr/proxy/search')
-rw-r--r--src/main/java/com/btr/proxy/search/ProxySearch.java258
-rw-r--r--src/main/java/com/btr/proxy/search/ProxySearchStrategy.java22
-rw-r--r--src/main/java/com/btr/proxy/search/browser/firefox/FirefoxProfileSource.java23
-rw-r--r--src/main/java/com/btr/proxy/search/browser/firefox/FirefoxProxySearchStrategy.java267
-rw-r--r--src/main/java/com/btr/proxy/search/browser/firefox/FirefoxSettingParser.java79
-rw-r--r--src/main/java/com/btr/proxy/search/browser/firefox/LinuxFirefoxProfileSource.java46
-rw-r--r--src/main/java/com/btr/proxy/search/browser/firefox/WinFirefoxProfileSource.java71
-rw-r--r--src/main/java/com/btr/proxy/search/browser/ie/IELocalByPassFilter.java28
-rw-r--r--src/main/java/com/btr/proxy/search/browser/ie/IEProxySearchStrategy.java213
-rw-r--r--src/main/java/com/btr/proxy/search/desktop/DesktopProxySearchStrategy.java69
-rw-r--r--src/main/java/com/btr/proxy/search/desktop/gnome/GnomeProxySearchStrategy.java353
-rw-r--r--src/main/java/com/btr/proxy/search/desktop/gnome/ProxySchemasGSettingsAccess.java60
-rw-r--r--src/main/java/com/btr/proxy/search/desktop/kde/KdeProxySearchStrategy.java198
-rw-r--r--src/main/java/com/btr/proxy/search/desktop/kde/KdeSettingsParser.java132
-rw-r--r--src/main/java/com/btr/proxy/search/desktop/osx/OsxProxySearchStrategy.java325
-rw-r--r--src/main/java/com/btr/proxy/search/desktop/win/DLLManager.java171
-rw-r--r--src/main/java/com/btr/proxy/search/desktop/win/Win32IESettings.java68
-rw-r--r--src/main/java/com/btr/proxy/search/desktop/win/Win32ProxyUtils.java88
-rw-r--r--src/main/java/com/btr/proxy/search/desktop/win/WinProxySearchStrategy.java52
-rw-r--r--src/main/java/com/btr/proxy/search/env/EnvProxySearchStrategy.java130
-rw-r--r--src/main/java/com/btr/proxy/search/java/JavaProxySearchStrategy.java133
-rw-r--r--src/main/java/com/btr/proxy/search/wpad/WpadProxySearchStrategy.java234
-rw-r--r--src/main/java/com/btr/proxy/search/wpad/WpadProxySearchStrategyWithDHPC.java315
-rw-r--r--src/main/java/com/btr/proxy/search/wpad/dhcp/DHCPMessage.java880
-rw-r--r--src/main/java/com/btr/proxy/search/wpad/dhcp/DHCPOptions.java235
-rw-r--r--src/main/java/com/btr/proxy/search/wpad/dhcp/DHCPSocket.java107
26 files changed, 4557 insertions, 0 deletions
diff --git a/src/main/java/com/btr/proxy/search/ProxySearch.java b/src/main/java/com/btr/proxy/search/ProxySearch.java
new file mode 100644
index 0000000..19db95f
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/ProxySearch.java
@@ -0,0 +1,258 @@
+package com.btr.proxy.search;
+
+import java.awt.GraphicsEnvironment;
+import java.net.ProxySelector;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.btr.proxy.search.browser.firefox.FirefoxProxySearchStrategy;
+import com.btr.proxy.search.browser.ie.IEProxySearchStrategy;
+import com.btr.proxy.search.desktop.DesktopProxySearchStrategy;
+import com.btr.proxy.search.desktop.gnome.GnomeProxySearchStrategy;
+import com.btr.proxy.search.desktop.kde.KdeProxySearchStrategy;
+import com.btr.proxy.search.desktop.win.WinProxySearchStrategy;
+import com.btr.proxy.search.env.EnvProxySearchStrategy;
+import com.btr.proxy.search.java.JavaProxySearchStrategy;
+import com.btr.proxy.selector.misc.BufferedProxySelector;
+import com.btr.proxy.selector.misc.ProxyListFallbackSelector;
+import com.btr.proxy.selector.pac.PacProxySelector;
+import com.btr.proxy.util.Logger;
+import com.btr.proxy.util.PlatformUtil;
+import com.btr.proxy.util.ProxyException;
+import com.btr.proxy.util.Logger.LogBackEnd;
+import com.btr.proxy.util.Logger.LogLevel;
+
+/*****************************************************************************
+ * Main class to setup and initialize the proxy detection system.<br/>
+ * This class can be used to select a proxy discovery strategy.<br/>
+ * Implements the "Builder" pattern.<br/>
+ * Use <code>addStrategy</code> to add one or more search strategies.<br/>
+ * If you are done call the <code>getProxySelector</code> method. <br/>
+ * Then the strategies are asked one after the other for a ProxySelector until
+ * an valid selector is found. <br/>
+ * <p>
+ * Invoke the static <code>getDefaultProxySearch</code> method to use a default search strategy.
+ * </p>
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class ProxySearch implements ProxySearchStrategy {
+
+ private static final int DEFAULT_PAC_CACHE_SIZE = 20;
+
+ private static final long DEFAULT_PAC_CACHE_TTL = 1000*60*10; // 10 Minutes
+
+ private List<ProxySearchStrategy> strategies;
+ private int pacCacheSize;
+ private long pacCacheTTL;
+
+ /*****************************************************************************
+ * Types of proxy detection supported by the builder.
+ ****************************************************************************/
+
+ public enum Strategy {
+ /// Use the platform settings.
+ OS_DEFAULT,
+ /// Use the settings of the platforms default browser.
+ BROWSER,
+ /// Use Firefox settings
+ FIREFOX,
+ /// Use InternetExplorer settings
+ IE,
+ /// Use environment variables for proxy settings.
+ ENV_VAR,
+ /// Use windows default proxy settings.
+ WIN,
+ /// Use KDE desktop default proxy settings.
+ KDE,
+ /// Use KDE desktop default proxy settings.
+ GNOME,
+ /// Use Java Networking system properties
+ JAVA
+ }
+
+ /*************************************************************************
+ * Constructor
+ ************************************************************************/
+
+ public ProxySearch() {
+ super();
+ this.strategies = new ArrayList<ProxySearchStrategy>();
+ this.pacCacheSize = DEFAULT_PAC_CACHE_SIZE;
+ this.pacCacheTTL = DEFAULT_PAC_CACHE_TTL;
+ }
+
+ /*************************************************************************
+ * Sets up a ProxySearch that uses a default search strategy suitable for
+ * every platform.
+ * @return a ProxySearch initialized with default settings.
+ ************************************************************************/
+
+ public static ProxySearch getDefaultProxySearch() {
+ ProxySearch s = new ProxySearch();
+
+ // Test if we are a server or a client.
+ boolean headless = GraphicsEnvironment.isHeadless();
+
+ if (headless) {
+ s.addStrategy(Strategy.JAVA);
+ s.addStrategy(Strategy.OS_DEFAULT);
+ s.addStrategy(Strategy.ENV_VAR);
+ } else {
+ s.addStrategy(Strategy.JAVA);
+ s.addStrategy(Strategy.BROWSER);
+ s.addStrategy(Strategy.OS_DEFAULT);
+ s.addStrategy(Strategy.ENV_VAR);
+ }
+ Logger.log(ProxySearch.class, LogLevel.TRACE, "Using default search priority: {0}", s);
+
+ return s;
+ }
+
+ /*************************************************************************
+ * Adds an search strategy to the list of proxy searches strategies.
+ * @param strategy the search strategy to add.
+ ************************************************************************/
+
+ public void addStrategy(Strategy strategy) {
+ switch (strategy) {
+ case OS_DEFAULT:
+ this.strategies.add(new DesktopProxySearchStrategy());
+ break;
+ case BROWSER:
+ this.strategies.add(getDefaultBrowserStrategy());
+ break;
+ case FIREFOX:
+ this.strategies.add(new FirefoxProxySearchStrategy());
+ break;
+ case IE:
+ this.strategies.add(new IEProxySearchStrategy());
+ break;
+ case ENV_VAR:
+ this.strategies.add(new EnvProxySearchStrategy());
+ break;
+ case WIN:
+ this.strategies.add(new WinProxySearchStrategy());
+ break;
+ case KDE:
+ this.strategies.add(new KdeProxySearchStrategy());
+ break;
+ case GNOME:
+ this.strategies.add(new GnomeProxySearchStrategy());
+ break;
+ case JAVA:
+ this.strategies.add(new JavaProxySearchStrategy());
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown strategy code!");
+ }
+ }
+
+ /*************************************************************************
+ * Sets the cache size of the PAC proxy selector cache.
+ * This defines the number of URLs that are cached together with the PAC
+ * script result. This improves performance because for URLs that are
+ * in the cache the script is not executed again.
+ * You have to set this before you add any strategies that may create a
+ * PAC script proxy selector.
+ * @param size of the cache. Set it to 0 to disable caching.
+ * @param ttl is the time to live of the cache entries as amount of milliseconds.
+ ************************************************************************/
+
+ public void setPacCacheSettings(int size, long ttl) {
+ this.pacCacheSize = size;
+ this.pacCacheTTL = ttl;
+ }
+
+ /*************************************************************************
+ * Gets the search strategy for the platforms default browser.
+ * @return a ProxySearchStrategy, null if no supported browser was found.
+ ************************************************************************/
+
+ private ProxySearchStrategy getDefaultBrowserStrategy() {
+ switch (PlatformUtil.getDefaultBrowser()) {
+ case IE:
+ return new IEProxySearchStrategy();
+ case FIREFOX:
+ return new FirefoxProxySearchStrategy();
+ }
+ return null;
+ }
+
+ /*************************************************************************
+ * Gets the proxy selector that will use the configured search order.
+ * @return a ProxySelector, null if none was found for the current
+ * builder configuration.
+ ************************************************************************/
+
+ public ProxySelector getProxySelector() {
+ Logger.log(getClass(), LogLevel.TRACE, "Executing search strategies to find proxy selector");
+ for (ProxySearchStrategy strat : this.strategies) {
+ try {
+ ProxySelector selector = strat.getProxySelector();
+ if (selector != null) {
+ selector = installBufferingAndFallbackBehaviour(selector);
+ return selector;
+ }
+ } catch (ProxyException e) {
+ Logger.log(getClass(), LogLevel.DEBUG, "Strategy {0} failed trying next one.", e);
+ // Ignore and try next strategy.
+ }
+ }
+
+ return null;
+ }
+
+ /*************************************************************************
+ * If it is PAC and we have caching enabled set it here.
+ * @param selector
+ * @return
+ ************************************************************************/
+
+ private ProxySelector installBufferingAndFallbackBehaviour(ProxySelector selector) {
+ if (selector instanceof PacProxySelector) {
+ if (this.pacCacheSize > 0) {
+ selector = new BufferedProxySelector(this.pacCacheSize, this.pacCacheTTL, selector);
+ }
+ selector = new ProxyListFallbackSelector(selector);
+ }
+ return selector;
+ }
+
+ /*************************************************************************
+ * toString
+ * @see java.lang.Object#toString()
+ ************************************************************************/
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("Proxy search: ");
+ for (ProxySearchStrategy strat : this.strategies) {
+ sb.append(strat);
+ sb.append(" ");
+ }
+ return sb.toString();
+ }
+
+ /*************************************************************************
+ * For testing only. Will print the logging & proxy information to the console.
+ * @param args the command line arguments.
+ ************************************************************************/
+
+ public static void main(String[] args) {
+ ProxySearch ps = ProxySearch.getDefaultProxySearch();
+ Logger.setBackend(new LogBackEnd() {
+
+ public void log(Class<?> clazz, LogLevel loglevel, String msg,
+ Object... params) {
+ System.out.println(MessageFormat.format(msg, params));
+ }
+
+ public boolean isLogginEnabled(LogLevel logLevel) {
+ return true;
+ }
+ });
+ ps.getProxySelector();
+ }
+
+}
diff --git a/src/main/java/com/btr/proxy/search/ProxySearchStrategy.java b/src/main/java/com/btr/proxy/search/ProxySearchStrategy.java
new file mode 100644
index 0000000..6f14c28
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/ProxySearchStrategy.java
@@ -0,0 +1,22 @@
+package com.btr.proxy.search;
+
+import java.net.ProxySelector;
+
+import com.btr.proxy.util.ProxyException;
+
+/*****************************************************************************
+ * Interface for a proxy search strategy.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public interface ProxySearchStrategy {
+
+ /*************************************************************************
+ * Gets the a ProxySelector found by applying the search strategy.
+ * @return a ProxySelector, null if none is found.
+ ************************************************************************/
+
+ public ProxySelector getProxySelector() throws ProxyException;
+
+}
diff --git a/src/main/java/com/btr/proxy/search/browser/firefox/FirefoxProfileSource.java b/src/main/java/com/btr/proxy/search/browser/firefox/FirefoxProfileSource.java
new file mode 100644
index 0000000..8517074
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/browser/firefox/FirefoxProfileSource.java
@@ -0,0 +1,23 @@
+package com.btr.proxy.search.browser.firefox;
+
+import java.io.File;
+import java.io.IOException;
+
+/*****************************************************************************
+ * A profile source for Firefox profiles.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+interface FirefoxProfileSource {
+
+ /*************************************************************************
+ * Gets a profile folder found on the current system.
+ * If multiple profile folders are available the "default" profile is chosen.
+ * @return a profile folder.
+ * @throws IOException on error.
+ ************************************************************************/
+
+ public File getProfileFolder() throws IOException;
+
+}
diff --git a/src/main/java/com/btr/proxy/search/browser/firefox/FirefoxProxySearchStrategy.java b/src/main/java/com/btr/proxy/search/browser/firefox/FirefoxProxySearchStrategy.java
new file mode 100644
index 0000000..30c3be1
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/browser/firefox/FirefoxProxySearchStrategy.java
@@ -0,0 +1,267 @@
+package com.btr.proxy.search.browser.firefox;
+
+import java.io.IOException;
+import java.net.ProxySelector;
+import java.util.Properties;
+
+import com.btr.proxy.search.ProxySearchStrategy;
+import com.btr.proxy.search.desktop.DesktopProxySearchStrategy;
+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.PlatformUtil;
+import com.btr.proxy.util.PlatformUtil.Platform;
+import com.btr.proxy.util.ProxyException;
+import com.btr.proxy.util.ProxyUtil;
+
+/*****************************************************************************
+ * Loads the Firefox3 proxy settings from the users Firefox3 settings.
+ * This will load the file <i>prefs.js</i> that is located in the
+ * <p>
+ * <i>.mozilla/firefox/(profile)/</i> folder.
+ * </p>
+ *
+ * See <a href="https://developer.mozilla.org/En/Mozilla_Networking_Preferences">Mozilla_Networking_Preferences</a>
+ * for an explanation of the proxy settings.
+ * <p>
+ * The following settings are extracted from
+ * this file:
+ * </p>
+ * Some generic settings:<br/>
+ * <ul>
+ * <li><i>network.proxy.type</i> -> n/a = use system settings, 0 = direct, 1 = Fixed proxy settings, 2 = proxy script (PAC), 3 = also direct , 4 = auto detect (WPAD)</li>
+ * <li><i>network.proxy.share_proxy_settings</i> -> true = use same proxy for all protocols</li>
+ * <li><i>network.proxy.no_proxies_on</i> -> a comma separated ignore list. </li>
+ * <li><i>network.proxy.autoconfig_url</i> -> a URL to an proxy configuration script</li>
+ * </ul>
+ * Host names and ports per protocol are stored in the following settings:
+ * <ul>
+ * <li><i>network.proxy.http</i></li>
+ * <li><i>network.proxy.http_port</i></li>
+ * <li><i>network.proxy.ssl</i></li>
+ * <li><i>network.proxy.ssl_port</i></li>
+ * <li><i>network.proxy.ftp</i></li>
+ * <li><i>network.proxy.ftp_port</i></li>
+ * <li><i>network.proxy.gopher</i></li>
+ * <li><i>network.proxy.gopher_port</i></li>
+ * <li><i>network.proxy.socks</i></li>
+ * <li><i>network.proxy.socks_port</i></li>
+ * <li><i>network.proxy.socks_version</i> -> 4 or 5</li>
+ * </u>
+ * <p>
+ * Note that if there are more than one profile the first profile found will be used.
+ * </p>
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class FirefoxProxySearchStrategy implements ProxySearchStrategy {
+
+ private FirefoxProfileSource profileScanner;
+ private FirefoxSettingParser settingsParser;
+
+ /*************************************************************************
+ * ProxySelector
+ * @see java.net.ProxySelector#ProxySelector()
+ ************************************************************************/
+
+ public FirefoxProxySearchStrategy() {
+ super();
+ if (PlatformUtil.getCurrentPlattform() == Platform.WIN) {
+ this.profileScanner = new WinFirefoxProfileSource();
+ } else {
+ this.profileScanner = new LinuxFirefoxProfileSource();
+ }
+ this.settingsParser = new FirefoxSettingParser();
+ }
+
+ /*************************************************************************
+ * Loads the proxy settings and initializes a proxy selector for the firefox
+ * 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 Firefox settings.");
+
+ Properties settings = readSettings();
+
+ ProxySelector result = null;
+ int type = Integer.parseInt(settings.getProperty("network.proxy.type", "-1"));
+ switch (type) {
+ case -1: // Use system settings
+ Logger.log(getClass(), LogLevel.TRACE, "Firefox uses system settings");
+ result = new DesktopProxySearchStrategy().getProxySelector();
+ break;
+ case 0: // Use no proxy
+ Logger.log(getClass(), LogLevel.TRACE, "Firefox uses no proxy");
+ result = NoProxySelector.getInstance();
+ break;
+ case 1: // Fixed settings
+ Logger.log(getClass(), LogLevel.TRACE, "Firefox uses manual settings");
+ result = setupFixedProxySelector(settings);
+ break;
+ case 2: // PAC Script
+ String pacScriptUrl = settings.getProperty("network.proxy.autoconfig_url", "");
+ Logger.log(getClass(), LogLevel.TRACE, "Firefox uses script (PAC) {0}", pacScriptUrl);
+ result = ProxyUtil.buildPacSelectorForUrl(pacScriptUrl);
+ break;
+ case 3: // Backward compatibility to netscape.
+ Logger.log(getClass(), LogLevel.TRACE, "Netscape compability mode -> uses no proxy");
+ result = NoProxySelector.getInstance();
+ break;
+ case 4: // WPAD auto config
+ Logger.log(getClass(), LogLevel.TRACE, "Firefox uses automatic detection (WPAD)");
+ result = new WpadProxySearchStrategy().getProxySelector();
+ break;
+ default:
+ break;
+ }
+
+ // Wrap in white list filter.
+ String noProxyList = settings.getProperty("network.proxy.no_proxies_on", null);
+ if (result != null && noProxyList != null && noProxyList.trim().length() > 0) {
+ Logger.log(getClass(), LogLevel.TRACE, "Firefox uses proxy bypass list for: {0}", noProxyList);
+ result = new ProxyBypassListSelector(noProxyList, result);
+ }
+
+ return result;
+ }
+
+ /*************************************************************************
+ * Reads the settings file and stores all settings in a Properties map.
+ * @return the parsed settings.
+ * @throws ProxyException on read error.
+ ************************************************************************/
+
+ public Properties readSettings() throws ProxyException {
+ try {
+ Properties settings = this.settingsParser.parseSettings(this.profileScanner);
+ return settings;
+ } catch (IOException e) {
+ Logger.log(getClass(), LogLevel.ERROR, "Error parsing settings", e);
+ throw new ProxyException(e);
+ }
+ }
+
+ /*************************************************************************
+ * Parse the fixed proxy settings and build an ProxySelector for this a
+ * chained configuration.
+ * @param settings the proxy settings to evaluate.
+ ************************************************************************/
+
+ private ProxySelector setupFixedProxySelector(Properties settings) {
+ ProtocolDispatchSelector ps = new ProtocolDispatchSelector();
+ installHttpProxy(ps, settings);
+ if (isProxyShared(settings)) {
+ installSharedProxy(ps);
+ } else {
+ installFtpProxy(ps, settings);
+ installSecureProxy(ps, settings);
+ installSocksProxy(ps, settings);
+ }
+ return ps;
+ }
+
+ /*************************************************************************
+ * @param ps
+ * @param settings
+ * @throws NumberFormatException
+ ************************************************************************/
+
+ private void installFtpProxy(ProtocolDispatchSelector ps,
+ Properties settings) throws NumberFormatException {
+ installSelectorForProtocol(ps, settings, "ftp");
+ }
+
+ /*************************************************************************
+ * @param ps
+ * @param settings
+ * @throws NumberFormatException
+ ************************************************************************/
+
+ private void installHttpProxy(ProtocolDispatchSelector ps,
+ Properties settings) throws NumberFormatException {
+ installSelectorForProtocol(ps, settings, "http");
+ }
+
+ /*************************************************************************
+ * @param settings
+ * @return
+ ************************************************************************/
+
+ private boolean isProxyShared(Properties settings) {
+ return Boolean.TRUE.toString().equals(settings.getProperty("network.proxy.share_proxy_settings", "false").toLowerCase());
+ }
+
+ /*************************************************************************
+ * @param ps
+ ************************************************************************/
+
+ private void installSharedProxy(ProtocolDispatchSelector ps) {
+ ProxySelector httpProxy = ps.getSelector("http");
+ if (httpProxy != null) {
+ ps.setFallbackSelector(httpProxy);
+ }
+ }
+
+ /*************************************************************************
+ * @param ps
+ * @param settings
+ * @throws NumberFormatException
+ ************************************************************************/
+
+ private void installSocksProxy(ProtocolDispatchSelector ps,
+ Properties settings) throws NumberFormatException {
+ String proxyHost = settings.getProperty("network.proxy.socks", null);
+ int proxyPort = Integer.parseInt(settings.getProperty("network.proxy.socks_port", "0"));
+ if (proxyHost != null && proxyPort != 0) {
+ Logger.log(getClass(), LogLevel.TRACE, "Firefox socks proxy is {0}:{1}", proxyHost, proxyPort);
+ ps.setSelector("socks", new FixedSocksSelector(proxyHost, proxyPort));
+ }
+ }
+
+ /*************************************************************************
+ * @param ps
+ * @param settings
+ * @throws NumberFormatException
+ ************************************************************************/
+
+ private void installSecureProxy(ProtocolDispatchSelector ps,
+ Properties settings) throws NumberFormatException {
+ String proxyHost = settings.getProperty("network.proxy.ssl", null);
+ int proxyPort = Integer.parseInt(settings.getProperty("network.proxy.ssl_port", "0"));
+ if (proxyHost != null && proxyPort != 0) {
+ Logger.log(getClass(), LogLevel.TRACE, "Firefox secure proxy is {0}:{1}", proxyHost, proxyPort);
+ ps.setSelector("https", new FixedProxySelector(proxyHost, proxyPort));
+ ps.setSelector("sftp", new FixedProxySelector(proxyHost, proxyPort));
+ }
+ }
+
+ /*************************************************************************
+ * Installs a proxy selector for the given protocol when settings are
+ * available.
+ * @param ps a ProtocolDispatchSelector to configure.
+ * @param settings to read the config from.
+ * @param protocol to configure.
+ * @throws NumberFormatException
+ ************************************************************************/
+
+ private void installSelectorForProtocol(ProtocolDispatchSelector ps,
+ Properties settings, String protocol) throws NumberFormatException {
+
+ String proxyHost = settings.getProperty("network.proxy."+protocol, null);
+ int proxyPort = Integer.parseInt(settings.getProperty("network.proxy."+protocol+"_port", "0"));
+ if (proxyHost != null && proxyPort != 0) {
+ Logger.log(getClass(), LogLevel.TRACE, "Firefox "+protocol+" proxy is {0}:{1}", proxyHost, proxyPort);
+ ps.setSelector(protocol, new FixedProxySelector(proxyHost, proxyPort));
+ }
+ }
+
+
+}
diff --git a/src/main/java/com/btr/proxy/search/browser/firefox/FirefoxSettingParser.java b/src/main/java/com/btr/proxy/search/browser/firefox/FirefoxSettingParser.java
new file mode 100644
index 0000000..b793299
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/browser/firefox/FirefoxSettingParser.java
@@ -0,0 +1,79 @@
+package com.btr.proxy.search.browser.firefox;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.Properties;
+
+/*****************************************************************************
+ * Parser for the Firefox settings file.
+ * Will extract all relevant proxy settings form the configuration file.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+class FirefoxSettingParser {
+
+ /*************************************************************************
+ * Constructor
+ ************************************************************************/
+
+ public FirefoxSettingParser() {
+ super();
+ }
+
+ /*************************************************************************
+ * Parse the settings file and extract all network.proxy.* settings from it.
+ * @param source of the Firefox profiles.
+ * @return the parsed properties.
+ * @throws IOException on read error.
+ ************************************************************************/
+
+ public Properties parseSettings(FirefoxProfileSource source) throws IOException {
+ // Search settings folder
+ File profileFolder = source.getProfileFolder();
+
+ // Read settings from file
+ File settingsFile = new File(profileFolder, "prefs.js");
+
+ BufferedReader fin = new BufferedReader(
+ new InputStreamReader(
+ new FileInputStream(settingsFile)));
+
+ Properties result = new Properties();
+ try {
+ String line = fin.readLine();
+ while (line != null) {
+ line = line.trim();
+ if (line.startsWith("user_pref(\"network.proxy")) {
+ line = line.substring(10, line.length()-2);
+ int index = line.indexOf(",");
+ String key = line.substring(0, index).trim();
+ if (key.startsWith("\"")) {
+ key = key.substring(1);
+ }
+ if (key.endsWith("\"")) {
+ key = key.substring(0, key.length()-1);
+ }
+ String value = line.substring(index+1).trim();
+ if (value.startsWith("\"")) {
+ value = value.substring(1);
+ }
+ if (value.endsWith("\"")) {
+ value = value.substring(0, value.length()-1);
+ }
+ result.put(key, value);
+ }
+ line = fin.readLine();
+ }
+ } finally {
+ fin.close();
+ }
+
+ return result;
+ }
+
+
+}
diff --git a/src/main/java/com/btr/proxy/search/browser/firefox/LinuxFirefoxProfileSource.java b/src/main/java/com/btr/proxy/search/browser/firefox/LinuxFirefoxProfileSource.java
new file mode 100644
index 0000000..38b1553
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/browser/firefox/LinuxFirefoxProfileSource.java
@@ -0,0 +1,46 @@
+package com.btr.proxy.search.browser.firefox;
+
+import java.io.File;
+
+import com.btr.proxy.util.Logger;
+import com.btr.proxy.util.Logger.LogLevel;
+
+/*****************************************************************************
+ * Searches for Firefox profile on an Linux / Unix base system.
+ * This will scan the <i>.mozilla</i> folder in the users home directory to find the
+ * profiles.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+class LinuxFirefoxProfileSource implements FirefoxProfileSource {
+
+ /*************************************************************************
+ * Get profile folder for the Linux Firefox profile
+ ************************************************************************/
+
+ public File getProfileFolder() {
+ File userDir = new File(System.getProperty("user.home"));
+ File cfgDir = new File(userDir, ".mozilla"+File.separator+"firefox"+File.separator);
+ if (!cfgDir.exists()) {
+ Logger.log(getClass(), LogLevel.DEBUG, "Firefox settings folder not found!");
+ return null;
+ }
+ File[] profiles = cfgDir.listFiles();
+ if (profiles == null || profiles.length == 0) {
+ Logger.log(getClass(), LogLevel.DEBUG, "Firefox settings folder not found!");
+ return null;
+ }
+ for (File p : profiles) {
+ if (p.getName().endsWith(".default")) {
+ Logger.log(getClass(), LogLevel.TRACE, "Firefox settings folder is {0}", p);
+ return p;
+ }
+ }
+
+ // Fall back -> take the first one found.
+ Logger.log(getClass(), LogLevel.TRACE, "Firefox settings folder is {0}", profiles[0]);
+ return profiles[0];
+ }
+
+}
diff --git a/src/main/java/com/btr/proxy/search/browser/firefox/WinFirefoxProfileSource.java b/src/main/java/com/btr/proxy/search/browser/firefox/WinFirefoxProfileSource.java
new file mode 100644
index 0000000..9189585
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/browser/firefox/WinFirefoxProfileSource.java
@@ -0,0 +1,71 @@
+package com.btr.proxy.search.browser.firefox;
+
+import java.io.File;
+import java.io.IOException;
+
+import com.btr.proxy.search.desktop.win.Win32ProxyUtils;
+import com.btr.proxy.util.Logger;
+import com.btr.proxy.util.Logger.LogLevel;
+
+/*****************************************************************************
+ * Finds the Firefox profile on Windows platforms.
+ * On Windows the profiles are located in the users appdata directory under:
+ * <p>
+ * <i>Mozilla\Firefox\Profiles\</i>
+ * </p>
+ * The location of the appdata folder is read from the windows registry.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+class WinFirefoxProfileSource implements FirefoxProfileSource {
+
+ /*************************************************************************
+ * Constructor
+ ************************************************************************/
+
+ public WinFirefoxProfileSource() {
+ super();
+ }
+
+ /*************************************************************************
+ * Reads the current location of the app data folder from the registry.
+ * @return a path to the folder.
+ ************************************************************************/
+
+ private String getAppFolder() {
+ return new Win32ProxyUtils().readUserHomedir();
+ }
+
+ /*************************************************************************
+ * Get profile folder for the Windows Firefox profile
+ * @throws IOException on error.
+ ************************************************************************/
+
+ public File getProfileFolder() throws IOException {
+
+ File appDataDir = new File(getAppFolder());
+ File cfgDir = new File(appDataDir, "Mozilla"+File.separator+"Firefox"+File.separator+"Profiles");
+
+ if (!cfgDir.exists()) {
+ Logger.log(getClass(), LogLevel.DEBUG, "Firefox windows settings folder not found.");
+ return null;
+ }
+ File[] profiles = cfgDir.listFiles();
+ if (profiles == null || profiles.length == 0) {
+ Logger.log(getClass(), LogLevel.DEBUG, "Firefox windows settings folder not found.");
+ return null;
+ }
+ for (File p : profiles) {
+ if (p.getName().endsWith(".default")) {
+ Logger.log(getClass(), LogLevel.TRACE, "Firefox windows settings folder is {0}.", p);
+ return p;
+ }
+ }
+
+ // Fall back -> take the first one found.
+ Logger.log(getClass(), LogLevel.TRACE, "Firefox windows settings folder is {0}.", profiles[0]);
+ return profiles[0];
+ }
+
+}
diff --git a/src/main/java/com/btr/proxy/search/browser/ie/IELocalByPassFilter.java b/src/main/java/com/btr/proxy/search/browser/ie/IELocalByPassFilter.java
new file mode 100644
index 0000000..fb81d6f
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/browser/ie/IELocalByPassFilter.java
@@ -0,0 +1,28 @@
+package com.btr.proxy.search.browser.ie;
+
+import java.net.URI;
+
+import com.btr.proxy.util.UriFilter;
+
+/*****************************************************************************
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class IELocalByPassFilter implements UriFilter {
+
+ /*************************************************************************
+ * accept
+ * @see com.btr.proxy.util.UriFilter#accept(java.net.URI)
+ ************************************************************************/
+
+ public boolean accept(URI uri) {
+ if (uri == null) {
+ return false;
+ }
+ String host = uri.getAuthority();
+ return host != null && !host.contains(".");
+ }
+
+}
+
diff --git a/src/main/java/com/btr/proxy/search/browser/ie/IEProxySearchStrategy.java b/src/main/java/com/btr/proxy/search/browser/ie/IEProxySearchStrategy.java
new file mode 100644
index 0000000..2a9b078
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/browser/ie/IEProxySearchStrategy.java
@@ -0,0 +1,213 @@
+package com.btr.proxy.search.browser.ie;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.net.ProxySelector;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+import com.btr.proxy.search.ProxySearchStrategy;
+import com.btr.proxy.search.desktop.win.Win32IESettings;
+import com.btr.proxy.search.desktop.win.Win32ProxyUtils;
+import com.btr.proxy.selector.fixed.FixedProxySelector;
+import com.btr.proxy.selector.misc.ProtocolDispatchSelector;
+import com.btr.proxy.selector.pac.PacProxySelector;
+import com.btr.proxy.selector.pac.UrlPacScriptSource;
+import com.btr.proxy.selector.whitelist.ProxyBypassListSelector;
+import com.btr.proxy.util.Logger;
+import com.btr.proxy.util.ProxyException;
+import com.btr.proxy.util.ProxyUtil;
+import com.btr.proxy.util.Logger.LogLevel;
+import com.btr.proxy.util.UriFilter;
+
+/*****************************************************************************
+ * Extracts the proxy settings for Microsoft Internet Explorer.
+ * The settings are read by invoking native Windows API methods.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class IEProxySearchStrategy implements ProxySearchStrategy {
+
+ /*************************************************************************
+ * getProxySelector
+ * @see com.btr.proxy.search.ProxySearchStrategy#getProxySelector()
+ ************************************************************************/
+
+ public ProxySelector getProxySelector() throws ProxyException {
+
+ Logger.log(getClass(), LogLevel.TRACE, "Detecting IE proxy settings");
+
+ Win32IESettings ieSettings = readSettings();
+
+ ProxySelector result = createPacSelector(ieSettings);
+ if (result == null) {
+ result = createFixedProxySelector(ieSettings);
+ }
+ return result;
+ }
+
+ /*************************************************************************
+ * Loads the settings from the windows registry.
+ * @return WinIESettings containing all proxy settings.
+ ************************************************************************/
+
+ public Win32IESettings readSettings() {
+ Win32IESettings ieSettings = new Win32ProxyUtils().winHttpGetIEProxyConfigForCurrentUser();
+ return ieSettings;
+ }
+
+ /*************************************************************************
+ * Parses the settings and creates an PAC ProxySelector for it.
+ * @param ieSettings the IE settings to use.
+ * @return a PacProxySelector the selector or null.
+ ************************************************************************/
+
+ private PacProxySelector createPacSelector(Win32IESettings ieSettings) {
+ String pacUrl = null;
+
+ if (ieSettings.isAutoDetect()) {
+ Logger.log(getClass(), LogLevel.TRACE, "Autodetecting script URL.");
+ // This will take some time.
+ pacUrl = new Win32ProxyUtils().winHttpDetectAutoProxyConfigUrl(
+ Win32ProxyUtils.WINHTTP_AUTO_DETECT_TYPE_DHCP+
+ Win32ProxyUtils.WINHTTP_AUTO_DETECT_TYPE_DNS_A);
+ }
+ if (pacUrl == null) {
+ pacUrl = ieSettings.getAutoConfigUrl();
+ }
+ if (pacUrl != null && pacUrl.trim().length() > 0) {
+ Logger.log(getClass(), LogLevel.TRACE, "IE uses script: "+pacUrl);
+
+ // Fix for issue 9
+ // If the IE has a file URL and it only starts has 2 slashes,
+ // add a third so it can be properly converted to the URL class
+ if (pacUrl.startsWith("file://") && !pacUrl.startsWith("file:///")) {
+ pacUrl = "file:///" + pacUrl.substring(7);
+ }
+ return ProxyUtil.buildPacSelectorForUrl(pacUrl);
+ }
+
+ return null;
+ }
+
+ /*************************************************************************
+ * Parses the proxy settings into an ProxySelector.
+ * @param ieSettings the settings to use.
+ * @return a ProxySelector, null if no settings are set.
+ * @throws ProxyException on error.
+ ************************************************************************/
+
+ private ProxySelector createFixedProxySelector(Win32IESettings ieSettings) throws ProxyException {
+ String proxyString = ieSettings.getProxy();
+ String bypassList = ieSettings.getProxyBypass();
+ if (proxyString == null) {
+ return null;
+ }
+ Logger.log(getClass(), LogLevel.TRACE,
+ "IE uses manual settings: {0} with bypass list: {1}", proxyString, bypassList);
+
+ Properties p = parseProxyList(proxyString);
+
+ ProtocolDispatchSelector ps = new ProtocolDispatchSelector();
+ addSelectorForProtocol(p, "http", ps);
+ addSelectorForProtocol(p, "https", ps);
+ addSelectorForProtocol(p, "ftp", ps);
+ addSelectorForProtocol(p, "gopher", ps);
+ addSelectorForProtocol(p, "socks", ps);
+ addFallbackSelector(p, ps);
+
+ ProxySelector result = setByPassListOnSelector(bypassList, ps);
+ return result;
+ }
+
+ /*************************************************************************
+ * @param bypassList
+ * @param ps
+ * @return
+ ************************************************************************/
+
+ private ProxySelector setByPassListOnSelector(String bypassList,
+ ProtocolDispatchSelector ps) {
+ if (bypassList != null && bypassList.trim().length() > 0) {
+ ProxyBypassListSelector result;
+ if ("<local>".equals(bypassList.trim())) {
+ result = buildLocalBypassSelector(ps);
+ } else {
+ bypassList = bypassList.replace(';', ',');
+ result = new ProxyBypassListSelector(bypassList, ps);
+ }
+ return result;
+ }
+ return ps;
+ }
+
+ /*************************************************************************
+ * @param ps
+ * @return
+ ************************************************************************/
+
+ private ProxyBypassListSelector buildLocalBypassSelector(
+ ProtocolDispatchSelector ps) {
+ List<UriFilter> localBypassFilter = new ArrayList<UriFilter>();
+ localBypassFilter.add(new IELocalByPassFilter());
+ return new ProxyBypassListSelector(localBypassFilter, ps);
+ }
+
+ /*************************************************************************
+ * Installs a fallback selector that is used whenever no protocol specific
+ * selector is defined.
+ * @param settings to take the proxy settings from.
+ * @param ps to install the created selector on.
+ ************************************************************************/
+
+ private void addFallbackSelector(Properties settings, ProtocolDispatchSelector ps) {
+ String proxy = settings.getProperty("default");
+ if (proxy != null) {
+ ps.setFallbackSelector(ProxyUtil.parseProxySettings(proxy));
+ }
+ }
+
+ /*************************************************************************
+ * Creates a selector for a given protocol. The proxy will be taken
+ * from the settings and installed on the dispatch selector.
+ * @param settings to take the proxy settings from.
+ * @param protocol to create a selector for.
+ * @param ps to install the created selector on.
+ ************************************************************************/
+
+ private void addSelectorForProtocol(Properties settings, String protocol, ProtocolDispatchSelector ps) {
+ String proxy = settings.getProperty(protocol);
+ if (proxy != null) {
+ FixedProxySelector protocolSelector = ProxyUtil.parseProxySettings(proxy);
+ ps.setSelector(protocol, protocolSelector);
+ }
+ }
+
+ /*************************************************************************
+ * Parses the proxy list and splits it by protocol.
+ * @param proxyString the proxy list string
+ * @return Properties with separated settings.
+ * @throws ProxyException on parse error.
+ ************************************************************************/
+
+ private Properties parseProxyList(String proxyString) throws ProxyException {
+ Properties p = new Properties();
+ if (proxyString.indexOf('=') == -1) {
+ p.setProperty("default", proxyString);
+ } else {
+ try {
+ proxyString = proxyString.replace(';', '\n');
+ p.load(new ByteArrayInputStream(proxyString.getBytes("ISO-8859-1")));
+ } catch (IOException e) {
+ Logger.log(getClass(), LogLevel.ERROR,
+ "Error reading IE settings as properties: {0}", e);
+
+ throw new ProxyException(e);
+ }
+ }
+ return p;
+ }
+
+}
diff --git a/src/main/java/com/btr/proxy/search/desktop/DesktopProxySearchStrategy.java b/src/main/java/com/btr/proxy/search/desktop/DesktopProxySearchStrategy.java
new file mode 100644
index 0000000..8a5a5d8
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/desktop/DesktopProxySearchStrategy.java
@@ -0,0 +1,69 @@
+package com.btr.proxy.search.desktop;
+
+import java.net.ProxySelector;
+
+import com.btr.proxy.search.ProxySearchStrategy;
+import com.btr.proxy.search.desktop.gnome.GnomeProxySearchStrategy;
+import com.btr.proxy.search.desktop.kde.KdeProxySearchStrategy;
+import com.btr.proxy.search.desktop.osx.OsxProxySearchStrategy;
+import com.btr.proxy.search.desktop.win.WinProxySearchStrategy;
+import com.btr.proxy.util.Logger;
+import com.btr.proxy.util.PlatformUtil;
+import com.btr.proxy.util.ProxyException;
+import com.btr.proxy.util.Logger.LogLevel;
+import com.btr.proxy.util.PlatformUtil.Desktop;
+import com.btr.proxy.util.PlatformUtil.Platform;
+
+/*****************************************************************************
+ * This search provider will try to find out on which desktop platform we
+ * are running and then will initialize the default proxy search.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class DesktopProxySearchStrategy implements ProxySearchStrategy {
+
+ /*************************************************************************
+ * Gets the default ProxySelector for the current platform.
+ * @return a ProxySelector, null if none is found.
+ * @throws ProxyException on error.
+ ************************************************************************/
+
+ public ProxySelector getProxySelector() throws ProxyException {
+ ProxySearchStrategy strategy = findDesktopSpecificStrategy();
+ return strategy == null? null : strategy.getProxySelector();
+ }
+
+ /*************************************************************************
+ * Determine the desktop and create a strategy for it.
+ * @return a desktop specific strategy, null if none was found.
+ ************************************************************************/
+
+ private ProxySearchStrategy findDesktopSpecificStrategy() {
+ Platform pf = PlatformUtil.getCurrentPlattform();
+ Desktop dt = PlatformUtil.getCurrentDesktop();
+
+ Logger.log(getClass(), LogLevel.TRACE, "Detecting system settings.");
+
+ ProxySearchStrategy strategy = null;
+
+ if (pf == Platform.WIN) {
+ Logger.log(getClass(), LogLevel.TRACE, "We are running on Windows.");
+ strategy = new WinProxySearchStrategy();
+ } else
+ if (dt == Desktop.KDE) {
+ Logger.log(getClass(), LogLevel.TRACE, "We are running on KDE.");
+ strategy = new KdeProxySearchStrategy();
+ } else
+ if (dt == Desktop.GNOME) {
+ Logger.log(getClass(), LogLevel.TRACE, "We are running on Gnome.");
+ strategy = new GnomeProxySearchStrategy();
+ } else
+ if (dt == Desktop.MAC_OS) {
+ Logger.log(getClass(), LogLevel.TRACE, "We are running on Mac OSX.");
+ strategy = new OsxProxySearchStrategy();
+ }
+ return strategy;
+ }
+
+}
diff --git a/src/main/java/com/btr/proxy/search/desktop/gnome/GnomeProxySearchStrategy.java b/src/main/java/com/btr/proxy/search/desktop/gnome/GnomeProxySearchStrategy.java
new file mode 100644
index 0000000..240fb39
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/desktop/gnome/GnomeProxySearchStrategy.java
@@ -0,0 +1,353 @@
+package com.btr.proxy.search.desktop.gnome;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.ProxySelector;
+import java.util.Properties;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+import com.btr.proxy.search.ProxySearchStrategy;
+import com.btr.proxy.selector.direct.NoProxySelector;
+import com.btr.proxy.selector.fixed.FixedProxySelector;
+import com.btr.proxy.selector.misc.ProtocolDispatchSelector;
+import com.btr.proxy.selector.whitelist.ProxyBypassListSelector;
+import com.btr.proxy.util.EmptyXMLResolver;
+import com.btr.proxy.util.Logger;
+import com.btr.proxy.util.ProxyException;
+import com.btr.proxy.util.ProxyUtil;
+import com.btr.proxy.util.Logger.LogLevel;
+
+/*****************************************************************************
+ * Loads the Gnome proxy settings from the Gnome GConf settings.
+ * <p>
+ * The following settings are extracted from the configuration that is stored
+ * in <i>.gconf</i> folder found in the user's home directory:
+ * </p>
+ * <ul>
+ * <li><i>/system/http_proxy/use_http_proxy</i> -> bool used only by gnome-vfs </li>
+ * <li><i>/system/http_proxy/host</i> -> string "my-proxy.example.com" without "http://"</li>
+ * <li><i>/system/http_proxy/port</i> -> int</li>
+ * <li><i>/system/http_proxy/use_authentication</i> -> bool</li>
+ * <li><i>/system/http_proxy/authentication_user</i> -> string</li>
+ * <li><i>/system/http_proxy/authentication_password</i> -> string</li>
+ * <li><i>/system/http_proxy/ignore_hosts</i> -> list-of-string</li>
+ * <li><i>/system/proxy/mode</i> -> string THIS IS THE CANONICAL KEY; SEE BELOW</li>
+ * <li><i>/system/proxy/secure_host</i> -> string "proxy-for-https.example.com"</li>
+ * <li><i>/system/proxy/secure_port</i> -> int</li>
+ * <li><i>/system/proxy/ftp_host</i> -> string "proxy-for-ftp.example.com"</li>
+ * <li><i>/system/proxy/ftp_port</i> -> int</li>
+ * <li><i>/system/proxy/socks_host</i> -> string "proxy-for-socks.example.com"</li>
+ * <li><i>/system/proxy/socks_port</i> -> int</li>
+ * <li><i>/system/proxy/autoconfig_url</i> -> string "http://proxy-autoconfig.example.com"</li>
+ * </ul>
+ * <i>/system/proxy/mode</i> can be either:<br/>
+ * "none" -> No proxy is used<br/>
+ * "manual" -> The user's configuration values are used (/system/http_proxy/{host,port,etc.})<br/>
+ * "auto" -> The "/system/proxy/autoconfig_url" key is used <br/>
+ * <p>
+ * GNOME Proxy_configuration settings are explained
+ * <a href="http://en.opensuse.org/GNOME/Proxy_configuration">here</a> in detail
+ * </p>
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class GnomeProxySearchStrategy implements ProxySearchStrategy {
+
+ /*************************************************************************
+ * ProxySelector
+ * @see java.net.ProxySelector#ProxySelector()
+ ************************************************************************/
+
+ public GnomeProxySearchStrategy() {
+ super();
+ }
+
+ /*************************************************************************
+ * Loads the proxy settings and initializes a proxy selector for the Gnome
+ * 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 Gnome proxy settings");
+
+ Properties settings = readSettings();
+
+ String type = settings.getProperty("/system/proxy/mode");
+ ProxySelector result = null;
+ if (type == null) {
+ String useProxy = settings.getProperty("/system/http_proxy/use_http_proxy");
+ if (useProxy == null) {
+ return null;
+ }
+ type = Boolean.parseBoolean(useProxy)?"manual":"none";
+ }
+
+ if ("none".equals(type)) {
+ Logger.log(getClass(), LogLevel.TRACE, "Gnome uses no proxy");
+ result = NoProxySelector.getInstance();
+ }
+ if ("manual".equals(type)) {
+ Logger.log(getClass(), LogLevel.TRACE, "Gnome uses manual proxy settings");
+ result = setupFixedProxySelector(settings);
+ }
+ if ("auto".equals(type)) {
+ String pacScriptUrl = settings.getProperty("/system/proxy/autoconfig_url", "");
+ Logger.log(getClass(), LogLevel.TRACE, "Gnome uses autodetect script {0}", pacScriptUrl);
+ result = ProxyUtil.buildPacSelectorForUrl(pacScriptUrl);
+ }
+
+ // Wrap into white-list filter?
+ String noProxyList = settings.getProperty("/system/http_proxy/ignore_hosts", null);
+ if (result != null && noProxyList != null && noProxyList.trim().length() > 0) {
+ Logger.log(getClass(), LogLevel.TRACE, "Gnome uses proxy bypass list: {0}", noProxyList);
+ result = new ProxyBypassListSelector(noProxyList, result);
+ }
+
+ return result;
+ }
+
+ /*************************************************************************
+ * Load the proxy settings from the gconf settings XML file.
+ * @return the loaded settings stored in a properties object.
+ * @throws ProxyException on processing error.
+ ************************************************************************/
+
+ public Properties readSettings() throws ProxyException {
+ Properties settings = new Properties();
+ try {
+ parseSettings("/system/proxy/", settings);
+ parseSettings("/system/http_proxy/", settings);
+ } catch (IOException e) {
+ Logger.log(getClass(), LogLevel.ERROR, "Gnome settings file error.", e);
+ throw new ProxyException(e);
+ }
+ return settings;
+ }
+
+ /*************************************************************************
+ * Finds the Gnome GConf settings file.
+ * @param context the gconf context to parse.
+ * @return a file or null if does not exist.
+ ************************************************************************/
+
+ private File findSettingsFile(String context) {
+ // Normally we should inspect /etc/gconf/<version>/path to find out where the actual file is.
+ // But for normal systems this is always stored in .gconf folder in the user's home directory.
+ File userDir = new File(System.getProperty("user.home"));
+
+ // Build directory path for context
+ StringBuilder path = new StringBuilder();
+ String[] parts = context.split("/");
+ for (String part : parts) {
+ path.append(part);
+ path.append(File.separator);
+ }
+
+ File settingsFile = new File(userDir, ".gconf"+File.separator+path.toString()+"%gconf.xml");
+ if (!settingsFile.exists()) {
+ Logger.log(getClass(), LogLevel.WARNING, "Gnome settings: {0} not found.", settingsFile);
+ return null;
+ }
+ return settingsFile;
+ }
+
+ /*************************************************************************
+ * Parse the fixed proxy settings and build an ProxySelector for this a
+ * chained configuration.
+ * @param settings the proxy settings to evaluate.
+ ************************************************************************/
+
+ private ProxySelector setupFixedProxySelector(Properties settings) {
+ if (!hasProxySettings(settings)) {
+ return null;
+ }
+ ProtocolDispatchSelector ps = new ProtocolDispatchSelector();
+ installHttpSelector(settings, ps);
+
+ if (useForAllProtocols(settings)) {
+ ps.setFallbackSelector(ps.getSelector("http"));
+ } else {
+ installSecureSelector(settings, ps);
+ installFtpSelector(settings, ps);
+ installSocksSelector(settings, ps);
+ }
+ return ps;
+ }
+
+ /*************************************************************************
+ * Check if the http proxy should also be used for all other protocols.
+ * @param settings to inspect.
+ * @return true if only one proxy is configured else false.
+ ************************************************************************/
+
+ private boolean useForAllProtocols(Properties settings) {
+ return Boolean.parseBoolean(
+ settings.getProperty("/system/http_proxy/use_same_proxy", "false"));
+ }
+
+ /*************************************************************************
+ * Checks if we have Proxy configuration settings in the properties.
+ * @param settings to inspect.
+ * @return true if we have found Proxy settings.
+ ************************************************************************/
+
+ private boolean hasProxySettings(Properties settings) {
+ String proxyHost = settings.getProperty("/system/http_proxy/host", null);
+ return proxyHost != null && proxyHost.length() > 0;
+ }
+
+ /*************************************************************************
+ * Install a http proxy from the given settings.
+ * @param settings to inspect
+ * @param ps the dispatch selector to configure.
+ * @throws NumberFormatException
+ ************************************************************************/
+
+ private void installHttpSelector(Properties settings,
+ ProtocolDispatchSelector ps) throws NumberFormatException {
+ String proxyHost = settings.getProperty("/system/http_proxy/host", null);
+ int proxyPort = Integer.parseInt(settings.getProperty("/system/http_proxy/port", "0").trim());
+ if (proxyHost != null && proxyHost.length() > 0 && proxyPort > 0) {
+ Logger.log(getClass(), LogLevel.TRACE, "Gnome http proxy is {0}:{1}", proxyHost, proxyPort);
+ ps.setSelector("http", new FixedProxySelector(proxyHost.trim(), proxyPort));
+ }
+ }
+
+ /*************************************************************************
+ * Install a socks proxy from the given settings.
+ * @param settings to inspect
+ * @param ps the dispatch selector to configure.
+ * @throws NumberFormatException
+ ************************************************************************/
+
+ private void installSocksSelector(Properties settings,
+ ProtocolDispatchSelector ps) throws NumberFormatException {
+ String proxyHost = settings.getProperty("/system/proxy/socks_host", null);
+ int proxyPort = Integer.parseInt(settings.getProperty("/system/proxy/socks_port", "0").trim());
+ if (proxyHost != null && proxyHost.length() > 0 && proxyPort > 0) {
+ Logger.log(getClass(), LogLevel.TRACE, "Gnome socks proxy is {0}:{1}", proxyHost, proxyPort);
+ ps.setSelector("socks", new FixedProxySelector(proxyHost.trim(), proxyPort));
+ }
+ }
+
+ /*************************************************************************
+ * @param settings
+ * @param ps
+ * @throws NumberFormatException
+ ************************************************************************/
+
+ private void installFtpSelector(Properties settings,
+ ProtocolDispatchSelector ps) throws NumberFormatException {
+ String proxyHost = settings.getProperty("/system/proxy/ftp_host", null);
+ int proxyPort = Integer.parseInt(settings.getProperty("/system/proxy/ftp_port", "0").trim());
+ if (proxyHost != null && proxyHost.length() > 0 && proxyPort > 0) {
+ Logger.log(getClass(), LogLevel.TRACE, "Gnome ftp proxy is {0}:{1}", proxyHost, proxyPort);
+ ps.setSelector("ftp", new FixedProxySelector(proxyHost.trim(), proxyPort));
+ }
+ }
+
+ /*************************************************************************
+ * @param settings
+ * @param ps
+ * @throws NumberFormatException
+ ************************************************************************/
+
+
+ private void installSecureSelector(Properties settings,
+ ProtocolDispatchSelector ps) throws NumberFormatException {
+ String proxyHost = settings.getProperty("/system/proxy/secure_host", null);
+ int proxyPort = Integer.parseInt(settings.getProperty("/system/proxy/secure_port", "0").trim());
+ if (proxyHost != null && proxyHost.length() > 0 && proxyPort > 0) {
+ Logger.log(getClass(), LogLevel.TRACE, "Gnome secure proxy is {0}:{1}", proxyHost, proxyPort);
+ ps.setSelector("https", new FixedProxySelector(proxyHost.trim(), proxyPort));
+ ps.setSelector("sftp", new FixedProxySelector(proxyHost.trim(), proxyPort));
+ }
+ }
+
+ /*************************************************************************
+ * Parse the settings file and extract all network.proxy.* settings from it.
+ * @param context the gconf context to parse.
+ * @param settings the settings object to fill.
+ * @return the parsed properties.
+ * @throws IOException on read error.
+ ************************************************************************/
+
+ private Properties parseSettings(String context, Properties settings) throws IOException {
+
+ // Read settings from file
+ File settingsFile = findSettingsFile(context);
+ if (settingsFile == null) {
+ return settings;
+ }
+
+ try {
+ DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+ documentBuilder.setEntityResolver(new EmptyXMLResolver());
+ Document doc = documentBuilder.parse(settingsFile);
+ Element root = doc.getDocumentElement();
+ Node entry = root.getFirstChild();
+ while (entry != null) {
+ if ("entry".equals(entry.getNodeName()) && entry instanceof Element) {
+ String entryName = ((Element)entry).getAttribute("name");
+ settings.setProperty(context+entryName, getEntryValue((Element) entry));
+ }
+ entry = entry.getNextSibling();
+ }
+ } catch (SAXException e) {
+ Logger.log(getClass(), LogLevel.ERROR, "Gnome settings parse error", e);
+ throw new IOException(e.getMessage());
+ } catch (ParserConfigurationException e) {
+ Logger.log(getClass(), LogLevel.ERROR, "Gnome settings parse error", e);
+ throw new IOException(e.getMessage());
+ }
+
+ return settings;
+ }
+
+ /*************************************************************************
+ * Parse an entry value from a given entry node.
+ * @param entry the XML node to inspect.
+ * @return the value, null if it has no value.
+ ************************************************************************/
+
+ private String getEntryValue(Element entry) {
+ String type = entry.getAttribute("type");
+
+ if ("int".equals(type) || "bool".equals(type)) {
+ return entry.getAttribute("value");
+ }
+ if ("string".equals(type)) {
+ NodeList list = entry.getElementsByTagName("stringvalue");
+ if (list.getLength() > 0) {
+ return list.item(0).getTextContent();
+ }
+ }
+ if ("list".equals(type)) {
+ StringBuilder result = new StringBuilder();
+ NodeList list = entry.getElementsByTagName("li");
+
+ // Build comma separated list of items
+ for (int i = 0; i < list.getLength(); i++) {
+ if (result.length() > 0) {
+ result.append(",");
+ }
+ result.append(getEntryValue((Element) list.item(i)));
+ }
+ return result.toString();
+ }
+ return null;
+ }
+
+}
diff --git a/src/main/java/com/btr/proxy/search/desktop/gnome/ProxySchemasGSettingsAccess.java b/src/main/java/com/btr/proxy/search/desktop/gnome/ProxySchemasGSettingsAccess.java
new file mode 100644
index 0000000..6e1b425
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/desktop/gnome/ProxySchemasGSettingsAccess.java
@@ -0,0 +1,60 @@
+package com.btr.proxy.search.desktop.gnome;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Map;
+
+public class ProxySchemasGSettingsAccess {
+
+ private static void closeStream(Closeable c) {
+ try {
+ c.close();
+ } catch (IOException e) {
+ // Ignore cleanup errors
+ }
+ }
+
+ static void copy(InputStream source, OutputStream dest) throws IOException {
+ try {
+ byte[] buffer = new byte[1024];
+ int read = 0;
+ while (read >= 0) {
+ dest.write(buffer, 0, read);
+ read = source.read(buffer);
+ }
+ dest.flush();
+ } finally {
+ closeStream(source);
+ closeStream(dest);
+ }
+ }
+
+ static {
+ String arch = System.getProperty("os.arch");
+ if (arch.equals("x86_64") || arch.equals("x64") || arch.equals("x86-64")) {
+ arch = "amd64";
+ }
+ if (arch.equals("i386") || arch.equals("i686")) {
+ arch = "x86";
+ }
+ String libName = "gsettings-" + arch + ".so";
+ File libFile;
+ try {
+ InputStream source = ProxySchemasGSettingsAccess.class.getResourceAsStream("/lib/" + libName);
+ libFile = File.createTempFile("gsettings", ".so");
+ libFile.deleteOnExit();
+ FileOutputStream destination = new FileOutputStream(libFile);
+ copy(source, destination);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ System.load(libFile.getAbsolutePath());
+ }
+
+ public static native Map<String, Map<String, Object>> getValueByKeyBySchema();
+
+}
diff --git a/src/main/java/com/btr/proxy/search/desktop/kde/KdeProxySearchStrategy.java b/src/main/java/com/btr/proxy/search/desktop/kde/KdeProxySearchStrategy.java
new file mode 100644
index 0000000..e05d72e
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/desktop/kde/KdeProxySearchStrategy.java
@@ -0,0 +1,198 @@
+package com.btr.proxy.search.desktop.kde;
+
+import java.io.IOException;
+import java.net.ProxySelector;
+import java.util.Properties;
+
+import com.btr.proxy.search.ProxySearchStrategy;
+import com.btr.proxy.search.env.EnvProxySearchStrategy;
+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.misc.ProtocolDispatchSelector;
+import com.btr.proxy.selector.pac.PacProxySelector;
+import com.btr.proxy.selector.pac.UrlPacScriptSource;
+import com.btr.proxy.selector.whitelist.ProxyBypassListSelector;
+import com.btr.proxy.selector.whitelist.UseProxyWhiteListSelector;
+import com.btr.proxy.util.Logger;
+import com.btr.proxy.util.Logger.LogLevel;
+import com.btr.proxy.util.ProxyException;
+import com.btr.proxy.util.ProxyUtil;
+
+/*****************************************************************************
+ * Loads the KDE4 proxy settings from the KDE <i>kioslaverc</i> file.
+ * This will load properties from the file
+ * <p>
+ * <i>.kde/share/config/kioslaverc</i>
+ * </P>
+ * starting from the current users home directory.
+ * <p>
+ * The following settings are extracted from the section "[Proxy Settings]":
+ * </p>
+ * <ul>
+ * <li><i>AuthMode</i> -> 0 = no auth., 1 = use login.</li>
+ * <li><i>ProxyType</i> -> 0 = direct 1 = use fixed settings, 2 = use PAC, 3 = automatic (WPAD) , 4 = Use environment variables?</li>
+ * <li><i>Proxy Config</i> Script -> URL to PAC file</li>
+ * <li><i>ftpProxy</i> -> Fixed ftp proxy address e.g. http://www.ftp-proxy.com:8080</li>
+ * <li><i>httpProxy</i> -> Fixed http proxy e.g http://www.http-proxy.com:8080</li>
+ * <li><i>httpsProxy</i> -> Fixed https proxy e.g http://www.https-proxy.com:8080</li>
+ * <li><i>NoProxyFor</i> -> Proxy white list</li>
+ * <li><i>ReversedException</i> -> false = use NoProxyFor, true = revert meaning of the NoProxyFor list</li>
+ * </ul>
+ *
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class KdeProxySearchStrategy implements ProxySearchStrategy {
+
+ private KdeSettingsParser settingsParser;
+
+ /*************************************************************************
+ * ProxySelector using the given parser.
+ * @see java.net.ProxySelector#ProxySelector()
+ ************************************************************************/
+
+ public KdeProxySearchStrategy() {
+ this(new KdeSettingsParser());
+ }
+
+ /*************************************************************************
+ * ProxySelector
+ * @see java.net.ProxySelector#ProxySelector()
+ ************************************************************************/
+
+ public KdeProxySearchStrategy(KdeSettingsParser settingsParser) {
+ super();
+ this.settingsParser = settingsParser;
+ }
+
+ /*************************************************************************
+ * Loads the proxy settings and initializes a proxy selector for the firefox
+ * 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 Kde proxy settings");
+
+ Properties settings = readSettings();
+ if (settings == null) {
+ return null;
+ }
+
+ ProxySelector result = null;
+ int type = Integer.parseInt(settings.getProperty("ProxyType", "-1"));
+ switch (type) {
+ case 0: // Use no proxy
+ Logger.log(getClass(), LogLevel.TRACE, "Kde uses no proxy");
+ result = NoProxySelector.getInstance();
+ break;
+ case 1: // Fixed settings
+ Logger.log(getClass(), LogLevel.TRACE, "Kde uses manual proxy settings");
+ result = setupFixedProxySelector(settings);
+ break;
+ case 2: // PAC Script
+ String pacScriptUrl = settings.getProperty("Proxy Config Script", "");
+ Logger.log(getClass(), LogLevel.TRACE, "Kde uses autodetect script {0}", pacScriptUrl);
+ result = ProxyUtil.buildPacSelectorForUrl(pacScriptUrl);
+ break;
+ case 3: // WPAD
+ Logger.log(getClass(), LogLevel.TRACE, "Kde uses WPAD to detect the proxy");
+ result = new WpadProxySearchStrategy().getProxySelector();
+ break;
+ case 4: // Use environment variables
+ Logger.log(getClass(), LogLevel.TRACE, "Kde reads proxy from environment");
+ result = setupEnvVarSelector(settings);
+ break;
+ default:
+ break;
+ }
+
+ return result;
+ }
+
+ /*************************************************************************
+ * Reads the settings and stores them in a properties map.
+ * @return the parsed settings.
+ * @throws ProxyException
+ ************************************************************************/
+
+ private Properties readSettings() throws ProxyException {
+ try {
+ return this.settingsParser.parseSettings();
+ } catch (IOException e) {
+ Logger.log(getClass(), LogLevel.ERROR, "Can't parse settings.", e);
+ throw new ProxyException(e);
+ }
+ }
+
+ /*************************************************************************
+ * Builds an environment variable selector.
+ * @param settings the settings to read from.
+ * @return the ProxySelector using environment variables.
+ ************************************************************************/
+
+ private ProxySelector setupEnvVarSelector(Properties settings) {
+ ProxySelector result;
+ result = new EnvProxySearchStrategy(
+ settings.getProperty("httpProxy"),
+ settings.getProperty("httpsProxy"),
+ settings.getProperty("ftpProxy"),
+ settings.getProperty("NoProxyFor")
+ ).getProxySelector();
+ return result;
+ }
+
+ /*************************************************************************
+ * Parse the fixed proxy settings and build an ProxySelector for this a
+ * chained configuration.
+ * @param settings the proxy settings to evaluate.
+ ************************************************************************/
+
+ private ProxySelector setupFixedProxySelector(Properties settings) {
+ String proxyVar = settings.getProperty("httpProxy", null);
+ FixedProxySelector httpPS = ProxyUtil.parseProxySettings(proxyVar);
+ if (httpPS == null) {
+ Logger.log(getClass(), LogLevel.TRACE, "Kde http proxy is {0}", proxyVar);
+ return null;
+ }
+
+ ProtocolDispatchSelector ps = new ProtocolDispatchSelector();
+ ps.setSelector("http", httpPS);
+
+ proxyVar = settings.getProperty("httpsProxy", null);
+ FixedProxySelector httpsPS = ProxyUtil.parseProxySettings(proxyVar);
+ if (httpsPS != null) {
+ Logger.log(getClass(), LogLevel.TRACE, "Kde https proxy is {0}", proxyVar);
+ ps.setSelector("https", httpsPS);
+ }
+
+ proxyVar = settings.getProperty("ftpProxy", null);
+ FixedProxySelector ftpPS = ProxyUtil.parseProxySettings(proxyVar);
+ if (ftpPS != null) {
+ Logger.log(getClass(), LogLevel.TRACE, "Kde ftp proxy is {0}", proxyVar);
+ ps.setSelector("ftp", ftpPS);
+ }
+
+ // Wrap in white list filter.
+ String noProxyList = settings.getProperty("NoProxyFor", null);
+ if (noProxyList != null && noProxyList.trim().length() > 0) {
+ boolean reverse = "true".equals(settings.getProperty("ReversedException", "false"));
+ if (reverse) {
+ Logger.log(getClass(), LogLevel.TRACE, "Kde proxy blacklist is {0}", noProxyList);
+ return new UseProxyWhiteListSelector(noProxyList, ps);
+ } else {
+ Logger.log(getClass(), LogLevel.TRACE, "Kde proxy whitelist is {0}", noProxyList);
+ return new ProxyBypassListSelector(noProxyList, ps);
+ }
+ }
+
+ return ps;
+ }
+
+
+
+}
diff --git a/src/main/java/com/btr/proxy/search/desktop/kde/KdeSettingsParser.java b/src/main/java/com/btr/proxy/search/desktop/kde/KdeSettingsParser.java
new file mode 100644
index 0000000..904528e
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/desktop/kde/KdeSettingsParser.java
@@ -0,0 +1,132 @@
+package com.btr.proxy.search.desktop.kde;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.Properties;
+
+import com.btr.proxy.util.Logger;
+import com.btr.proxy.util.Logger.LogLevel;
+
+/*****************************************************************************
+ * Parser for the KDE settings file.
+ * The KDE proxy settings are stored in the file:
+ * <p>
+ * <i>.kde/share/config/kioslaverc</i>
+ * </p>
+ * in the users home directory.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class KdeSettingsParser {
+
+ private File settingsFile;
+
+ /*************************************************************************
+ * Constructor
+ ************************************************************************/
+
+ public KdeSettingsParser() {
+ this(null);
+ }
+
+ /*************************************************************************
+ * Constructor
+ ************************************************************************/
+
+ public KdeSettingsParser(File settingsFile) {
+ super();
+ this.settingsFile = settingsFile;
+ }
+
+ /*************************************************************************
+ * Parse the settings file and extract all network.proxy.* settings from it.
+ * @return the parsed properties.
+ * @throws IOException on read error.
+ ************************************************************************/
+
+ public Properties parseSettings() throws IOException {
+ // Search for existing settings.
+ if (this.settingsFile == null) {
+ this.settingsFile = findSettingsFile();
+ }
+ if (this.settingsFile == null) {
+ return null;
+ }
+
+ // Read settings from file.
+ BufferedReader fin = new BufferedReader(
+ new InputStreamReader(
+ new FileInputStream(this.settingsFile)));
+
+ Properties result = new Properties();
+ try {
+ String line = fin.readLine();
+
+ // Find section start.
+ while (line != null && !"[Proxy Settings]".equals(line.trim())) {
+ line = fin.readLine();
+ }
+ if (line == null) {
+ return result;
+ }
+
+ // Read full section
+ line = "";
+ while (line != null && !line.trim().startsWith("[")) {
+ line = line.trim();
+ int index = line.indexOf('=');
+ if (index > 0) {
+ String key = line.substring(0, index).trim();
+ String value = line.substring(index+1).trim();
+ result.setProperty(key, value);
+ }
+
+ line = fin.readLine();
+ }
+ } finally {
+ fin.close();
+ }
+
+ return result;
+ }
+
+ /*************************************************************************
+ * Finds all the KDE network settings file.
+ * @return a file or null if does not exist.
+ ************************************************************************/
+
+ private File findSettingsFile() {
+ File userDir = new File(System.getProperty("user.home"));
+ if ("4".equals(System.getenv("KDE_SESSION_VERSION"))) {
+ this.settingsFile = findSettingsFile(
+ new File(userDir, ".kde4"+File.separator+"share"+File.separator+"config"+File.separator+"kioslaverc"));
+ }
+ if (this.settingsFile == null) {
+ return findSettingsFile(
+ new File(userDir, ".kde"+File.separator+"share"+File.separator+"config"+File.separator+"kioslaverc"));
+ } else {
+ return this.settingsFile;
+ }
+ }
+
+ /*************************************************************************
+ * Internal method to test if the settings file is at the given place.
+ * @param settingsFile the path to test.
+ * @return the file or null if it does not exist.
+ ************************************************************************/
+
+ private File findSettingsFile(File settingsFile) {
+ Logger.log(getClass(), LogLevel.TRACE, "Searching Kde settings in {0}", settingsFile);
+ if (!settingsFile.exists()) {
+ Logger.log(getClass(), LogLevel.DEBUG, "Settings not found");
+ return null;
+ }
+ Logger.log(getClass(), LogLevel.TRACE, "Settings found");
+ return settingsFile;
+ }
+
+}
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);
+ }
+
+}
diff --git a/src/main/java/com/btr/proxy/search/desktop/win/DLLManager.java b/src/main/java/com/btr/proxy/search/desktop/win/DLLManager.java
new file mode 100644
index 0000000..8d67984
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/desktop/win/DLLManager.java
@@ -0,0 +1,171 @@
+package com.btr.proxy.search.desktop.win;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import com.btr.proxy.util.Logger;
+import com.btr.proxy.util.Logger.LogLevel;
+
+/*****************************************************************************
+ * This class provides some helper methods to work with the dll
+ * extracting /loading for windows.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+final class DLLManager {
+
+ private static final class TempDLLFileFilter implements FileFilter {
+ public boolean accept(File pathname) {
+ String name = pathname.getName();
+ return pathname.isFile() &&
+ name.startsWith(TEMP_FILE_PREFIX) &&
+ name.endsWith(DLL_EXTENSION);
+ }
+ }
+
+ public static final String LIB_DIR_OVERRIDE = "proxy_vole_lib_dir";
+
+ static final String TEMP_FILE_PREFIX = "proxy_vole";
+ static final String DLL_EXTENSION = ".dll";
+ static String LIB_NAME_BASE = "proxy_util_";
+ static final String DEFAULT_LIB_FOLDER = "lib";
+
+ /*************************************************************************
+ * Find the location of the native code dll file.
+ * @return the File pointing to the dll.
+ * @throws IOException on IO error.
+ ************************************************************************/
+
+ static File findLibFile() throws IOException {
+ String libName = buildLibName();
+ File libFile = getOverrideLibFile(libName);
+ if (libFile == null || libFile.exists() == false) {
+ libFile = getDefaultLibFile(libName);
+ }
+ if (libFile == null || libFile.exists() == false) {
+ libFile = extractToTempFile(libName);
+ }
+ return libFile;
+ }
+
+ /*************************************************************************
+ * Delete old temp files that may be there because they were extracted
+ * from the jar but could not be deleted on VM shutdown because they
+ * are still locked by windows.
+ * This is here to prevent a lot of temp dll files on disk.
+ ************************************************************************/
+
+ static void cleanupTempFiles() {
+ try {
+ String tempFolder = System.getProperty("java.io.tmpdir");
+ if (tempFolder == null || tempFolder.trim().length() == 0) {
+ return;
+ }
+ File fldr = new File(tempFolder);
+ File[] oldFiles = fldr.listFiles(new TempDLLFileFilter());
+ if (oldFiles == null) {
+ return;
+ }
+ for (File tmp : oldFiles) {
+ tmp.delete();
+ }
+ } catch (Exception e) {
+ Logger.log(DLLManager.class, LogLevel.DEBUG, "Error cleaning up temporary dll files. ", e);
+ }
+ }
+
+ /*************************************************************************
+ * @param libName
+ * @return
+ ************************************************************************/
+
+ private static File getDefaultLibFile(String libName) {
+ return new File(DEFAULT_LIB_FOLDER, libName);
+ }
+
+ /*************************************************************************
+ * Gets the file name that was overriden via system property.
+ * @param libName
+ * @return the file, null if it is not existing.
+ ************************************************************************/
+
+ private static File getOverrideLibFile(String libName) {
+ String libDir = System.getProperty(LIB_DIR_OVERRIDE);
+ if (libDir == null || libDir.trim().length() == 0) {
+ return null;
+ }
+ return new File(libDir, libName);
+ }
+
+ /*************************************************************************
+ * @param libName
+ * @return
+ * @throws IOException
+ * @throws FileNotFoundException
+ ************************************************************************/
+
+ static File extractToTempFile(String libName) throws IOException {
+ InputStream source = Win32ProxyUtils.class.getResourceAsStream("/"+DEFAULT_LIB_FOLDER+"/"+libName);
+ File tempFile = File.createTempFile(TEMP_FILE_PREFIX, DLL_EXTENSION);
+ tempFile.deleteOnExit();
+ FileOutputStream destination = new FileOutputStream(tempFile);
+ copy(source, destination);
+ return tempFile;
+ }
+
+ /*************************************************************************
+ * @param c a closeable to cleanup ignoring all errors.
+ ************************************************************************/
+
+ private static void closeStream(Closeable c) {
+ try {
+ c.close();
+ } catch (IOException e) {
+ // Ignore cleanup errors
+ }
+ }
+
+ /*************************************************************************
+ * Copies the content from source to destination.
+ * @param source
+ * @param dest
+ * @throws IOException
+ ************************************************************************/
+
+ static void copy(InputStream source, OutputStream dest)
+ throws IOException {
+ try {
+ byte[] buffer = new byte[1024];
+ int read = 0;
+ while (read >= 0) {
+ dest.write(buffer, 0, read);
+ read = source.read(buffer);
+ }
+ dest.flush();
+ } finally {
+ closeStream(source);
+ closeStream(dest);
+ }
+ }
+
+ /*************************************************************************
+ * @return the name of the dll valid for the current architecture.
+ ************************************************************************/
+
+ private static String buildLibName() {
+ String arch = "w32";
+ if(!System.getProperty("os.arch").equals("x86") ) {
+ arch = System.getProperty("os.arch");
+ }
+ return LIB_NAME_BASE + arch + DLL_EXTENSION;
+ }
+
+}
+
diff --git a/src/main/java/com/btr/proxy/search/desktop/win/Win32IESettings.java b/src/main/java/com/btr/proxy/search/desktop/win/Win32IESettings.java
new file mode 100644
index 0000000..ef91a22
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/desktop/win/Win32IESettings.java
@@ -0,0 +1,68 @@
+package com.btr.proxy.search.desktop.win;
+
+/*****************************************************************************
+ * Proxy settings container used for the native methods.
+ * Will contain the Internet Explorer proxy settings as reported by windows
+ * WinHTTP API.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class Win32IESettings {
+
+ private boolean autoDetect;
+ private String autoConfigUrl;
+ private String proxy;
+ private String proxyBypass;
+
+ /*************************************************************************
+ * Constructor
+ * @param autoDetect flag is autodetect is active or not.
+ * @param autoConfigUrl the URL for a PAC script
+ * @param proxy the proxy server selected
+ * @param proxyBypass the proxy bypass address list.
+ ************************************************************************/
+
+ public Win32IESettings(boolean autoDetect, String autoConfigUrl, String proxy, String proxyBypass) {
+ super();
+ this.autoDetect = autoDetect;
+ this.autoConfigUrl = autoConfigUrl;
+ this.proxy = proxy;
+ this.proxyBypass = proxyBypass;
+ }
+
+ /*************************************************************************
+ * @return Returns the autoDetect.
+ ************************************************************************/
+
+ public boolean isAutoDetect() {
+ return this.autoDetect;
+ }
+
+ /*************************************************************************
+ * @return Returns the autoConfigUrl.
+ ************************************************************************/
+
+ public String getAutoConfigUrl() {
+ return this.autoConfigUrl;
+ }
+
+ /*************************************************************************
+ * @return Returns the proxy.
+ ************************************************************************/
+
+ public String getProxy() {
+ return this.proxy;
+ }
+
+ /*************************************************************************
+ * @return Returns the proxyBypass.
+ ************************************************************************/
+
+ public String getProxyBypass() {
+ return this.proxyBypass;
+ }
+
+
+}
+
diff --git a/src/main/java/com/btr/proxy/search/desktop/win/Win32ProxyUtils.java b/src/main/java/com/btr/proxy/search/desktop/win/Win32ProxyUtils.java
new file mode 100644
index 0000000..fb1f0b3
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/desktop/win/Win32ProxyUtils.java
@@ -0,0 +1,88 @@
+package com.btr.proxy.search.desktop.win;
+
+import java.io.File;
+import java.io.IOException;
+
+/*****************************************************************************
+ * Defines the native methods used for windows to extract some system information.
+ * <p>
+ * This class will need some native code from the library proxy_util_"arch".dll.
+ * To load this library we use a three step algorithm as following:
+ * </p><P>
+ * First check the System property "proxy_vole_lib_dir" if it is set and
+ * it points to a folder where the dll is found than the dll from this
+ * folder is loaded as e.g. <i>"proxy_vole_lib_dir"/proxy_util_w32.dll</i>
+ * </p><p>
+ * Second we try to load the dll from the subfolder <i>lib</i> if that one exists.<br>
+ * Finally if we are inside of a jar file we need to extract the dll file
+ * to a temp-file because windows can not load dlls from a jar
+ * directly. This is a hack but it may work.
+ * </p><p>
+ * Please note that the file is named Win32ProxyUtils but has now also support
+ * for x64 architecture.
+ * </p>
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class Win32ProxyUtils {
+
+ public static final int WINHTTP_AUTO_DETECT_TYPE_DHCP = 0x00000001;
+ public static final int WINHTTP_AUTO_DETECT_TYPE_DNS_A = 0x00000002;
+
+
+ // Code for loading the windows native dll
+ static {
+ try {
+ File libFile = DLLManager.findLibFile();
+ System.load(libFile.getAbsolutePath());
+ DLLManager.cleanupTempFiles();
+ } catch (IOException e) {
+ throw new RuntimeException("Error loading dll"+e.getMessage(), e);
+ }
+ }
+
+ /*************************************************************************
+ * Constructor
+ ************************************************************************/
+
+ public Win32ProxyUtils() {
+ super();
+ }
+
+ /*************************************************************************
+ * WinHTTP method to detect an PAC URL.
+ * @param mode the mode to use.
+ * @return the PAC URL, null if none was found.
+ ************************************************************************/
+
+ public native String winHttpDetectAutoProxyConfigUrl(int mode);
+
+ /*************************************************************************
+ * Gets the default windows proxy settings.
+ * The returned string will have the following format.
+ * TYPE PROXY | BYPASSLIST
+ * <p>
+ * e.g. DIRECT myproxy.mycompany.com:8080 | *.mycompany.com, localhost
+ * </p>
+ * @return a string containing all info, null if not found.
+ ************************************************************************/
+ // TODO Not implemented correctly in DLL yet.
+ native String winHttpGetDefaultProxyConfiguration();
+
+ /*************************************************************************
+ * Extracts the Internet Explorer proxy settings from the Windows system.
+ * @return a data structure containing all details, null on fail.
+ ************************************************************************/
+
+ public native Win32IESettings winHttpGetIEProxyConfigForCurrentUser();
+
+ /*************************************************************************
+ * Extracts the Internet Explorer proxy settings from the Windows system.
+ * @return a data structure containing all details, null on fail.
+ ************************************************************************/
+
+ public native String readUserHomedir();
+
+
+}
+
diff --git a/src/main/java/com/btr/proxy/search/desktop/win/WinProxySearchStrategy.java b/src/main/java/com/btr/proxy/search/desktop/win/WinProxySearchStrategy.java
new file mode 100644
index 0000000..1f26505
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/desktop/win/WinProxySearchStrategy.java
@@ -0,0 +1,52 @@
+package com.btr.proxy.search.desktop.win;
+
+import java.net.ProxySelector;
+
+import com.btr.proxy.search.ProxySearchStrategy;
+import com.btr.proxy.search.browser.ie.IEProxySearchStrategy;
+import com.btr.proxy.util.ProxyException;
+
+/*****************************************************************************
+ * Extracts the proxy settings from the windows registry.
+ * This will read the windows system proxy settings.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class WinProxySearchStrategy implements ProxySearchStrategy {
+
+ /*************************************************************************
+ * Constructor
+ ************************************************************************/
+
+ public WinProxySearchStrategy() {
+ super();
+ }
+
+ /*************************************************************************
+ * getProxySelector
+ * @see com.btr.proxy.search.ProxySearchStrategy#getProxySelector()
+ ************************************************************************/
+
+ public ProxySelector getProxySelector() throws ProxyException {
+ // TODO Rossi 08.05.2009 Implement this by using Win API calls.
+ // new Win32ProxyUtils().winHttpGetDefaultProxyConfiguration()
+ // Current fallback is to use the IE settings. This is better
+ // because the registry settings are most of the time not set.
+ // Some Windows server installations may use it though.
+ return new IEProxySearchStrategy().getProxySelector();
+ }
+
+ /*************************************************************************
+ * Loads the settings.
+ * @return a WinIESettings object containing all proxy settings.
+ ************************************************************************/
+
+ public Win32IESettings readSettings() {
+ return new IEProxySearchStrategy().readSettings();
+ }
+
+
+
+
+}
diff --git a/src/main/java/com/btr/proxy/search/env/EnvProxySearchStrategy.java b/src/main/java/com/btr/proxy/search/env/EnvProxySearchStrategy.java
new file mode 100644
index 0000000..37bd0a1
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/env/EnvProxySearchStrategy.java
@@ -0,0 +1,130 @@
+package com.btr.proxy.search.env;
+
+import java.net.ProxySelector;
+import java.util.Properties;
+
+import com.btr.proxy.search.ProxySearchStrategy;
+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.ProxyUtil;
+import com.btr.proxy.util.Logger.LogLevel;
+
+/*****************************************************************************
+ * Reads some environment variables and extracts the proxy settings from them.
+ * These variables are mainly set on linux / unix environments.
+ * The following variables are read per default:
+ * <ul>
+ * <li><i>http_proxy</i> -> This will be used for http / https</li>
+ * <li><i>https_proxy</i> -> Will be used for https, if not set then http_proxy is used instead.</li>
+ * <li><i>ftp_proxy</i> -> Used for FTP.</li>
+ * <li><i>no_proxy</i> -> a no proxy white list.</li>
+ * </ul>
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class EnvProxySearchStrategy implements ProxySearchStrategy {
+
+ private String httpEnv;
+ private String httpsEnv;
+ private String ftpEnv;
+ private String noProxyEnv;
+
+ private String httpProxy;
+ private String httpsProxy;
+ private String ftpProxy;
+ private String noProxy;
+
+ /*************************************************************************
+ * Constructor
+ * Will use the default environment variables.
+ ************************************************************************/
+
+ public EnvProxySearchStrategy() {
+ this("http_proxy", "https_proxy", "ftp_proxy", "no_proxy");
+ }
+
+ /*************************************************************************
+ * Constructor
+ * @param httpEnv name of environment variable
+ * @param httpsEnv name of environment variable
+ * @param ftpEnv name of environment variable
+ * @param noProxyEnv name of environment variable
+ ************************************************************************/
+
+ public EnvProxySearchStrategy(String httpEnv, String httpsEnv, String ftpEnv, String noProxyEnv) {
+ super();
+ this.httpEnv = httpEnv;
+ this.httpsEnv = httpsEnv;
+ this.ftpEnv = ftpEnv;
+ this.noProxyEnv = noProxyEnv;
+
+ loadProxySettings();
+ }
+
+ /*************************************************************************
+ * Loads the proxy settings from the system environment variables.
+ ************************************************************************/
+
+ private void loadProxySettings() {
+ this.httpProxy = System.getenv(this.httpEnv);
+ this.httpsProxy = System.getenv(this.httpsEnv);
+ this.ftpProxy = System.getenv(this.ftpEnv);
+ this.noProxy = System.getenv(this.noProxyEnv);
+ }
+
+ /*************************************************************************
+ * Loads the settings and stores them in a properties map.
+ * @return the settings.
+ ************************************************************************/
+
+ public Properties readSettings() {
+ Properties result = new Properties();
+ result.setProperty(this.httpEnv, this.httpProxy);
+ result.setProperty(this.httpsEnv, this.httpsProxy);
+ result.setProperty(this.ftpEnv, this.ftpProxy);
+ result.setProperty(this.noProxyEnv, this.noProxy);
+ return result;
+ }
+
+
+ /*************************************************************************
+ * Loads the proxy settings from environment variables.
+ * @return a configured ProxySelector, null if none is found.
+ ************************************************************************/
+
+ public ProxySelector getProxySelector() {
+
+ Logger.log(getClass(), LogLevel.TRACE, "Inspecting environment variables.");
+
+ // Check if http_proxy var is set.
+ ProxySelector httpPS = ProxyUtil.parseProxySettings(this.httpProxy);
+ if (httpPS == null) {
+ return null;
+ }
+
+ Logger.log(getClass(), LogLevel.TRACE, "Http Proxy is {0}", this.httpProxy);
+ ProtocolDispatchSelector ps = new ProtocolDispatchSelector();
+ ps.setSelector("http", httpPS);
+
+ ProxySelector httpsPS = ProxyUtil.parseProxySettings(this.httpsProxy);
+ Logger.log(getClass(), LogLevel.TRACE, "Https Proxy is {0}", httpsPS == null? this.httpsProxy: httpsPS);
+ ps.setSelector("https", httpsPS != null? httpsPS: httpPS);
+
+ ProxySelector ftpPS = ProxyUtil.parseProxySettings(this.ftpProxy);
+ if (ftpPS != null) {
+ Logger.log(getClass(), LogLevel.TRACE, "Ftp Proxy is {0}", this.ftpProxy);
+ ps.setSelector("ftp", ftpPS);
+ }
+
+ // Wrap with white list support
+ ProxySelector result = ps;
+ if (this.noProxy != null && this.noProxy.trim().length() > 0) {
+ Logger.log(getClass(), LogLevel.TRACE, "Using proxy bypass list: {0}", this.noProxy);
+ result = new ProxyBypassListSelector(this.noProxy, ps);
+ }
+
+ return result;
+ }
+
+}
diff --git a/src/main/java/com/btr/proxy/search/java/JavaProxySearchStrategy.java b/src/main/java/com/btr/proxy/search/java/JavaProxySearchStrategy.java
new file mode 100644
index 0000000..531ef94
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/java/JavaProxySearchStrategy.java
@@ -0,0 +1,133 @@
+package com.btr.proxy.search.java;
+
+import java.net.ProxySelector;
+
+import com.btr.proxy.search.ProxySearchStrategy;
+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;
+
+/*****************************************************************************
+ * Reads some java system properties and extracts the proxy settings from them.
+ * The following variables are read:
+ * <ul>
+ * <li><i>http.proxyHost</i> (default: none)</li>
+ * <li><i>http.proxyPort</i> (default: 80 if http.proxyHost specified)</li>
+ * <li><i>http.nonProxyHosts</i> (default: none)</li>
+ * </ul>
+ * <ul>
+ * <li><i>https.proxyHost</i> (default: none)</li>
+ * <li><i>https.proxyPort</i> (default: 443 if https.proxyHost specified)</li>
+ * </ul>
+ * <ul>
+ * <li><i>ftp.proxyHost</i> (default: none)</li>
+ * <li><i>ftp.proxyPort</i> (default: 80 if ftp.proxyHost specified)</li>
+ * <li><i>ftp.nonProxyHosts</i> (default: none)</li>
+ * </ul>
+ * <ul>
+ * <li><i>socksProxyHost</i></li>
+ * <li><i>socksProxyPort</i> (default: 1080)</li>
+ * </ul>
+ * <p>
+ * This is based on information found here: <br/>
+ * http://download.oracle.com/javase/6/docs/technotes/guides/net/proxies.html
+ * </p>
+ * If the "http.proxyHost" property is not set then the no proxy selector is setup
+ * This property is used as marker to signal that the System settings should be used.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class JavaProxySearchStrategy implements ProxySearchStrategy {
+
+ /*************************************************************************
+ * Constructor
+ * Will use the default environment variables.
+ ************************************************************************/
+
+ public JavaProxySearchStrategy() {
+ super();
+ }
+
+ /*************************************************************************
+ * Loads the proxy settings from environment variables.
+ * @return a configured ProxySelector, null if none is found.
+ ************************************************************************/
+
+ public ProxySelector getProxySelector() {
+ ProtocolDispatchSelector ps = new ProtocolDispatchSelector();
+
+ if (!proxyPropertyPresent()) {
+ return null;
+ }
+ Logger.log(getClass(), LogLevel.TRACE, "Using settings from Java System Properties");
+
+
+ setupProxyForProtocol(ps, "http", 80);
+ setupProxyForProtocol(ps, "https", 443);
+ setupProxyForProtocol(ps, "ftp", 80);
+ setupProxyForProtocol(ps, "ftps", 80);
+ setupSocktProxy(ps);
+
+ return ps;
+ }
+
+ /*************************************************************************
+ * @return true if the http.proxyHost is available as system property.
+ ************************************************************************/
+
+ private boolean proxyPropertyPresent() {
+ return System.getProperty("http.proxyHost") != null
+ && System.getProperty("http.proxyHost").trim().length() > 0;
+ }
+
+ /*************************************************************************
+ * Parse SOCKS settings
+ * @param ps
+ * @throws NumberFormatException
+ ************************************************************************/
+
+
+ private void setupSocktProxy(ProtocolDispatchSelector ps) {
+ String host = System.getProperty("socksProxyHost");
+ String port = System.getProperty("socksProxyPort", "1080");
+ if (host != null && host.trim().length() > 0) {
+ Logger.log(getClass(), LogLevel.TRACE, "Socks proxy {0}:{1} found", host, port);
+ ps.setSelector("socks", new FixedSocksSelector(host, Integer.parseInt(port)));
+ }
+ }
+
+ /*************************************************************************
+ * Parse properties for the given protocol.
+ * @param ps
+ * @param protocol
+ * @throws NumberFormatException
+ ************************************************************************/
+
+ private void setupProxyForProtocol(ProtocolDispatchSelector ps, String protocol, int defaultPort) {
+ String host = System.getProperty(protocol+".proxyHost");
+ String port = System.getProperty(protocol+".proxyPort", ""+defaultPort);
+ String whiteList = System.getProperty(protocol+".nonProxyHosts", "").replace('|', ',');
+
+ if ("https".equalsIgnoreCase(protocol)) { // This is dirty but https has no own property for it.
+ whiteList = System.getProperty("http.nonProxyHosts", "").replace('|', ',');
+ }
+
+ if (host == null || host.trim().length() == 0) {
+ return;
+ }
+
+ Logger.log(getClass(), LogLevel.TRACE, protocol.toUpperCase()+" proxy {0}:{1} found using whitelist: {2}", host, port, whiteList);
+
+ ProxySelector protocolSelector = new FixedProxySelector(host, Integer.parseInt(port));
+ if (whiteList.trim().length() > 0) {
+ protocolSelector = new ProxyBypassListSelector(whiteList, protocolSelector);
+ }
+
+ ps.setSelector(protocol, protocolSelector);
+ }
+
+}
diff --git a/src/main/java/com/btr/proxy/search/wpad/WpadProxySearchStrategy.java b/src/main/java/com/btr/proxy/search/wpad/WpadProxySearchStrategy.java
new file mode 100644
index 0000000..cf01fd3
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/wpad/WpadProxySearchStrategy.java
@@ -0,0 +1,234 @@
+package com.btr.proxy.search.wpad;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.net.HttpURLConnection;
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.Socket;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.UnknownHostException;
+import java.util.List;
+import java.util.Properties;
+
+import com.btr.proxy.search.ProxySearchStrategy;
+import com.btr.proxy.util.Logger;
+import com.btr.proxy.util.Logger.LogLevel;
+import com.btr.proxy.util.ProxyException;
+import com.btr.proxy.util.ProxyUtil;
+
+
+/*****************************************************************************
+ * Uses automatic proxy script search (WPAD) to find an PAC file automatically.
+ * <p>
+ * Note: at the moment only the DNS name guessing schema is implemented.
+ * All others are missing.
+ * </p><p>
+ * For more information about WPAD:
+ * <a href="http://en.wikipedia.org/wiki/Web_Proxy_Autodiscovery_Protocol">Web_Proxy_Autodiscovery_Protocol</a>
+ * </p><p>
+ * Outdated RFC draft:
+ * <a href="http://www.web-cache.com/Writings/Internet-Drafts/draft-ietf-wrec-wpad-01.txt">draft-ietf-wrec-wpad-01.txt</a>
+ * </p>
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class WpadProxySearchStrategy implements ProxySearchStrategy {
+
+ /*************************************************************************
+ * Constructor
+ ************************************************************************/
+
+ public WpadProxySearchStrategy() {
+ super();
+ }
+
+ /*************************************************************************
+ * Loads the proxy settings from a PAC file.
+ * The location of the PAC file is determined automatically.
+ * @return a configured ProxySelector, null if none is found.
+ * @throws ProxyException on error.
+ ************************************************************************/
+
+ public ProxySelector getProxySelector() throws ProxyException {
+ try {
+ Logger.log(getClass(), LogLevel.TRACE, "Using WPAD to find a proxy");
+
+ String pacScriptUrl = detectScriptUrlPerDHCP();
+ if (pacScriptUrl == null) {
+ pacScriptUrl = detectScriptUrlPerDNS();
+ }
+ if (pacScriptUrl == null) {
+ return null;
+ }
+ Logger.log(getClass(), LogLevel.TRACE, "PAC script url found: {0}", pacScriptUrl);
+ return ProxyUtil.buildPacSelectorForUrl(pacScriptUrl);
+ } catch (IOException e) {
+ Logger.log(getClass(), LogLevel.ERROR, "Error during WPAD search.", e);
+ throw new ProxyException(e);
+ }
+ }
+
+ /*************************************************************************
+ * Loads the settings and stores them in a properties map.
+ * @return the settings.
+ ************************************************************************/
+
+ public Properties readSettings() {
+ try {
+ String pacScriptUrl = detectScriptUrlPerDHCP();
+ if (pacScriptUrl == null) {
+ pacScriptUrl = detectScriptUrlPerDNS();
+ }
+ if (pacScriptUrl == null) {
+ return null;
+ }
+ Properties result = new Properties();
+ result.setProperty("url", pacScriptUrl);
+ return result;
+ } catch (IOException e) {
+ // Ignore and return empty properties.
+ return new Properties();
+ }
+ }
+
+ /*************************************************************************
+ * Uses DNS to find the script URL.
+ * Attention: this detection method is known to have some severe security issues.
+ * @return the URL, null if not found.
+ ************************************************************************/
+
+ private String detectScriptUrlPerDNS() throws IOException {
+ String result = null;
+ // String fqdn = InetAddress.getLocalHost().getCanonicalHostName();
+
+ Logger.log(getClass(), LogLevel.TRACE, "Searching per DNS guessing.");
+ // Logger.log(getClass(), LogLevel.INFO, "fqdn: ", fqdn);
+
+ /** Reading address from "/etc/resolv.conf"; file looks like:
+ *
+ # Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)
+ # DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
+ nameserver 127.0.1.1
+ search ruf.uni-freiburg.de lp.ruf.uni-freiburg.de
+ */
+
+ FileReader fr = new FileReader("/etc/resolv.conf");
+ BufferedReader br = new BufferedReader(fr);
+
+ String input;
+ String[] addresses = null;
+ // using the 4th line of the file.
+ while ((input = br.readLine()) != null) {
+ if (input.startsWith("search")) {
+ // the first one is "search" and afterwards addresses are following.
+ addresses = input.substring(6).split(" ");
+ break;
+ }
+ }
+
+ if (addresses == null) {
+ addresses = new String[]{""};
+ }
+
+
+ for (int i = 0; i < addresses.length; ++i) {
+ String address = addresses[i];
+ int index = -1;
+ do {
+ if (index != -1) {
+ address = address.substring(index);
+ } else {
+ address = "";
+ }
+
+ // Try to connect to URL
+ try {
+ address = ".uni-freiburg.de";
+ URL lookupURL = new URL("http://wpad"+ address +"/wpad.dat");
+ Logger.log(getClass(), LogLevel.TRACE, "Trying url: {0}", lookupURL);
+
+ HttpURLConnection con = (HttpURLConnection) lookupURL.openConnection(Proxy.NO_PROXY);
+ con.setInstanceFollowRedirects(true);
+ con.setRequestProperty("accept", "application/x-ns-proxy-autoconfig");
+ if (con.getResponseCode() == 200) {
+ result = lookupURL.toString();
+ return result;
+ }
+ con.disconnect();
+ } catch (UnknownHostException e) {
+ Logger.log(getClass(), LogLevel.DEBUG, "Not available!");
+ // Not a real error, try next address
+ }
+ if (address.length() == 0) {
+ break;
+ }
+ index = address.indexOf('.', 1);
+ } while (true);
+ }
+
+ return null;
+ }
+
+ /*************************************************************************
+ * Uses DHCP to find the script URL.
+ * @return the URL, null if not found.
+ ************************************************************************/
+
+ private String detectScriptUrlPerDHCP() {
+ Logger.log(getClass(), LogLevel.DEBUG, "Searching per DHCP not supported yet.");
+ // TODO Rossi 28.04.2009 Not implemented yet.
+ return null;
+ }
+
+ // Main method for testing.
+ public static void main( String[] args ) throws IOException {
+ WpadProxySearchStrategy wPSS = new WpadProxySearchStrategy();
+// System.setProperty("com.btr.proxy.pac.overrideLocalIP", "10.0.0.1");
+ try {
+ ProxySelector pS = wPSS.getProxySelector();
+ ProxySelector.setDefault(pS);
+ List<Proxy> proxyList = pS.select(new URI("http://www.google.de"));
+
+ if (proxyList.isEmpty()) {
+ Logger.log(WpadProxySearchStrategy.class, LogLevel.INFO, "ProxyList is empty!");
+ } else {
+ Logger.log(WpadProxySearchStrategy.class, LogLevel.INFO, "proxyList contains: {0}", proxyList.toString());
+ }
+ } catch (ProxyException e) {
+ // TODO bjoern 28.10.2014 Auto-generated catch block
+ e.printStackTrace();
+ } catch (URISyntaxException e) {
+ // TODO bjoern 28.10.2014 Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ URL test = new URL("http://www.google.de");
+ URLConnection uc = test.openConnection();
+ BufferedReader br = new BufferedReader(new InputStreamReader(uc.getInputStream()));
+
+ String inputLine;
+ while ((inputLine = br.readLine()) != null) {
+ System.out.println(inputLine);
+ }
+
+ Socket socket = new Socket("www.google.de", 80);
+ BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
+ bw.write("GET / HTTP/1.1\r\nHost: www.google.de\r\nConnection: close\r\nAccept-Encoding: *\r\n\r\n");
+ bw.flush();
+
+ br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+ while ((inputLine = br.readLine()) != null) {
+ System.out.println(inputLine);
+ }
+
+ }
+
+}
diff --git a/src/main/java/com/btr/proxy/search/wpad/WpadProxySearchStrategyWithDHPC.java b/src/main/java/com/btr/proxy/search/wpad/WpadProxySearchStrategyWithDHPC.java
new file mode 100644
index 0000000..bb56cc0
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/wpad/WpadProxySearchStrategyWithDHPC.java
@@ -0,0 +1,315 @@
+package com.btr.proxy.search.wpad;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.HttpURLConnection;
+import java.net.InetAddress;
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.SocketException;
+import java.net.URL;
+import java.net.UnknownHostException;
+import java.util.Properties;
+import java.util.Random;
+import java.util.StringTokenizer;
+
+import com.btr.proxy.search.ProxySearchStrategy;
+import com.btr.proxy.search.wpad.dhcp.DHCPMessage;
+import com.btr.proxy.search.wpad.dhcp.DHCPOptions;
+import com.btr.proxy.search.wpad.dhcp.DHCPSocket;
+import com.btr.proxy.util.Logger;
+import com.btr.proxy.util.Logger.LogLevel;
+import com.btr.proxy.util.ProxyException;
+import com.btr.proxy.util.ProxyUtil;
+
+/*****************************************************************************
+ * Uses automatic proxy script search (WPAD) to find an PAC file automatically.
+ * <p>
+ * Note: at the moment only the DNS name guessing schema is implemented. All
+ * others are missing.
+ * </p>
+ * <p>
+ * For more information about WPAD: <a
+ * href="http://en.wikipedia.org/wiki/Web_Proxy_Autodiscovery_Protocol"
+ * >Web_Proxy_Autodiscovery_Protocol</a>
+ * </p>
+ * <p>
+ * Outdated RFC draft: <a href=
+ * "http://www.web-cache.com/Writings/Internet-Drafts/draft-ietf-wrec-wpad-01.txt"
+ * >draft-ietf-wrec-wpad-01.txt</a>
+ * </p>
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class WpadProxySearchStrategyWithDHPC implements ProxySearchStrategy {
+ DHCPSocket bindSocket = null;
+ byte hwaddr[] = new byte[16];
+ InetAddress serverIP;
+ int portNum;
+ boolean gSentinel;
+
+ /*************************************************************************
+ * Constructor
+ ************************************************************************/
+
+ public WpadProxySearchStrategyWithDHPC() {
+ super();
+ }
+
+ /*************************************************************************
+ * Loads the proxy settings from a PAC file. The location of the PAC file is
+ * determined automatically.
+ *
+ * @return a configured ProxySelector, null if none is found.
+ * @throws ProxyException
+ * on error.
+ ************************************************************************/
+
+ public ProxySelector getProxySelector() throws ProxyException {
+ try {
+ Logger.log(getClass(), LogLevel.TRACE, "Using WPAD to find a proxy");
+
+ String pacScriptUrl = detectScriptUrlPerDHCP();
+ if (pacScriptUrl == null) {
+ pacScriptUrl = detectScriptUrlPerDNS();
+ }
+ if (pacScriptUrl == null) {
+ return null;
+ }
+ Logger.log(getClass(), LogLevel.TRACE, "PAC script url found: {0}",
+ pacScriptUrl);
+ return ProxyUtil.buildPacSelectorForUrl(pacScriptUrl);
+ } catch (IOException e) {
+ Logger.log(getClass(), LogLevel.ERROR, "Error during WPAD search.",
+ e);
+ throw new ProxyException(e);
+ }
+ }
+
+ /*************************************************************************
+ * Loads the settings and stores them in a properties map.
+ *
+ * @return the settings.
+ ************************************************************************/
+
+ public Properties readSettings() {
+ try {
+ String pacScriptUrl = detectScriptUrlPerDHCP();
+ if (pacScriptUrl == null) {
+ pacScriptUrl = detectScriptUrlPerDNS();
+ }
+ if (pacScriptUrl == null) {
+ return null;
+ }
+ Properties result = new Properties();
+ result.setProperty("url", pacScriptUrl);
+ return result;
+ } catch (IOException e) {
+ // Ignore and return empty properties.
+ return new Properties();
+ }
+ }
+
+ /*************************************************************************
+ * Uses DNS to find the script URL. Attention: this detection method is
+ * known to have some severe security issues.
+ *
+ * @return the URL, null if not found.
+ ************************************************************************/
+
+ private String detectScriptUrlPerDNS() throws IOException {
+ String result = null;
+ // String fqdn = InetAddress.getLocalHost().getCanonicalHostName();
+
+ Logger.log(getClass(), LogLevel.TRACE, "Searching per DNS guessing.");
+ // Logger.log(getClass(), LogLevel.INFO, "fqdn: ", fqdn);
+
+ /**
+ * Reading address from "/etc/resolv.conf"; file looks like:
+ *
+ * # Dynamic resolv.conf(5) file for glibc resolver(3) generated by
+ * resolvconf(8) # DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE
+ * OVERWRITTEN nameserver 127.0.1.1 search ruf.uni-freiburg.de
+ * lp.ruf.uni-freiburg.de
+ */
+
+ FileReader fr = new FileReader("/etc/resolv.conf");
+ BufferedReader br = new BufferedReader(fr);
+
+ String input;
+ // using the 4th line of the file.
+ br.readLine();
+ br.readLine();
+ br.readLine();
+ input = br.readLine();
+
+ String[] addresses = input.split(" ");
+ // the first one is "search" and afterwards addresses are following.
+
+ for (int i = 0; i < addresses.length; ++i) {
+ String address = addresses[i];
+ int index = -1;
+ do {
+ address = address.substring(index + 1);
+
+ // if we are already on TLD level then escape
+ if (address.indexOf('.') == -1) {
+ break;
+ }
+
+ // Try to connect to URL
+ try {
+ URL lookupURL = new URL("http://wpad." + address
+ + "/wpad.dat");
+ Logger.log(getClass(), LogLevel.TRACE, "Trying url: {0}",
+ lookupURL);
+
+ HttpURLConnection con = (HttpURLConnection) lookupURL
+ .openConnection(Proxy.NO_PROXY);
+ con.setInstanceFollowRedirects(true);
+ con.setRequestProperty("accept",
+ "application/x-ns-proxy-autoconfig");
+ if (con.getResponseCode() == 200) {
+ result = lookupURL.toString();
+ return result;
+ }
+ con.disconnect();
+ } catch (UnknownHostException e) {
+ Logger.log(getClass(), LogLevel.DEBUG, "Not available!");
+ // Not a real error, try next address
+ }
+ index = address.indexOf('.');
+ } while (index != -1);
+ }
+
+ return null;
+ }
+
+ /*************************************************************************
+ * Uses DHCP to find the script URL.
+ *
+ * @return the URL, null if not found.
+ ************************************************************************/
+
+ private String detectScriptUrlPerDHCP() {
+ Logger.log(getClass(), LogLevel.DEBUG,
+ "Searching per DHCP not supported yet.");
+
+ // create socket.
+ try {
+ // bindSocket = new DHCPSocket(DHCPMessage.CLIENT_PORT);
+ // --> Not able to use ports under 1024 without being root.
+ this.bindSocket = new DHCPSocket(1068);
+ } catch (SocketException e) {
+ System.err.println(e);
+ return null;
+ }
+
+
+
+ return null;
+ }
+
+ // Sends DHCPDISCOVER Message and returns server message
+ private DHCPMessage SendDiscover() {
+ DHCPSocket bindSocket = null;
+ try {
+ bindSocket = new DHCPSocket(1068);
+ } catch (SocketException e1) {
+ // TODO bjoern 29.10.2014 Auto-generated catch block
+ e1.printStackTrace();
+ return null;
+ }
+
+ Random ranXid = new Random();
+ DHCPMessage messageOut = new DHCPMessage(DHCPMessage.BROADCAST_ADDR,
+ DHCPMessage.SERVER_PORT);
+ DHCPMessage messageIn = new DHCPMessage(DHCPMessage.BROADCAST_ADDR,
+ DHCPMessage.SERVER_PORT);
+ try {
+ // Specify type of message: 1 = request - message, 2 = reply - message
+ messageOut.setOp((byte) 1);
+ // set Hardware type: 1 = Ethernet, 6 = IEEE 802 Networks, ...
+ messageOut.setHtype((byte) 1); // 1 = ethernet
+ // set hardware address length: 6 for MAC address
+ messageOut.setHlen((byte) 6);
+ messageOut.setHops((byte) 0);
+ // set session ID for later comparing with incoming message.
+ messageOut.setXid(ranXid.nextInt());
+ messageOut.setSecs((short) 0);
+ messageOut.setFlags((short) 0x8000);
+ // set client hardware address, in this case my own hardware address of eth0.
+ messageOut.setChaddr(ChaddrToByte("D8:D3:85:80:8F:C9"));
+
+ byte[] opt = new byte[1];
+ byte[] wpadOpt = new byte[1];
+ opt[0] = DHCPMessage.DHCPDISCOVER;
+
+ // change message type
+ messageOut.setOption(DHCPOptions.OPTION_DHCP_MESSAGE_TYPE, opt);
+ wpadOpt[0] = DHCPMessage.OPTION_DHCP_WPAD;
+ messageOut.setOption(DHCPOptions.OPTION_DHCP_PARAMETER_REQUEST_LIST, wpadOpt);
+// messageOut.setOption(DHCPOptions.OPTION_DHCP_IP_ADRESS_REQUESTED,
+// offerMessageIn.getYiaddr());
+
+ bindSocket.send(messageOut); // send DHCPREQUEST
+ System.out.println("Sending DHCPDISCOVER ...");
+ boolean sentinal = true;
+ int counter = 0;
+ while (sentinal) {
+ if (counter == 2) { return null; }
+ if (bindSocket.receive(messageIn)) {
+ if (messageOut.getXid() == messageIn.getXid()) {
+ sentinal = false;
+ } else {
+ bindSocket.send(messageOut);
+ counter++;
+ }
+ } else {
+ bindSocket.send(messageOut);
+ counter++;
+ }
+ }
+ } catch (SocketException e) {
+ System.err.println(e);
+ return null;
+ } catch (IOException e) {
+ System.err.println(e);
+ return null;
+ } finally {
+ try {
+ bindSocket.close();
+ } catch (Exception e) {
+ // do nothing.
+ }
+ } // end catch
+ return messageIn;
+ }
+
+ // Main method for testing.
+ public static void main(String[] args) throws UnsupportedEncodingException {
+ WpadProxySearchStrategyWithDHPC wPSSDHCP = new WpadProxySearchStrategyWithDHPC();
+ DHCPMessage serverAnswer = wPSSDHCP.SendDiscover();
+
+ Logger.log(WpadProxySearchStrategyWithDHPC.class, LogLevel.INFO,
+ "DHCP serverAnswer: {0}", new String(serverAnswer.getOption(DHCPMessage.OPTION_DHCP_WPAD), "UTF-8"));
+ }
+
+ private byte[] ChaddrToByte(String inChaddr) {
+ StringTokenizer token = new StringTokenizer(inChaddr, ":");
+ Integer tempInt = new Integer(0);
+ byte outHwaddr[] = new byte[16];
+ int temp;
+ int i = 0;
+ while (i < 6) {
+ temp = tempInt.parseInt(token.nextToken(), 16);
+ outHwaddr[i] = (byte) temp;
+ i++;
+ }
+ return outHwaddr;
+ }
+
+}
diff --git a/src/main/java/com/btr/proxy/search/wpad/dhcp/DHCPMessage.java b/src/main/java/com/btr/proxy/search/wpad/dhcp/DHCPMessage.java
new file mode 100644
index 0000000..86323d5
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/wpad/dhcp/DHCPMessage.java
@@ -0,0 +1,880 @@
+package com.btr.proxy.search.wpad.dhcp;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * This class represents a DHCP Message.
+ *
+ * @author Jason Goldschmidt, Nick Stone and Simon Frankenberger
+ */
+public class DHCPMessage {
+ // -----------------------------------------------------------
+ // Constants
+ // -----------------------------------------------------------
+ /**
+ * Operation for a request
+ */
+ public static final byte OP_REQUEST = 1;
+
+ /**
+ * Operation for a reply
+ */
+ public static final byte OP_REPLY = 2;
+
+ /**
+ * Message Code representing a DHCPDISCOVER message
+ */
+ public static final byte DHCPDISCOVER = 1;
+
+ /**
+ * Message Code representing a DHCPOFFER message
+ */
+ public static final byte DHCPOFFER = 2;
+
+ /**
+ * Message Code representing a DHCPREQUEST message
+ */
+ public static final byte DHCPREQUEST = 3;
+
+ /**
+ * Message Code representing a DHCPDECLINE message
+ */
+ public static final byte DHCPDECLINE = 4;
+
+ /**
+ * Message Code representing a DHCPACK message
+ */
+ public static final byte DHCPACK = 5;
+
+ /**
+ * Message Code representing a DHCPNAK message
+ */
+ public static final byte DHCPNAK = 6;
+
+ /**
+ * Message Code representing a DHCPRELEASE message
+ */
+ public static final byte DHCPRELEASE = 7;
+
+ /**
+ * Message Code representing a DHCPINFORM message
+ */
+ public static final byte DHCPINFORM = 8;
+
+ /**
+ * Default DHCP client port
+ */
+ public static final int CLIENT_PORT = 68; // client port (by default)
+
+ /**
+ * Default DHCP server port
+ */
+ public static final int SERVER_PORT = 67; // server port (by default)
+
+ /**
+ * Broadcast Adress to send packets to
+ */
+ public static InetAddress BROADCAST_ADDR = null;
+
+ public static final byte OPTION_DHCP_WPAD = (byte) 252;
+
+
+
+ // -----------------------------------------------------------
+ // Fields defining a dhcp message
+ // -----------------------------------------------------------
+ /**
+ * Operation Code.<br>
+ * <br>
+ * Can either be {@link #OP_REQUEST} or {@link #OP_REPLY}.
+ */
+ private byte op;
+
+ /**
+ * Networktype as defined by
+ * <a href="http://tools.ietf.org/html/rfc1340#page-54">RFC1340 page 54</a>.
+ */
+ private byte htype;
+
+ /**
+ * Hardware address length (e.g. '6' for ethernet).
+ */
+ private byte hlen;
+
+ /**
+ * Client sets to zero, optionally used by relay-agents
+ * when booting via a relay-agent.
+ */
+ private byte hops;
+
+ /**
+ * Transaction ID, a random number chosen by the
+ * client, used by the client and server to associate
+ * messages and responses between a client and a
+ * server.
+ */
+ private int xid;
+
+ /**
+ * Filled in by client, seconds elapsed since client
+ * started trying to boot.
+ */
+ private short secs;
+
+ /**
+ * Flags for this message.<br>
+ * The leftmost bit is defined as the BROADCAST (B) flag.
+ */
+ private short flags;
+
+ /**
+ * Client IP address; filled in by client in
+ * DHCPREQUEST if verifying previously allocated
+ * configuration parameters.
+ */
+ private byte ciaddr[] = new byte[4];
+
+ /**
+ * 'your' (client) IP address.
+ */
+ private byte yiaddr[] = new byte[4];
+
+ /**
+ * IP address of next server to use in bootstrap;
+ * returned in DHCPOFFER, DHCPACK and DHCPNAK by
+ * server.
+ */
+ private byte siaddr[] = new byte[4];
+
+ /**
+ * Relay agent IP address, used in booting via a
+ * relay-agent.
+ */
+ private byte giaddr[] = new byte[4];
+
+ /**
+ * Client hardware address.
+ */
+ private byte chaddr[] = new byte[16];
+
+ /**
+ * Optional server host name, null terminated string.
+ */
+ private byte sname[] = new byte[64];
+
+ /**
+ * Boot file name, null terminated string; "generic"
+ * name or null in DHCPDISCOVER, fully qualified
+ * directory-path name in DHCPOFFER.
+ */
+ private byte file[] = new byte[128];
+
+ /**
+ * Internal representation of the given DHCP options.
+ */
+ private DHCPOptions optionsList = null;
+
+ /**
+ * global port variable for this message
+ */
+ private int gPort;
+
+ /**
+ * The destination IP-Adress of this message
+ */
+ private InetAddress destination_IP;
+
+ static {
+ try {
+ BROADCAST_ADDR = InetAddress.getByName("255.255.255.255");
+ // broadcast address(by default)
+ }
+ catch (UnknownHostException e) {
+ // Broadcast address must always exist
+ }
+ }
+
+ // -----------------------------------------------------------
+ // Constructors
+ // -----------------------------------------------------------
+
+ /**
+ * Creates empty DHCPMessage object,
+ * initializes the object, sets the host to the broadcast address,
+ * the local subnet, binds to the default server port.
+ */
+ public DHCPMessage() {
+ initialize();
+
+ this.destination_IP = BROADCAST_ADDR;
+ this.gPort = SERVER_PORT;
+ }
+
+ /**
+ * Copy constructor
+ * creates DHCPMessage from inMessage
+ *
+ * @param inMessage The message to be copied
+ */
+ public DHCPMessage(DHCPMessage inMessage) {
+ initialize();
+
+ this.destination_IP = BROADCAST_ADDR;
+ this.gPort = SERVER_PORT;
+ this.op = inMessage.getOp();
+ this.htype = inMessage.getHtype();
+ this.hlen = inMessage.getHlen();
+ this.hops = inMessage.getHops();
+ this.xid = inMessage.getXid();
+ this.secs = inMessage.getSecs();
+ this.flags = inMessage.getFlags();
+ this.ciaddr = inMessage.getCiaddr();
+ this.yiaddr = inMessage.getYiaddr();
+ this.siaddr = inMessage.getSiaddr();
+ this.giaddr = inMessage.getGiaddr();
+ this.chaddr = inMessage.getChaddr();
+ this.sname = inMessage.getSname();
+ this.file = inMessage.getFile();
+ this.optionsList.internalize(inMessage.getOptions());
+ }
+
+ /**
+ * Copy constructor
+ * creates DHCPMessage from inMessage and sets server and port.
+ *
+ * @param inMessage The message to be copied
+ * @param inServername The host name
+ * @param inPort The port number
+ */
+ public DHCPMessage(DHCPMessage inMessage, InetAddress inServername, int inPort) {
+ initialize();
+
+ this.destination_IP = inServername;
+ this.gPort = inPort;
+
+ this.op = inMessage.getOp();
+ this.htype = inMessage.getHtype();
+ this.hlen = inMessage.getHlen();
+ this.hops = inMessage.getHops();
+ this.xid = inMessage.getXid();
+ this.secs = inMessage.getSecs();
+ this.flags = inMessage.getFlags();
+ this.ciaddr = inMessage.getCiaddr();
+ this.yiaddr = inMessage.getYiaddr();
+ this.siaddr = inMessage.getSiaddr();
+ this.giaddr = inMessage.getGiaddr();
+ this.chaddr = inMessage.getChaddr();
+ this.sname = inMessage.getSname();
+ this.file = inMessage.getFile();
+ this.optionsList.internalize(inMessage.getOptions());
+ }
+
+ /**
+ * Copy constructor
+ * creates DHCPMessage from inMessage and sets server name.
+ *
+ * @param inMessage The message to be copied
+ * @param inServername The host name
+ */
+ public DHCPMessage(DHCPMessage inMessage, InetAddress inServername) {
+ initialize();
+
+ this.destination_IP = inServername;
+ this.gPort = SERVER_PORT;
+
+ this.op = inMessage.getOp();
+ this.htype = inMessage.getHtype();
+ this.hlen = inMessage.getHlen();
+ this.hops = inMessage.getHops();
+ this.xid = inMessage.getXid();
+ this.secs = inMessage.getSecs();
+ this.flags = inMessage.getFlags();
+ this.ciaddr = inMessage.getCiaddr();
+ this.yiaddr = inMessage.getYiaddr();
+ this.siaddr = inMessage.getSiaddr();
+ this.giaddr = inMessage.getGiaddr();
+ this.chaddr = inMessage.getChaddr();
+ this.sname = inMessage.getSname();
+ this.file = inMessage.getFile();
+ this.optionsList.internalize(inMessage.getOptions());
+ }
+
+ /**
+ * Creates an empty DHCPMessage object,
+ * initializes the object, sets the host to a specified host name,
+ * and binds to a specified port.
+ *
+ * @param inServername The host name
+ * @param inPort The port number
+ */
+ public DHCPMessage(InetAddress inServername, int inPort) {
+ initialize();
+
+ this.destination_IP = inServername;
+ this.gPort = inPort;
+ }
+
+ /**
+ * Creates an empty DHCPMessage object,
+ * initializes the object, sets the host to a specified host name,
+ * and binds to the default port.
+ *
+ * @param inServername The host name
+ */
+ public DHCPMessage(InetAddress inServername) {
+ initialize();
+
+ this.destination_IP = inServername;
+ this.gPort = SERVER_PORT;
+ }
+
+ /**
+ * Creates an empty DHCPMessage object,
+ * initializes the object, sets the host to the broadcast address,
+ * and binds to a specified port.
+ *
+ * @param inPort The port number
+ */
+ public DHCPMessage(int inPort) {
+ initialize();
+
+ this.destination_IP = BROADCAST_ADDR;
+ this.gPort = inPort;
+ }
+
+ /**
+ * Creates an empty DHCPMessage object,
+ * initializes the object with a specified byte array containing
+ * DHCP message information, sets the host to default host name, the
+ * local subnet, and bind to the default server port.
+ *
+ * @param ibuf The byte array to initialize DHCPMessage object
+ */
+ public DHCPMessage(byte[] ibuf) {
+ initialize();
+ internalize(ibuf);
+
+ this.destination_IP = BROADCAST_ADDR;
+ this.gPort = SERVER_PORT;
+ }
+
+ /**
+ * Creates an empty DHCPMessage object,
+ * initializes the object with a specified byte array containing
+ * DHCP message information, sets the host to specified host name,
+ * and binds to the specified port.
+ *
+ * @param ibuf The byte array to initialize DHCPMessage object
+ * @param inServername The hostname
+ * @param inPort The port number
+ */
+ public DHCPMessage(byte[] ibuf, InetAddress inServername, int inPort) {
+ initialize();
+ internalize(ibuf);
+
+ this.destination_IP = inServername;
+ this.gPort = inPort;
+ }
+
+ /**
+ * Creates an empty DHCPMessage object,
+ * initializes the object with a specified byte array containing
+ * DHCP message information, sets the host to broadcast address,
+ * and binds to the specified port.
+ *
+ * @param ibuf The byte array to initialize DHCPMessage object
+ * @param inPort The port number
+ */
+ public DHCPMessage(byte ibuf[], int inPort) {
+ initialize();
+ internalize(ibuf);
+
+ this.destination_IP = BROADCAST_ADDR;
+ this.gPort = inPort;
+ }
+
+ /**
+ * Creates an empty DHCPMessage object,
+ * initializes the object with a specified byte array containing
+ * DHCP message information, sets the host to specified host name,
+ * and binds to the specified port.
+ *
+ * @param ibuf The byte array to initialize DHCPMessage object
+ * @param inServername The hostname
+ */
+ public DHCPMessage(byte[] ibuf, InetAddress inServername) {
+ initialize();
+ internalize(ibuf);
+
+ this.destination_IP = inServername;
+ this.gPort = SERVER_PORT;
+ }
+
+ /**
+ * Creates a new DHCPMessage object from the giben DataInputStream.
+ *
+ * @param inStream The stream to read from
+ */
+ public DHCPMessage(DataInputStream inStream) {
+ initialize();
+
+ try {
+ this.op = inStream.readByte();
+ this.htype = inStream.readByte();
+ this.hlen = inStream.readByte();
+ this.hops = inStream.readByte();
+ this.xid = inStream.readInt();
+ this.secs = inStream.readShort();
+ this.flags = inStream.readShort();
+ inStream.readFully(this.ciaddr, 0, 4);
+ inStream.readFully(this.yiaddr, 0, 4);
+ inStream.readFully(this.siaddr, 0, 4);
+ inStream.readFully(this.giaddr, 0, 4);
+ inStream.readFully(this.chaddr, 0, 16);
+ inStream.readFully(this.sname, 0, 64);
+ inStream.readFully(this.file, 0, 128);
+ byte[] options = new byte[312];
+ inStream.readFully(options, 0, 312);
+ this.optionsList.internalize(options);
+ }
+ catch (IOException e) {
+ System.err.println(e);
+ }
+ }
+
+ // -----------------------------------------------------------
+ // Methods
+ // -----------------------------------------------------------
+ /**
+ * Initializes datamembers in the constructors
+ * every empty DHCPMessage object will by default contain these params.
+ * Initializes options array from linked list form.
+ */
+ private void initialize() {
+ this.optionsList = new DHCPOptions();
+ }
+
+ /**
+ * Converts a DHCPMessage object to a byte array.
+ *
+ * @return A byte array with information from DHCPMessage object,
+ * ready to send.
+ */
+ public synchronized byte[] externalize() {
+ ByteArrayOutputStream outBStream = new ByteArrayOutputStream();
+ DataOutputStream outStream = new DataOutputStream(outBStream);
+
+ try {
+ outStream.writeByte(this.op);
+ outStream.writeByte(this.htype);
+ outStream.writeByte(this.hlen);
+ outStream.writeByte(this.hops);
+ outStream.writeInt(this.xid);
+ outStream.writeShort(this.secs);
+ outStream.writeShort(this.flags);
+ outStream.write(this.ciaddr, 0, 4);
+ outStream.write(this.yiaddr, 0, 4);
+ outStream.write(this.siaddr, 0, 4);
+ outStream.write(this.giaddr, 0, 4);
+ outStream.write(this.chaddr, 0, 16);
+ outStream.write(this.sname, 0, 64);
+ outStream.write(this.file, 0, 128);
+
+ byte[] options = new byte[312];
+ if (this.optionsList == null) {
+ initialize();
+ }
+
+ options = this.optionsList.externalize();
+ outStream.write(options, 0, 312);
+ } catch (IOException e) {
+ System.err.println(e);
+ }
+
+ // extract the byte array from the Stream
+ byte data[] = outBStream.toByteArray();
+
+ return data;
+ }
+
+ /**
+ * Convert a specified byte array containing a DHCP message into a
+ * DHCPMessage object.
+ *
+ * @param ibuff Byte array to convert to a DHCPMessage object
+ * @return A DHCPMessage object with information from byte array.
+ */
+
+ public synchronized DHCPMessage internalize(byte[] ibuff) {
+ ByteArrayInputStream inBStream =
+ new ByteArrayInputStream(ibuff, 0, ibuff.length);
+ DataInputStream inStream = new DataInputStream(inBStream);
+
+ try {
+ this.op = inStream.readByte();
+ this.htype = inStream.readByte();
+ this.hlen = inStream.readByte();
+ this.hops = inStream.readByte();
+ this.xid = inStream.readInt();
+ this.secs = inStream.readShort();
+ this.flags = inStream.readShort();
+ inStream.readFully(this.ciaddr, 0, 4);
+ inStream.readFully(this.yiaddr, 0, 4);
+ inStream.readFully(this.siaddr, 0, 4);
+ inStream.readFully(this.giaddr, 0, 4);
+ inStream.readFully(this.chaddr, 0, 16);
+ inStream.readFully(this.sname, 0, 64);
+ inStream.readFully(this.file, 0, 128);
+
+ byte[] options = new byte[312];
+ inStream.readFully(options, 0, 312);
+ if (this.optionsList == null) {
+ initialize();
+ }
+
+ this.optionsList.internalize(options);
+ }
+ catch (IOException e) {
+ System.err.println(e);
+ } // end catch
+
+ return this;
+ }
+
+ /**
+ * Set message Op code / message type.
+ *
+ * @param inOp message Op code / message type
+ */
+ public void setOp(byte inOp) {
+ this.op = inOp;
+ }
+
+ /**
+ * Set hardware address type.
+ *
+ * @param inHtype hardware address type
+ */
+ public void setHtype(byte inHtype) {
+ this.htype = inHtype;
+ }
+
+ /**
+ * Set hardware address length.
+ *
+ * @param inHlen hardware address length
+ */
+ public void setHlen(byte inHlen) {
+ this.hlen = inHlen;
+ }
+
+ /**
+ * Set hops field.
+ *
+ * @param inHops hops field
+ */
+ public void setHops(byte inHops) {
+ this.hops = inHops;
+ }
+
+ /**
+ * Set transaction ID.
+ *
+ * @param inXid transactionID
+ */
+ public void setXid(int inXid) {
+ this.xid = inXid;
+ }
+
+ /**
+ * Set seconds elapsed since client began address acquisition or
+ * renewal process.
+ *
+ * @param inSecs Seconds elapsed since client began address acquisition
+ * or renewal process
+ */
+ public void setSecs(short inSecs) {
+ this.secs = inSecs;
+ }
+
+ /**
+ * Set flags field.
+ *
+ * @param inFlags flags field
+ */
+ public void setFlags(short inFlags) {
+ this.flags = inFlags;
+ }
+
+ /**
+ * Set client IP address.
+ *
+ * @param inCiaddr client IP address
+ */
+ public void setCiaddr(byte[] inCiaddr) {
+ this.ciaddr = inCiaddr;
+ }
+
+ /**
+ * Set 'your' (client) IP address.
+ *
+ * @param inYiaddr 'your' (client) IP address
+ */
+ public void setYiaddr(byte[] inYiaddr) {
+ this.yiaddr = inYiaddr;
+ }
+
+ /**
+ * Set address of next server to use in bootstrap.
+ *
+ * @param inSiaddr address of next server to use in bootstrap
+ */
+ public void setSiaddr(byte[] inSiaddr) {
+ this.siaddr = inSiaddr;
+ }
+
+ /**
+ * Set relay agent IP address.
+ *
+ * @param inGiaddr relay agent IP address
+ */
+ public void setGiaddr(byte[] inGiaddr) {
+ this.giaddr = inGiaddr;
+ }
+
+ /**
+ * Set client harware address.
+ *
+ * @param inChaddr client hardware address
+ */
+ public void setChaddr(byte[] inChaddr) {
+ this.chaddr = inChaddr;
+ }
+
+ /**
+ * Set optional server host name.
+ *
+ * @param inSname server host name
+ */
+ public void setSname(byte[] inSname) {
+ this.sname = inSname;
+ }
+
+ /**
+ * Set boot file name.
+ *
+ * @param inFile boot file name
+ */
+ public void setFile(byte[] inFile) {
+ this.file = inFile;
+ }
+
+ /**
+ * Set message destination port.
+ *
+ * @param inPortNum port on message destination host
+ */
+ public void setPort(int inPortNum) {
+ this.gPort = inPortNum;
+ }
+
+ /**
+ * Set message destination IP
+ * @param inHost string representation of message destination IP or
+ * hostname
+ */
+ public void setDestinationHost(String inHost) {
+ try {
+ this.destination_IP = InetAddress.getByName(inHost);
+ }
+ catch (Exception e) {
+ System.err.println(e);
+ }
+ }
+
+ /**
+ * @return message Op code / message type.
+ */
+ public byte getOp() {
+ return this.op;
+ }
+
+ /**
+ * @return hardware address type.
+ */
+ public byte getHtype() {
+ return this.htype;
+ }
+
+ /**
+ * @return hardware address length.
+ */
+ public byte getHlen() {
+ return this.hlen;
+ }
+
+ /**
+ * @return hops field.
+ */
+ public byte getHops() {
+ return this.hops;
+ }
+
+ /**
+ * @return transaction ID.
+ */
+ public int getXid() {
+ return this.xid;
+ }
+
+ /**
+ * @return seconds elapsed since client began address
+ * acquisition or renewal process.
+ */
+ public short getSecs() {
+ return this.secs;
+ }
+
+ /**
+ * @return flags field.
+ */
+ public short getFlags() {
+ return this.flags;
+ }
+
+ /**
+ * @return client IP address.
+ */
+ public byte[] getCiaddr() {
+ return this.ciaddr;
+ }
+
+ /**
+ * @return 'your' (client) IP address.
+ */
+ public byte[] getYiaddr() {
+ return this.yiaddr;
+ }
+
+ /**
+ * @return address of next server to use in bootstrap.
+ */
+ public byte[] getSiaddr() {
+ return this.siaddr;
+ }
+
+ /**
+ * @return relay agent IP address.
+ */
+ public byte[] getGiaddr() {
+ return this.giaddr;
+ }
+
+ /**
+ * @return client harware address.
+ */
+ public byte[] getChaddr() {
+ return this.chaddr;
+ }
+
+ /**
+ * @return optional server host name.
+ */
+ public byte[] getSname() {
+ return this.sname;
+ }
+
+ /**
+ * @return boot file name.
+ */
+ public byte[] getFile() {
+ return this.file;
+ }
+
+ /**
+ * @return a byte array containing options
+ */
+ public byte[] getOptions() {
+ if (this.optionsList == null) {
+ initialize();
+ }
+ return this.optionsList.externalize();
+ }
+
+ /**
+ * @return An interger representation of the message
+ * destination port
+ */
+ public int getPort() {
+ return this.gPort;
+ }
+
+ /**
+ * Get message destination hostname
+ *
+ * @return A string representing the hostname of the
+ * message destination server
+ */
+ public String getDestinationAddress() {
+ return this.destination_IP.getHostAddress();
+ }
+
+ /**
+ * Sets DHCP options in DHCPMessage. If option already exists
+ * then remove old option and insert a new one.
+ *
+ * @param inOptNum option number
+ * @param inOptionData option data
+ */
+ public void setOption(int inOptNum, byte[] inOptionData) {
+ this.optionsList.setOption((byte) inOptNum, inOptionData);
+ }
+
+ /**
+ * Returns specified DHCP option that matches the input code. Null is
+ * returned if option is not set.
+ *
+ * @param inOptNum option number
+ *
+ * @return the option matching input code
+ */
+ public byte[] getOption(int inOptNum) {
+ if (this.optionsList == null) {
+ initialize();
+ }
+ return this.optionsList.getOption((byte) inOptNum);
+ }
+
+ /**
+ * Removes the specified DHCP option that matches the input code.
+ *
+ * @param inOptNum option number
+ */
+ public void removeOption(int inOptNum) {
+ if (this.optionsList == null) {
+ initialize();
+ }
+ this.optionsList.removeOption((byte) inOptNum);
+ }
+
+ /**
+ * Report whether or not the input option is set.
+ *
+ * @param inOptNum option number
+ *
+ * @return is the given option set?
+ */
+ public boolean IsOptSet(int inOptNum) {
+ if (this.optionsList == null) {
+ initialize();
+ }
+
+ return this.optionsList.contains((byte) inOptNum);
+ }
+}
diff --git a/src/main/java/com/btr/proxy/search/wpad/dhcp/DHCPOptions.java b/src/main/java/com/btr/proxy/search/wpad/dhcp/DHCPOptions.java
new file mode 100644
index 0000000..1a07efb
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/wpad/dhcp/DHCPOptions.java
@@ -0,0 +1,235 @@
+package com.btr.proxy.search.wpad.dhcp;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+/**
+ * This class represents a linked list of options for a DHCP message.
+ * Its purpose is to ease option handling such as add, remove or change.
+ *
+ * @author Jason Goldschmidt and Simon Frankenberger
+ */
+public class DHCPOptions {
+ public static final int OPTION_PAD = 0;
+ public static final int OPTION_NETMASK = 1;
+ public static final int OPTION_TIME_OFFSET = 2;
+ public static final int OPTION_ROUTERS = 3;
+ public static final int OPTION_TIME_SERVERS = 4;
+ public static final int OPTION_NAME_SERVERS = 5;
+ public static final int OPTION_DNS_SERVERS = 6;
+ public static final int OPTION_LOG_SERVERS = 7;
+ public static final int OPTION_COOKIE_SERVERS = 8;
+ public static final int OPTION_LPR_SERVERS = 9;
+ public static final int OPTION_IMPRESS_SERVERS = 10;
+ public static final int OPTION_RESSOURCE_LOCATION_SERVERS = 11;
+ public static final int OPTION_HOSTNAME = 12;
+ public static final int OPTION_BOOT_FILESIZE = 13;
+ public static final int OPTION_MERIT_DUMPFILE = 14;
+ public static final int OPTION_DOMAIN_NAME = 15;
+ public static final int OPTION_SWAP_SERVER = 16;
+ public static final int OPTION_ROOT_PATH = 17;
+ public static final int OPTION_EXTENSIONS_PATH = 18;
+ public static final int OPTION_END = 255;
+
+ public static final int OPTION_IP_HOST_FORWARDING_ENABLE = 19;
+ public static final int OPTION_IP_HOST_NON_LOCAL_SOURCE_ROUTING_ENABLE = 20;
+ public static final int OPTION_IP_HOST_POLICY_FILTERS = 21;
+ public static final int OPTION_IP_HOST_MAXIMUM_DATAGRAM_REASSEMBLY_SIZE = 22;
+ public static final int OPTION_IP_HOST_DEFAULT_TTL = 23;
+ public static final int OPTION_IP_HOST_MTU_AGEING_TIMEOUT = 24;
+ public static final int OPTION_IP_HOST_MTU_PLATEAU_TABLE = 25;
+
+ public static final int OPTION_IP_INTERFACE_MTU = 26;
+ public static final int OPTION_IP_INTERFACE_ALL_SUBNETS_LOCAL_ENABLE = 27;
+ public static final int OPTION_IP_INTERFACE_BROADCAST_ADDRESS = 28;
+ public static final int OPTION_IP_INTERFACE_PERFORM_MASK_DISCOVERY_ENABLE = 29;
+ public static final int OPTION_IP_INTERFACE_MASK_SUPPLIER_ENABLE = 30;
+ public static final int OPTION_IP_INTERFACE_PERFORM_ROUTER_DISCOVERY_ENABLE = 31;
+ public static final int OPTION_IP_INTERFACE_ROUTER_SOLICITATION_ADDRESS = 32;
+ public static final int OPTION_IP_INTERFACE_STATIC_ROUTES = 33;
+
+ public static final int OPTION_LINK_TRAILER_ENCAPSULATION_ENABLE = 34;
+ public static final int OPTION_LINK_ARP_CACHE_TIMEOUT = 35;
+ public static final int OPTION_LINK_ETHERNET_ENCAPSULATION_ENABLE = 36;
+
+ public static final int OPTION_TCP_DEFAULT_TTL = 37;
+ public static final int OPTION_TCP_KEEP_ALIVE_INTERVAL = 38;
+ public static final int OPTION_TCP_KEEP_ALIVE_GERBAGE_ENABLE = 39;
+
+ public static final int OPTION_NIS_DOMAIN = 40;
+ public static final int OPTION_NIS_SERVERS = 41;
+ public static final int OPTION_NTP_SERVERS = 42;
+
+ public static final int OPTION_SERVICE_VENDOR_SPECIFIC_INFORMATIONS = 43;
+ public static final int OPTION_SERVICE_NETBOIS_NAME_SERVERS = 44;
+ public static final int OPTION_SERVICE_NETBOIS_DATAGRAM_DISTRIBUTION_SERVERS = 45;
+ public static final int OPTION_SERVICE_NETBOIS_NODE_TYPE = 46;
+ public static final int OPTION_SERVICE_NETBOIS_SCOPE_TYPE = 47;
+ public static final int OPTION_SERVICE_X_FONT_SERVERS = 48;
+ public static final int OPTION_SERVICE_X_DISPLAY_MANAGERS = 49;
+
+ public static final int OPTION_DHCP_IP_ADRESS_REQUESTED = 50;
+ public static final int OPTION_DHCP_IP_LEASE_TIME = 51;
+ public static final int OPTION_DHCP_OVERLOAD = 52;
+ public static final int OPTION_DHCP_MESSAGE_TYPE = 53;
+ public static final int OPTION_DHCP_SERVER_IDENTIFIER = 54;
+ public static final int OPTION_DHCP_PARAMETER_REQUEST_LIST = 55;
+ public static final int OPTION_DHCP_MESSAGE = 56;
+ public static final int OPTION_DHCP_MAXIMUM_MESSAGE_SIZE = 57;
+ public static final int OPTION_DHCP_RENEWAL_TIME = 58;
+ public static final int OPTION_DHCP_REBIND_TIME = 59;
+ public static final int OPTION_DHCP_CLASS_IDENTIFIER = 60;
+ public static final int OPTION_DHCP_CLIENT_IDENTIFIER = 61;
+
+
+ /**
+ *This inner class represent an entry in the Option Table
+ */
+
+ class DHCPOptionsEntry {
+ protected byte code;
+ protected byte length;
+ protected byte content[];
+
+ public DHCPOptionsEntry(byte entryCode, byte entryLength,
+ byte entryContent[]) {
+ this.code = entryCode;
+ this.length = entryLength;
+ this.content = entryContent;
+ }
+
+ @Override
+ public String toString() {
+ return "Code: " + this.code + "\nContent: " + new String(this.content);
+ }
+ }
+
+ private Hashtable<Byte, DHCPOptionsEntry> optionsTable = null;
+
+ public DHCPOptions() {
+ this.optionsTable = new Hashtable<Byte, DHCPOptionsEntry>();
+ }
+
+ /**
+ * Removes option with specified bytecode
+ * @param entryCode The code of option to be removed
+ */
+
+ public void removeOption(byte entryCode) {
+ this.optionsTable.remove(new Byte(entryCode));
+ }
+
+ /**
+ * Returns true if option code is set in list; false otherwise
+ * @param entryCode The node's option code
+ * @return true if option is set, otherwise false
+ */
+ public boolean contains(byte entryCode) {
+ return this.optionsTable.containsKey(new Byte(entryCode));
+ }
+
+ /**
+ * Determines if list is empty
+ * @return true if there are no options set, otherwise false
+ */
+ public boolean isEmpty() {
+ return this.optionsTable.isEmpty();
+ }
+
+ /**
+ * Fetches value of option by its option code
+ * @param entryCode The node's option code
+ * @return byte array containing the value of option entryCode.
+ * null is returned if option is not set.
+ */
+ public byte[] getOption(byte entryCode) {
+ if (this.contains(entryCode)) {
+ DHCPOptionsEntry ent = this.optionsTable.get(new Byte(entryCode));
+ return ent.content;
+ }
+ else {
+ return null;
+ }
+ }
+
+ /**
+ * Changes an existing option to new value
+ * @param entryCode The node's option code
+ * @param value Content of node option
+ */
+ public void setOption(byte entryCode, byte value[]) {
+ DHCPOptionsEntry opt = new DHCPOptionsEntry(entryCode, (byte) value.length, value);
+ this.optionsTable.put(new Byte(entryCode), opt);
+ }
+
+ /**
+ * Returns the option value of a specified option code in a byte array
+ * @param length Length of option content
+ * @param position Location in array of option node
+ * @param options The byte array of options
+ * @return byte array containing the value for the option
+ */
+ private byte[] getArrayOption(int length, int position, byte options[]) {
+ byte value[] = new byte[length];
+ for (int i = 0; i < length; i++) {
+ value[i] = options[position + i];
+ }
+ return value;
+ }
+
+ /**
+ * Converts an options byte array to a linked list
+ * @param optionsArray The byte array representation of the options list
+ */
+ public void internalize(byte[] optionsArray) {
+
+ /* Assume options valid and correct */
+ int pos = 4; // ignore vendor magic cookie
+ byte code, length;
+ byte value[];
+
+ while (optionsArray[pos] != (byte) 255) { // until end option
+ code = optionsArray[pos++];
+ length = optionsArray[pos++];
+ value = getArrayOption(length, pos, optionsArray);
+ setOption(code, value);
+ pos += length; // increment position pointer
+ }
+ }
+
+ /**
+ * Converts a linked options list to a byte array
+ * @return array representation of optionsTable
+ */
+ // todo provide overflow return
+ public byte[] externalize() {
+ byte[] options = new byte[312];
+
+ options[0] = (byte) 99; // insert vendor magic cookie
+ options[1] = (byte) 130;
+ options[2] = (byte) 83;
+ options[3] = (byte) 99;
+
+ int position = 4;
+ Enumeration<DHCPOptionsEntry> e = this.optionsTable.elements();
+
+ while (e.hasMoreElements()) {
+ DHCPOptionsEntry entry = e.nextElement();
+ options[position++] = entry.code;
+ options[position++] = entry.length;
+ for (int i = 0; i < entry.length; ++i) {
+ options[position++] = entry.content[i];
+ }
+ }
+
+ options[position] = (byte) 255; // insert end option
+ return options;
+ }
+
+ /**
+ * Prints the options linked list: For testing only.
+ */
+ public void printList() {
+ System.out.println(this.optionsTable.toString());
+ }
+}
diff --git a/src/main/java/com/btr/proxy/search/wpad/dhcp/DHCPSocket.java b/src/main/java/com/btr/proxy/search/wpad/dhcp/DHCPSocket.java
new file mode 100644
index 0000000..6dbe2bf
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/wpad/dhcp/DHCPSocket.java
@@ -0,0 +1,107 @@
+package com.btr.proxy.search.wpad.dhcp;
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+
+/**
+ * This class represents a Socket for sending DHCP Messages
+ *
+ * @author Jason Goldschmidt and Simon Frankenberger
+ *
+ * @see java.net.DatagramSocket
+ */
+
+public class DHCPSocket extends DatagramSocket {
+ /**
+ * Default socket timeout (1 second)
+ */
+ private int SOCKET_TIMEOUT = 5000;
+
+ /**
+ * Default MTU (Maximum Transmission Unit) for ethernet (in bytes)
+ */
+ private int mtu = 1500;
+
+ /**
+ * Constructor for creating DHCPSocket on a specific port on the local
+ * machine.
+ *
+ * @param inPort The port for the application to bind.
+ *
+ * @throws SocketException As thrown by the {@link Socket} constructor
+ */
+ public DHCPSocket(int inPort) throws SocketException {
+ super(inPort);
+ setSoTimeout(this.SOCKET_TIMEOUT);
+ }
+
+ /**
+ * Sets the Maximum Transfer Unit for the UDP DHCP Packets to be set.
+ *
+ * @param inSize Integer representing desired MTU
+ */
+ public void setMTU(int inSize) {
+ this.mtu = inSize;
+ }
+
+ /**
+ * Returns the set MTU for this socket
+ *
+ * @return The Maximum Transfer Unit set for this socket
+ */
+ public int getMTU() {
+ return this.mtu;
+ }
+
+ /**
+ * Sends a DHCPMessage object to a predifined host.
+ *
+ * @param inMessage Well-formed DHCPMessage to be sent to a server
+ *
+ * @throws IOException If the message could not be sent.
+ */
+ public synchronized void send(DHCPMessage inMessage) throws IOException {
+ byte data[] = new byte[this.mtu];
+ data = inMessage.externalize();
+ InetAddress dest = null;
+ try {
+ dest = InetAddress.getByName(inMessage.getDestinationAddress());
+ }
+ catch (UnknownHostException e) {
+ }
+
+ DatagramPacket outgoing = new DatagramPacket(data, data.length, dest,
+ inMessage.getPort());
+
+ send(outgoing); // send outgoing message
+ }
+
+ /**
+ * Receives a datagram packet containing a DHCP Message into
+ * a DHCPMessage object.
+ *
+ * @return <code>true</code> if message is received,
+ * <code>false</code> if timeout occurs.
+ * @param outMessage DHCPMessage object to receive new message into
+ */
+ public synchronized boolean receive(DHCPMessage outMessage) {
+ try {
+ DatagramPacket incoming = new DatagramPacket(new byte[this.mtu],
+ this.mtu);
+ //gSocket.
+ receive(incoming); // block on receive for SOCKET_TIMEOUT
+
+ outMessage.internalize(incoming.getData());
+ }
+ catch (java.io.IOException e) {
+ e.printStackTrace();
+ return false;
+ } // end catch
+ return true;
+ }
+}