From e70ee5b59306ea37dd0c72603c61b33b1555def9 Mon Sep 17 00:00:00 2001 From: Björn Hagemeister Date: Tue, 11 Nov 2014 14:40:18 +0100 Subject: Added proxy java classes. --- doc/PacIPv6.txt | 6 + doc/changelog.txt | 56 + doc/internet_explorer_info.txt | 4 + doc/mac_osx.txt | 7 + doc/misc_links.txt | 5 + doc/pac_files_docu.html | 1182 ++++++++++++++++++++ misc/logo.png | Bin 0 -> 11923 bytes misc/logo.xcf | Bin 0 -> 26871 bytes misc/splash.xcf | Bin 0 -> 350958 bytes misc/stylesheet.css | 50 + pom.xml | 214 ++++ .../java/com/btr/proxy/search/ProxySearch.java | 258 +++++ .../com/btr/proxy/search/ProxySearchStrategy.java | 22 + .../browser/firefox/FirefoxProfileSource.java | 23 + .../firefox/FirefoxProxySearchStrategy.java | 267 +++++ .../browser/firefox/FirefoxSettingParser.java | 79 ++ .../browser/firefox/LinuxFirefoxProfileSource.java | 46 + .../browser/firefox/WinFirefoxProfileSource.java | 71 ++ .../search/browser/ie/IELocalByPassFilter.java | 28 + .../search/browser/ie/IEProxySearchStrategy.java | 213 ++++ .../search/desktop/DesktopProxySearchStrategy.java | 69 ++ .../desktop/gnome/GnomeProxySearchStrategy.java | 353 ++++++ .../desktop/gnome/ProxySchemasGSettingsAccess.java | 60 + .../search/desktop/kde/KdeProxySearchStrategy.java | 198 ++++ .../search/desktop/kde/KdeSettingsParser.java | 132 +++ .../search/desktop/osx/OsxProxySearchStrategy.java | 325 ++++++ .../btr/proxy/search/desktop/win/DLLManager.java | 171 +++ .../proxy/search/desktop/win/Win32IESettings.java | 68 ++ .../proxy/search/desktop/win/Win32ProxyUtils.java | 88 ++ .../search/desktop/win/WinProxySearchStrategy.java | 52 + .../proxy/search/env/EnvProxySearchStrategy.java | 130 +++ .../proxy/search/java/JavaProxySearchStrategy.java | 133 +++ .../proxy/search/wpad/WpadProxySearchStrategy.java | 234 ++++ .../wpad/WpadProxySearchStrategyWithDHPC.java | 315 ++++++ .../btr/proxy/search/wpad/dhcp/DHCPMessage.java | 880 +++++++++++++++ .../btr/proxy/search/wpad/dhcp/DHCPOptions.java | 235 ++++ .../com/btr/proxy/search/wpad/dhcp/DHCPSocket.java | 107 ++ .../btr/proxy/selector/direct/NoProxySelector.java | 70 ++ .../proxy/selector/fixed/FixedProxySelector.java | 69 ++ .../proxy/selector/fixed/FixedSocksSelector.java | 27 + .../proxy/selector/misc/BufferedProxySelector.java | 126 +++ .../selector/misc/ProtocolDispatchSelector.java | 115 ++ .../selector/misc/ProxyListFallbackSelector.java | 150 +++ .../proxy/selector/pac/JavaxPacScriptParser.java | 147 +++ .../btr/proxy/selector/pac/PacProxySelector.java | 184 +++ .../btr/proxy/selector/pac/PacScriptMethods.java | 656 +++++++++++ .../btr/proxy/selector/pac/PacScriptParser.java | 29 + .../btr/proxy/selector/pac/PacScriptSource.java | 31 + .../selector/pac/ProxyEvaluationException.java | 51 + .../proxy/selector/pac/RhinoPacScriptParser.java | 318 ++++++ .../btr/proxy/selector/pac/ScriptAvailability.java | 46 + .../com/btr/proxy/selector/pac/ScriptMethods.java | 256 +++++ .../btr/proxy/selector/pac/UrlPacScriptSource.java | 270 +++++ .../selector/whitelist/DefaultWhiteListParser.java | 78 ++ .../proxy/selector/whitelist/HostnameFilter.java | 93 ++ .../selector/whitelist/IPv4WithSubnetChecker.java | 29 + .../proxy/selector/whitelist/IpRangeFilter.java | 83 ++ .../whitelist/ProxyBypassListSelector.java | 84 ++ .../whitelist/UseProxyWhiteListSelector.java | 74 ++ .../proxy/selector/whitelist/WhiteListParser.java | 24 + src/main/java/com/btr/proxy/test/ProxyTester.java | 176 +++ .../java/com/btr/proxy/util/EmptyXMLResolver.java | 26 + src/main/java/com/btr/proxy/util/Logger.java | 87 ++ src/main/java/com/btr/proxy/util/PListParser.java | 544 +++++++++ src/main/java/com/btr/proxy/util/PlatformUtil.java | 114 ++ .../java/com/btr/proxy/util/ProxyException.java | 50 + src/main/java/com/btr/proxy/util/ProxyUtil.java | 84 ++ src/main/java/com/btr/proxy/util/UriFilter.java | 21 + src/main/resources/lib/gsettings-amd64.so | Bin 0 -> 11979 bytes src/main/resources/lib/gsettings-x86.so | Bin 0 -> 11822 bytes src/main/resources/lib/proxy_util_amd64.dll | Bin 0 -> 40448 bytes src/main/resources/lib/proxy_util_ia64.dll | Bin 0 -> 91136 bytes src/main/resources/lib/proxy_util_w32.dll | Bin 0 -> 43520 bytes src/test/java/com/btr/proxy/Examples.java | 47 + src/test/java/com/btr/proxy/TestUtil.java | 61 + .../com/btr/proxy/search/browser/FirefoxTest.java | 144 +++ .../java/com/btr/proxy/search/browser/IeTest.java | 59 + .../search/desktop/DesktopProxySearchTest.java | 45 + .../proxy/search/desktop/win/DLLManagerTest.java | 78 ++ .../proxy/search/gnome/GnomeProxySearchTest.java | 117 ++ .../btr/proxy/search/java/JavaProxySearchTest.java | 137 +++ .../btr/proxy/search/kde/KdeProxySearchTest.java | 177 +++ .../btr/proxy/selector/fixed/FixedProxyTest.java | 56 + .../proxy/selector/java/JavaProxySelectorTest.java | 28 + .../proxy/selector/misc/ProtocolDispatchTest.java | 87 ++ .../misc/ProxyListFallbackSelectorTest.java | 94 ++ .../selector/pac/JavaxPacScriptParserTest.java | 119 ++ .../btr/proxy/selector/pac/PacPerProtocolTest.java | 47 + .../btr/proxy/selector/pac/PacProxyDebugging.java | 103 ++ .../proxy/selector/pac/PacProxySelectorTest.java | 133 +++ .../proxy/selector/pac/PacScriptMethodsTest.java | 165 +++ .../selector/pac/RhinoPacScriptParserTest.java | 114 ++ .../proxy/selector/pac/UrlPacScriptSourceTest.java | 34 + .../btr/proxy/selector/whitelist/NoProxyTest.java | 108 ++ .../java/com/btr/proxy/util/PListParserTest.java | 101 ++ .../java/com/btr/proxy/util/ProxyUtilTest.java | 78 ++ .../java/com/btr/proxy/util/UriFilterTest.java | 122 ++ .../.mozilla/firefox/9f1uyzzu.default/prefs.js | 76 ++ .../.mozilla/firefox/9f1uyzzu.default/prefs.js | 77 ++ .../.mozilla/firefox/9f1uyzzu.default/prefs.js | 76 ++ .../.mozilla/firefox/9f1uyzzu.default/prefs.js | 77 ++ .../.gconf/system/http_proxy/%gconf.xml | 23 + .../gnome_manual/.gconf/system/proxy/%gconf.xml | 36 + .../gnome_none/.gconf/system/http_proxy/%gconf.xml | 21 + .../.gconf/system/http_proxy/%gconf.xml | 26 + .../.gconf/system/proxy/%gconf.xml | 39 + .../.gconf/system/http_proxy/%gconf.xml | 26 + .../.gconf/system/proxy/%gconf.xml | 36 + .../data/kde_env/.kde/share/config/kioslaverc | 17 + .../data/kde_manual/.kde/share/config/kioslaverc | 17 + .../data/kde_none/.kde/share/config/kioslaverc | 17 + .../kde_pac_script/.kde/share/config/kioslaverc | 17 + .../kde_white_list/.kde/share/config/kioslaverc | 17 + src/test/resources/data/osx/osx_all.plist | 144 +++ src/test/resources/data/osx/osx_manual.plist | 144 +++ src/test/resources/data/osx/osx_pac.plist | 144 +++ src/test/resources/data/pac/test1.pac | 4 + src/test/resources/data/pac/test2.pac | 10 + src/test/resources/data/pac/testDateRange.pac | 11 + src/test/resources/data/pac/testLocalIP.pac | 4 + src/test/resources/data/pac/testMultiProxy.pac | 4 + src/test/resources/data/pac/testTimeRange.pac | 11 + src/test/resources/data/pac/testWeekDay.pac | 10 + src/test/resources/data/win/proxy_util_amd64.dll | 0 src/test/resources/data/win/proxy_util_ia64.dll | 0 src/test/resources/data/win/proxy_util_w32.dll | 0 src/test/resources/data/wpad/wpad.pac | 4 + src_native/gnome/ProxySchemasGSettingsAccess.c | 117 ++ src_native/gnome/ProxySchemasGSettingsAccess.h | 21 + src_native/gnome/make | 2 + src_native/win/proxy_util_w23/compile.bat | 1 + src_native/win/proxy_util_w23/dllmain.cpp | 19 + .../win/proxy_util_w23/proxy_util_w23.vcproj | 255 +++++ src_native/win/proxy_util_w23/proxy_util_w32.cpp | 205 ++++ src_native/win/proxy_util_w23/proxy_util_w32.h | 50 + src_native/win/proxy_util_w23/stdafx.cpp | 2 + src_native/win/proxy_util_w23/stdafx.h | 11 + src_native/win/proxy_util_w23/targetver.h | 28 + src_native/win/proxy_util_w32.sln | 20 + src_native/win/proxy_util_w32.suo | Bin 0 -> 41984 bytes 140 files changed, 14266 insertions(+) create mode 100644 doc/PacIPv6.txt create mode 100644 doc/changelog.txt create mode 100644 doc/internet_explorer_info.txt create mode 100644 doc/mac_osx.txt create mode 100644 doc/misc_links.txt create mode 100644 doc/pac_files_docu.html create mode 100644 misc/logo.png create mode 100644 misc/logo.xcf create mode 100644 misc/splash.xcf create mode 100644 misc/stylesheet.css create mode 100644 pom.xml create mode 100644 src/main/java/com/btr/proxy/search/ProxySearch.java create mode 100644 src/main/java/com/btr/proxy/search/ProxySearchStrategy.java create mode 100644 src/main/java/com/btr/proxy/search/browser/firefox/FirefoxProfileSource.java create mode 100644 src/main/java/com/btr/proxy/search/browser/firefox/FirefoxProxySearchStrategy.java create mode 100644 src/main/java/com/btr/proxy/search/browser/firefox/FirefoxSettingParser.java create mode 100644 src/main/java/com/btr/proxy/search/browser/firefox/LinuxFirefoxProfileSource.java create mode 100644 src/main/java/com/btr/proxy/search/browser/firefox/WinFirefoxProfileSource.java create mode 100644 src/main/java/com/btr/proxy/search/browser/ie/IELocalByPassFilter.java create mode 100644 src/main/java/com/btr/proxy/search/browser/ie/IEProxySearchStrategy.java create mode 100644 src/main/java/com/btr/proxy/search/desktop/DesktopProxySearchStrategy.java create mode 100644 src/main/java/com/btr/proxy/search/desktop/gnome/GnomeProxySearchStrategy.java create mode 100644 src/main/java/com/btr/proxy/search/desktop/gnome/ProxySchemasGSettingsAccess.java create mode 100644 src/main/java/com/btr/proxy/search/desktop/kde/KdeProxySearchStrategy.java create mode 100644 src/main/java/com/btr/proxy/search/desktop/kde/KdeSettingsParser.java create mode 100644 src/main/java/com/btr/proxy/search/desktop/osx/OsxProxySearchStrategy.java create mode 100644 src/main/java/com/btr/proxy/search/desktop/win/DLLManager.java create mode 100644 src/main/java/com/btr/proxy/search/desktop/win/Win32IESettings.java create mode 100644 src/main/java/com/btr/proxy/search/desktop/win/Win32ProxyUtils.java create mode 100644 src/main/java/com/btr/proxy/search/desktop/win/WinProxySearchStrategy.java create mode 100644 src/main/java/com/btr/proxy/search/env/EnvProxySearchStrategy.java create mode 100644 src/main/java/com/btr/proxy/search/java/JavaProxySearchStrategy.java create mode 100644 src/main/java/com/btr/proxy/search/wpad/WpadProxySearchStrategy.java create mode 100644 src/main/java/com/btr/proxy/search/wpad/WpadProxySearchStrategyWithDHPC.java create mode 100644 src/main/java/com/btr/proxy/search/wpad/dhcp/DHCPMessage.java create mode 100644 src/main/java/com/btr/proxy/search/wpad/dhcp/DHCPOptions.java create mode 100644 src/main/java/com/btr/proxy/search/wpad/dhcp/DHCPSocket.java create mode 100644 src/main/java/com/btr/proxy/selector/direct/NoProxySelector.java create mode 100644 src/main/java/com/btr/proxy/selector/fixed/FixedProxySelector.java create mode 100644 src/main/java/com/btr/proxy/selector/fixed/FixedSocksSelector.java create mode 100644 src/main/java/com/btr/proxy/selector/misc/BufferedProxySelector.java create mode 100644 src/main/java/com/btr/proxy/selector/misc/ProtocolDispatchSelector.java create mode 100644 src/main/java/com/btr/proxy/selector/misc/ProxyListFallbackSelector.java create mode 100644 src/main/java/com/btr/proxy/selector/pac/JavaxPacScriptParser.java create mode 100644 src/main/java/com/btr/proxy/selector/pac/PacProxySelector.java create mode 100644 src/main/java/com/btr/proxy/selector/pac/PacScriptMethods.java create mode 100644 src/main/java/com/btr/proxy/selector/pac/PacScriptParser.java create mode 100644 src/main/java/com/btr/proxy/selector/pac/PacScriptSource.java create mode 100644 src/main/java/com/btr/proxy/selector/pac/ProxyEvaluationException.java create mode 100644 src/main/java/com/btr/proxy/selector/pac/RhinoPacScriptParser.java create mode 100644 src/main/java/com/btr/proxy/selector/pac/ScriptAvailability.java create mode 100644 src/main/java/com/btr/proxy/selector/pac/ScriptMethods.java create mode 100644 src/main/java/com/btr/proxy/selector/pac/UrlPacScriptSource.java create mode 100644 src/main/java/com/btr/proxy/selector/whitelist/DefaultWhiteListParser.java create mode 100644 src/main/java/com/btr/proxy/selector/whitelist/HostnameFilter.java create mode 100644 src/main/java/com/btr/proxy/selector/whitelist/IPv4WithSubnetChecker.java create mode 100644 src/main/java/com/btr/proxy/selector/whitelist/IpRangeFilter.java create mode 100644 src/main/java/com/btr/proxy/selector/whitelist/ProxyBypassListSelector.java create mode 100644 src/main/java/com/btr/proxy/selector/whitelist/UseProxyWhiteListSelector.java create mode 100644 src/main/java/com/btr/proxy/selector/whitelist/WhiteListParser.java create mode 100644 src/main/java/com/btr/proxy/test/ProxyTester.java create mode 100644 src/main/java/com/btr/proxy/util/EmptyXMLResolver.java create mode 100644 src/main/java/com/btr/proxy/util/Logger.java create mode 100644 src/main/java/com/btr/proxy/util/PListParser.java create mode 100644 src/main/java/com/btr/proxy/util/PlatformUtil.java create mode 100644 src/main/java/com/btr/proxy/util/ProxyException.java create mode 100644 src/main/java/com/btr/proxy/util/ProxyUtil.java create mode 100644 src/main/java/com/btr/proxy/util/UriFilter.java create mode 100644 src/main/resources/lib/gsettings-amd64.so create mode 100644 src/main/resources/lib/gsettings-x86.so create mode 100644 src/main/resources/lib/proxy_util_amd64.dll create mode 100644 src/main/resources/lib/proxy_util_ia64.dll create mode 100644 src/main/resources/lib/proxy_util_w32.dll create mode 100644 src/test/java/com/btr/proxy/Examples.java create mode 100644 src/test/java/com/btr/proxy/TestUtil.java create mode 100644 src/test/java/com/btr/proxy/search/browser/FirefoxTest.java create mode 100644 src/test/java/com/btr/proxy/search/browser/IeTest.java create mode 100644 src/test/java/com/btr/proxy/search/desktop/DesktopProxySearchTest.java create mode 100644 src/test/java/com/btr/proxy/search/desktop/win/DLLManagerTest.java create mode 100644 src/test/java/com/btr/proxy/search/gnome/GnomeProxySearchTest.java create mode 100644 src/test/java/com/btr/proxy/search/java/JavaProxySearchTest.java create mode 100644 src/test/java/com/btr/proxy/search/kde/KdeProxySearchTest.java create mode 100644 src/test/java/com/btr/proxy/selector/fixed/FixedProxyTest.java create mode 100644 src/test/java/com/btr/proxy/selector/java/JavaProxySelectorTest.java create mode 100644 src/test/java/com/btr/proxy/selector/misc/ProtocolDispatchTest.java create mode 100644 src/test/java/com/btr/proxy/selector/misc/ProxyListFallbackSelectorTest.java create mode 100644 src/test/java/com/btr/proxy/selector/pac/JavaxPacScriptParserTest.java create mode 100644 src/test/java/com/btr/proxy/selector/pac/PacPerProtocolTest.java create mode 100644 src/test/java/com/btr/proxy/selector/pac/PacProxyDebugging.java create mode 100644 src/test/java/com/btr/proxy/selector/pac/PacProxySelectorTest.java create mode 100644 src/test/java/com/btr/proxy/selector/pac/PacScriptMethodsTest.java create mode 100644 src/test/java/com/btr/proxy/selector/pac/RhinoPacScriptParserTest.java create mode 100644 src/test/java/com/btr/proxy/selector/pac/UrlPacScriptSourceTest.java create mode 100644 src/test/java/com/btr/proxy/selector/whitelist/NoProxyTest.java create mode 100644 src/test/java/com/btr/proxy/util/PListParserTest.java create mode 100644 src/test/java/com/btr/proxy/util/ProxyUtilTest.java create mode 100644 src/test/java/com/btr/proxy/util/UriFilterTest.java create mode 100644 src/test/resources/data/ff3_manual/.mozilla/firefox/9f1uyzzu.default/prefs.js create mode 100644 src/test/resources/data/ff3_none/.mozilla/firefox/9f1uyzzu.default/prefs.js create mode 100644 src/test/resources/data/ff3_pac_script/.mozilla/firefox/9f1uyzzu.default/prefs.js create mode 100644 src/test/resources/data/ff3_white_list/.mozilla/firefox/9f1uyzzu.default/prefs.js create mode 100755 src/test/resources/data/gnome_manual/.gconf/system/http_proxy/%gconf.xml create mode 100755 src/test/resources/data/gnome_manual/.gconf/system/proxy/%gconf.xml create mode 100755 src/test/resources/data/gnome_none/.gconf/system/http_proxy/%gconf.xml create mode 100755 src/test/resources/data/gnome_pac_script/.gconf/system/http_proxy/%gconf.xml create mode 100755 src/test/resources/data/gnome_pac_script/.gconf/system/proxy/%gconf.xml create mode 100755 src/test/resources/data/gnome_white_list/.gconf/system/http_proxy/%gconf.xml create mode 100755 src/test/resources/data/gnome_white_list/.gconf/system/proxy/%gconf.xml create mode 100644 src/test/resources/data/kde_env/.kde/share/config/kioslaverc create mode 100644 src/test/resources/data/kde_manual/.kde/share/config/kioslaverc create mode 100644 src/test/resources/data/kde_none/.kde/share/config/kioslaverc create mode 100644 src/test/resources/data/kde_pac_script/.kde/share/config/kioslaverc create mode 100644 src/test/resources/data/kde_white_list/.kde/share/config/kioslaverc create mode 100644 src/test/resources/data/osx/osx_all.plist create mode 100644 src/test/resources/data/osx/osx_manual.plist create mode 100644 src/test/resources/data/osx/osx_pac.plist create mode 100644 src/test/resources/data/pac/test1.pac create mode 100644 src/test/resources/data/pac/test2.pac create mode 100644 src/test/resources/data/pac/testDateRange.pac create mode 100644 src/test/resources/data/pac/testLocalIP.pac create mode 100644 src/test/resources/data/pac/testMultiProxy.pac create mode 100644 src/test/resources/data/pac/testTimeRange.pac create mode 100644 src/test/resources/data/pac/testWeekDay.pac create mode 100644 src/test/resources/data/win/proxy_util_amd64.dll create mode 100644 src/test/resources/data/win/proxy_util_ia64.dll create mode 100644 src/test/resources/data/win/proxy_util_w32.dll create mode 100644 src/test/resources/data/wpad/wpad.pac create mode 100644 src_native/gnome/ProxySchemasGSettingsAccess.c create mode 100644 src_native/gnome/ProxySchemasGSettingsAccess.h create mode 100644 src_native/gnome/make create mode 100755 src_native/win/proxy_util_w23/compile.bat create mode 100644 src_native/win/proxy_util_w23/dllmain.cpp create mode 100644 src_native/win/proxy_util_w23/proxy_util_w23.vcproj create mode 100644 src_native/win/proxy_util_w23/proxy_util_w32.cpp create mode 100644 src_native/win/proxy_util_w23/proxy_util_w32.h create mode 100644 src_native/win/proxy_util_w23/stdafx.cpp create mode 100644 src_native/win/proxy_util_w23/stdafx.h create mode 100644 src_native/win/proxy_util_w23/targetver.h create mode 100644 src_native/win/proxy_util_w32.sln create mode 100644 src_native/win/proxy_util_w32.suo diff --git a/doc/PacIPv6.txt b/doc/PacIPv6.txt new file mode 100644 index 0000000..3b3587e --- /dev/null +++ b/doc/PacIPv6.txt @@ -0,0 +1,6 @@ + +PAC IPV6 in Windows Vista and newer +http://blogs.msdn.com/b/wndp/archive/2006/07/18/ipv6-wpad-for-winhttp-and-wininet.aspx + +And the extension specs at: +http://blogs.msdn.com/b/wndp/archive/2006/07/13/ipv6-pac-extensions-v0-9.aspx \ No newline at end of file diff --git a/doc/changelog.txt b/doc/changelog.txt new file mode 100644 index 0000000..4bcff46 --- /dev/null +++ b/doc/changelog.txt @@ -0,0 +1,56 @@ +Change Log: +This file contains the change log. The changes are marked with the following symbology. + ++ for a new feature +- for a removed feature +* for a modified feature or a bugfix + +----------------- +Current version +* Fixed issue 29 : KDE settings file was not found on KDE4. This is solved now. +* Fixed issue 25 : Disabled DTD and schema validation in settings parsers. + +20120920 +* Improved the fix for issue 26 PAC download recursion bug + +20120905 +* Fixed issue 26 : Implemented (hopefully) better fix for PAC download recursion bug +* Fixed issue 27 + 22 : JavaProxySearch was used always even if no http.proxyHost property was set. + +20120727 +* Fixed issue 19 : Added check to return null if no KDE settings are available. +* Fixed issue 20 : Fix in isInNet method. Resolve host names to IP addresses. +* Fixed issue 21 : Improved handling of empty/invalid URIs + +20111102 +* Fixed issue 14 : Added code to cleanup temporary dll files that were not deleted on VM exit. +* Fixed issue 18 : Added code to support the https.proxy system properties too. +* Fixed issue 9 : IE PAC file urls are now patched to be in line with Firefox URLs. ++ Added support for automatic retry if PAC returns a list of fallback proxies. ++ Started to implement IPv6 support + +20110515 ++ Added (experiemental) support for Max OSX. This is not very well tested yet but should work. ++ Fixed issue 15 : The Firefox proxy search lacked support for socks proxy. This is now available. +* Fixed issue 12 : The PAC JavaScript method isInNet did not work for all patterns. This is fixed now. + +20110210 +* Fixed issue 11: Parsing was unreliable when a PAC script returned more than one proxy +* Some code cleanup to fix some minor problems (platform detection issues and others) ++ Added a debug system property "com.btr.proxy.pac.overrideLocalIP" for the PAC parser ++ Added Support for the IE "Bypass Proxy Server for local addresses" feature + +20100914 ++ PAC support on Java 1.6 will now use the javax.script framework and the internal javascript engine that is shipped with JRE6. This will allow you to support PAC without bundling the Rhino engine which will dramatically reduce the footprint of the library. Java 1.5 will still be supported but then you need to bundle it with the Rhino engine. ++ Added contributed dlls to support 64 windows version for the IE proxy detection. +* Some fixes for the http client that is used to download PAC scripts from a webserver. We accept now all content-type send by the server and we support now different charsets for the scripts. +* Fixed some unit tests that did not pass and did some other small fixes. + +20100724 +* Fixed Issue 5: PAC Parser was not working correctly in multithreaded environments. + +20100411 +* Fixed Issue 4: Improved parser used to parse proxy urls from environment varibales and other places + +Older +Did not track changes for older releases \ No newline at end of file diff --git a/doc/internet_explorer_info.txt b/doc/internet_explorer_info.txt new file mode 100644 index 0000000..6b49928 --- /dev/null +++ b/doc/internet_explorer_info.txt @@ -0,0 +1,4 @@ + +The following article gives a good description of IEs "Bypass Proxy Server for local addresses" feature and how it works. + +http://www.windowsreference.com/internet-explorer/bypass-proxy-server-for-local-addresses-in-internet-explorer-explained/ \ No newline at end of file diff --git a/doc/mac_osx.txt b/doc/mac_osx.txt new file mode 100644 index 0000000..df9c18e --- /dev/null +++ b/doc/mac_osx.txt @@ -0,0 +1,7 @@ +Settings are stored in a XML file that can be found at + +/Library/Preferences/SystemConfiguration/preferences.plist + +The plist file format is specified under + +http://www.apple.com/DTDs/PropertyList-1.0.dtd diff --git a/doc/misc_links.txt b/doc/misc_links.txt new file mode 100644 index 0000000..b16885f --- /dev/null +++ b/doc/misc_links.txt @@ -0,0 +1,5 @@ + +Oracle iPlanet Web Proxy Server +-------------------------------- +http://download.oracle.com/docs/cd/E21692_01/821-1882/adyrr/index.html + diff --git a/doc/pac_files_docu.html b/doc/pac_files_docu.html new file mode 100644 index 0000000..9e08bf8 --- /dev/null +++ b/doc/pac_files_docu.html @@ -0,0 +1,1182 @@ + +
+ + + + + + +(There are several examples and tips in the end of this document) +
+
+
+
+
+
The proxy autoconfig file is written in JavaScript. The file must define +the function: +
function FindProxyForURL(url, host) + { + ... + }+which will be called by the Navigator in the following way for every URL +that is retrieved by it: +
ret = FindProxyForURL(url, host);+where: +
proxy.pac+Note 1: You should save the JavaScript function by itself, +not embed it in HTML. +
Note 2: The examples in the end of this document are +complete, +there is no additional syntax needed to save it into a file and use it +(of course, the JavaScripts have to be edited to reflect your site's domain +name and/or subnets). +
application/x-ns-proxy-autoconfig+If using a Netscape server, edit the mime.types file in the config +directory. If using Apache, CERN or NCSA servers, use the AddType +directive. +
If the string is null, no proxies should be used. +
The string can contain any number of the following building blocks, +separated by a semicolon: +
+
+
The Navigator will automatically retry a previously unresponsive proxy +after 30 minutes, then after 1 hour from the previous try (always adding +an extra 30 minutes). +
If all proxies are down, and there was no DIRECT option specified, +the Navigator will ask if proxies should be temporarily ignored, and direct +connections attempted. The Navigator will ask if proxies should be retried +after 20 minutes has passed (then the next time 40 minutes from the previous +question, always adding 20 minutes). +
+
+
+
+
+
+
+
+
+
+
Pattern and mask specification is done the same way as for SOCKS configuration. +
+
+
+
+
Actually, currently the patterns are shell expressions, not +regular expressions. +
+
+
SUN MON TUE WED THU FRI SAT+ +
+
+
If only one parameter is present, the function yeilds a true value on +the weekday that the parameter represents. If the string +"GMT" +is specified as a second parameter, times are taken to be in GMT, otherwise +in local timezone. +
If both wd1 and wd1 are defined, the condition is +true if the current weekday is in between those two weekdays. Bounds are +inclusive. If the "GMT" parameter is specified, times are taken +to be in GMT, otherwise the local timezone is used. +
+
+
JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC+ +
+
Even though the above examples don't show, the "GMT" parameter
+can be specified in any of the 9 different call profiles, always as the
+last parameter.
+
+
+
+
+
+
+
+
function FindProxyForURL(url, host) + { + if (isPlainHostName(host) || + dnsDomainIs(host, ".netscape.com")) + return "DIRECT"; + else + return "PROXY w3proxy.netscape.com:8080; DIRECT"; + }+Note: This is the simplest and most efficient autoconfig file for +cases where there's only one proxy. +
+
function FindProxyForURL(url, host) + { + if ((isPlainHostName(host) || + dnsDomainIs(host, ".netscape.com")) && + !localHostOrDomainIs(host, "www.netscape.com") && + !localHostOrDoaminIs(host, "merchant.netscape.com")) + + return "DIRECT"; + else + return "PROXY w3proxy.netscape.com:8080; DIRECT"; + }+The above will use the proxy for everything else except local hosts in +the netscape.com domain, with the further exception that hosts +www.netscape.com +and +merchant.netscape.com will go through the proxy. +
Note the order of the above exceptions for efficiency: localHostOrDomainIs() +functions only get executed for URLs that are in local domain, not for +every URL. Be careful to note the parentheses around the or expression +before the and expression to achieve the abovementioned efficient +behaviour. +
+
function FindProxyForURL(url, host) + { + if (isResolvable(host)) + return "DIRECT"; + else + return "PROXY proxy.mydomain.com:8080"; + }+The above requires consulting the DNS every time; it can be grouped smartly +with other rules so that DNS is consulted only if other rules do not yield +a result: +
function FindProxyForURL(url, host) + { + if (isPlainHostName(host) || + dnsDomainIs(host, ".mydomain.com") || + isResolvable(host)) + return "DIRECT"; + else + return "PROXY proxy.mydomain.com:8080"; + }+ +
function FindProxyForURL(url, host) + { + if (isInNet(host, "198.95.0.0", "255.255.0.0")) + return "DIRECT"; + else + return "PROXY proxy.mydomain.com:8080"; + }+Again, use of DNS in the above can be minimized by adding redundant rules +in the beginning: +
function FindProxyForURL(url, host) + { + if (isPlainHostName(host) || + dnsDomainIs(host, ".mydomain.com") || + isInNet(host, "198.95.0.0", "255.255.0.0")) + return "DIRECT"; + else + return "PROXY proxy.mydomain.com:8080"; + }+ +
Furthermore, the three remaining proxy servers share the load based +on URL patterns, which makes their caching more effective (there is only +one copy of any document on the three servers -- as opposed to one copy +on each of them). The load is distributed like this: +
Proxy | + +Purpose | +
---|---|
#1 | + +.com domain | +
#2 | + +.edu domain | +
#3 | + +all other domains | +
#4 | + +hot stand-by | +
All local accesses are desired to be direct. All proxy servers run on +the port 8080 (they wouldn't need to). Note how strings can be concatenated +by the + operator in JavaScript. +
function FindProxyForURL(url, host) + { + if (isPlainHostName(host) || dnsDomainIs(host, ".mydomain.com")) + return "DIRECT"; + else if (shExpMatch(host, "*.com")) + return "PROXY proxy1.mydomain.com:8080; " + + "PROXY proxy4.mydomain.com:8080"; + else if (shExpMatch(host, "*.edu")) + return "PROXY proxy2.mydomain.com:8080; " + + "PROXY proxy4.mydomain.com:8080"; + else + return "PROXY proxy3.mydomain.com:8080; " + + "PROXY proxy4.mydomain.com:8080"; + }+ +
function FindProxyForURL(url, host) + { + if (url.substring(0, 5) == "http:") { + + return "PROXY http-proxy.mydomain.com:8080"; + } + else if (url.substring(0, 4) == "ftp:") { + + return "PROXY ftp-proxy.mydomain.com:8080"; + } + else if (url.substring(0, 7) == "gopher:") { + + return "PROXY gopher-proxy.mydomain.com:8080"; + } + else if (url.substring(0, 6) == "https:" || + url.substring(0, 6) == "snews:") { + + return "PROXY security-proxy.mydomain.com:8080"; + } + else { + + return "DIRECT"; + } + }+Note: The same can be accomplished using the +shExpMatch() +function described earlier; for example: +
... + if (shExpMatch(url, "http:*")) { + return "PROXY http-proxy.mydomain.com:8080; + } + ...+ +
+
+
+
Help | Site Map | How to Get Netscape Products | Advertise With Us | Add Site | Custom Browser Program |
+ |
+ +Autos | Business | Computing & Internet | Entertainment | Family | Games | Health | Lifestyles | Local | Netscape | Netscape Open Directory | News | Personal Finance | Real Estate | Research & Learn | Shopping | Small Business | Sports | Travel |
+
+© 1999 Netscape, All Rights Reserved. Legal & Privacy Notices This site powered by Netscape SuiteSpot servers. |
+
+
+
+
+
addStrategy
to add one or more search strategies.getProxySelector
method.
+ * Invoke the static getDefaultProxySearch
method to use a default search strategy.
+ *
+ * .mozilla/firefox/(profile)/ folder. + *
+ * + * See Mozilla_Networking_Preferences + * for an explanation of the proxy settings. + *+ * The following settings are extracted from + * this file: + *
+ * Some generic settings:+ * Note that if there are more than one profile the first profile found will be used. + *
+ * @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 .mozilla 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: + *+ * Mozilla\Firefox\Profiles\ + *
+ * 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 ("+ * The following settings are extracted from the configuration that is stored + * in .gconf folder found in the user's home directory: + *
+ *+ * GNOME Proxy_configuration settings are explained + * here in detail + *
+ * @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/+ * .kde/share/config/kioslaverc + *
+ * starting from the current users home directory. + *+ * The following settings are extracted from the section "[Proxy Settings]": + *
+ *+ * .kde/share/config/kioslaverc + *
+ * 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. + *+ * 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. + *
+ * To parse this file we use a parser that is derived from a plist parser that + * comes with the xmlwise XML parser package: + *
+ * http://code.google.com/p/xmlwise/ + *
+ * I modified that parser to work with the default Java XML parsing library. + *
+ * The plist file is located on OSX at: + *
+ * /Library/Preferences/SystemConfiguration/preferences.plist + *
+ * + * @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+ * 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: + *
+ * 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. "proxy_vole_lib_dir"/proxy_util_w32.dll + *
+ * Second we try to load the dll from the subfolder lib if that one exists.
+ * 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.
+ *
+ * Please note that the file is named Win32ProxyUtils but has now also support + * for x64 architecture. + *
+ * @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 + *+ * e.g. DIRECT myproxy.mycompany.com:8080 | *.mycompany.com, localhost + *
+ * @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: + *
+ * This is based on information found here:
+ * http://download.oracle.com/javase/6/docs/technotes/guides/net/proxies.html
+ *
+ * Note: at the moment only the DNS name guessing schema is implemented. + * All others are missing. + *
+ * For more information about WPAD: + * Web_Proxy_Autodiscovery_Protocol + *
+ * Outdated RFC draft: + * draft-ietf-wrec-wpad-01.txt + *
+ * @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+ * Note: at the moment only the DNS name guessing schema is implemented. All + * others are missing. + *
+ *+ * For more information about WPAD: Web_Proxy_Autodiscovery_Protocol + *
+ *+ * Outdated RFC draft: draft-ietf-wrec-wpad-01.txt + *
+ * + * @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.true
if message is received,
+ * false
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;
+ }
+}
diff --git a/src/main/java/com/btr/proxy/selector/direct/NoProxySelector.java b/src/main/java/com/btr/proxy/selector/direct/NoProxySelector.java
new file mode 100644
index 0000000..cf9e150
--- /dev/null
+++ b/src/main/java/com/btr/proxy/selector/direct/NoProxySelector.java
@@ -0,0 +1,70 @@
+package com.btr.proxy.selector.direct;
+
+import java.io.IOException;
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.SocketAddress;
+import java.net.URI;
+import java.util.List;
+
+import com.btr.proxy.util.ProxyUtil;
+
+/*****************************************************************************
+ * This proxy selector will always return a "DIRECT" proxy.
+ * Implemented as singleton.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class NoProxySelector extends ProxySelector {
+
+ private static NoProxySelector instance;
+
+ /*************************************************************************
+ * Constructor
+ ************************************************************************/
+
+ private NoProxySelector() {
+ super();
+ }
+
+ /*************************************************************************
+ * Gets the one and only instance of this selector.
+ * @return a DirectSelector.
+ ************************************************************************/
+
+ public static synchronized NoProxySelector getInstance() {
+ if (NoProxySelector.instance == null) {
+ NoProxySelector.instance = new NoProxySelector();
+ }
+ return instance;
+ }
+
+ /*************************************************************************
+ * connectFailed
+ * @see java.net.ProxySelector#connectFailed(java.net.URI, java.net.SocketAddress, java.io.IOException)
+ ************************************************************************/
+
+ @Override
+ public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
+ // Not used.
+ }
+
+ /*************************************************************************
+ * select
+ * @see java.net.ProxySelector#select(java.net.URI)
+ ************************************************************************/
+
+ @Override
+ public ListURI
to be evaluated.
+ * @return Proxy
-object list as result of the evaluation.
+ ************************************************************************/
+
+ private ListProxy
for it.
+ * @param pacResult the result from the PAC parser.
+ * @return a Proxy
+ ************************************************************************/
+
+ private Proxy buildProxyFromPacResult(String pacResult) {
+ if (pacResult == null || pacResult.trim().length() < 6) {
+ return Proxy.NO_PROXY;
+ }
+ String proxyDef = pacResult.trim();
+ if (proxyDef.toUpperCase().startsWith(PAC_DIRECT)) {
+ return Proxy.NO_PROXY;
+ }
+
+ // Check proxy type.
+ Proxy.Type type = Proxy.Type.HTTP;
+ if (proxyDef.toUpperCase().startsWith(PAC_SOCKS)) {
+ type = Proxy.Type.SOCKS;
+ }
+
+ String host = proxyDef.substring(6);
+ Integer port = ProxyUtil.DEFAULT_PROXY_PORT;
+
+ // Split port from host
+ int indexOfPort = host.indexOf(':');
+ if (indexOfPort != -1) {
+ port = Integer.parseInt(host.substring(indexOfPort+1).trim());
+ host = host.substring(0, indexOfPort).trim();
+ }
+
+ SocketAddress adr = InetSocketAddress.createUnresolved(host, port);
+ return new Proxy(type, adr);
+ }
+
+}
diff --git a/src/main/java/com/btr/proxy/selector/pac/PacScriptMethods.java b/src/main/java/com/btr/proxy/selector/pac/PacScriptMethods.java
new file mode 100644
index 0000000..101c267
--- /dev/null
+++ b/src/main/java/com/btr/proxy/selector/pac/PacScriptMethods.java
@@ -0,0 +1,656 @@
+package com.btr.proxy.selector.pac;
+
+import java.io.IOException;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+import java.util.TimeZone;
+
+import com.btr.proxy.util.Logger;
+import com.btr.proxy.util.Logger.LogLevel;
+
+/***************************************************************************
+ * Implementation of PAC JavaScript functions.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ***************************************************************************
+ */
+public class PacScriptMethods implements ScriptMethods {
+
+ public static final String OVERRIDE_LOCAL_IP = "com.btr.proxy.pac.overrideLocalIP";
+
+ private final static String GMT = "GMT";
+
+ private final static List+ * timeRange(hour) + * timeRange(hour1, hour2) + * timeRange(hour1, min1, hour2, min2) + * timeRange(hour1, min1, sec1, hour2, min2, sec2) + * timeRange(hour1, min1, sec1, hour2, min2, sec2, gmt) + *+ * + * @param hour1 + * is the hour from 0 to 23. (0 is midnight, 23 is 11 pm.) + * @param min1 + * minutes from 0 to 59. + * @param sec1 + * seconds from 0 to 59. + * @param hour2 + * is the hour from 0 to 23. (0 is midnight, 23 is 11 pm.) + * @param min2 + * minutes from 0 to 59. + * @param sec2 + * seconds from 0 to 59. + * @param gmt + * "GMT" for gmt time format else "undefined" + * @return true if the current time matches the given range. + ************************************************************************/ + + public boolean timeRange(Object hour1, Object min1, Object sec1, + Object hour2, Object min2, Object sec2, Object gmt) { + boolean useGmt = GMT.equalsIgnoreCase(String.valueOf(min1)) + || GMT.equalsIgnoreCase(String.valueOf(sec1)) + || GMT.equalsIgnoreCase(String.valueOf(min2)) + || GMT.equalsIgnoreCase(String.valueOf(gmt)); + + Calendar cal = getCurrentTime(useGmt); + cal.set(Calendar.MILLISECOND, 0); + Date current = cal.getTime(); + Date from; + Date to; + if (sec2 instanceof Number) { + cal.set(Calendar.HOUR_OF_DAY, ((Number) hour1).intValue()); + cal.set(Calendar.MINUTE, ((Number) min1).intValue()); + cal.set(Calendar.SECOND, ((Number) sec1).intValue()); + from = cal.getTime(); + + cal.set(Calendar.HOUR_OF_DAY, ((Number) hour2).intValue()); + cal.set(Calendar.MINUTE, ((Number) min2).intValue()); + cal.set(Calendar.SECOND, ((Number) sec2).intValue()); + to = cal.getTime(); + } else if (hour2 instanceof Number) { + cal.set(Calendar.HOUR_OF_DAY, ((Number) hour1).intValue()); + cal.set(Calendar.MINUTE, ((Number) min1).intValue()); + cal.set(Calendar.SECOND, 0); + from = cal.getTime(); + + cal.set(Calendar.HOUR_OF_DAY, ((Number) sec1).intValue()); + cal.set(Calendar.MINUTE, ((Number) hour2).intValue()); + cal.set(Calendar.SECOND, 59); + to = cal.getTime(); + } else if (min1 instanceof Number) { + cal.set(Calendar.HOUR_OF_DAY, ((Number) hour1).intValue()); + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.SECOND, 0); + from = cal.getTime(); + + cal.set(Calendar.HOUR_OF_DAY, ((Number) min1).intValue()); + cal.set(Calendar.MINUTE, 59); + cal.set(Calendar.SECOND, 59); + to = cal.getTime(); + } else { + cal.set(Calendar.HOUR_OF_DAY, ((Number) hour1).intValue()); + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.SECOND, 0); + from = cal.getTime(); + + cal.set(Calendar.HOUR_OF_DAY, ((Number) hour1).intValue()); + cal.set(Calendar.MINUTE, 59); + cal.set(Calendar.SECOND, 59); + to = cal.getTime(); + } + + if (to.before(from)) { + cal.setTime(to); + cal.add(Calendar.DATE, +1); + to = cal.getTime(); + } + + return current.compareTo(from) >= 0 && current.compareTo(to) <= 0; + } + + // Microsoft PAC extensions for IPv6 support. + + /************************************************************************* + * isResolvableEx + * @see com.btr.proxy.selector.pac.ScriptMethods#isResolvableEx(java.lang.String) + ************************************************************************/ + + public boolean isResolvableEx(String host) { + return isResolvable(host); + } + + /************************************************************************* + * isInNetEx + * @see com.btr.proxy.selector.pac.ScriptMethods#isInNetEx(java.lang.String, java.lang.String) + ************************************************************************/ + + public boolean isInNetEx(String ipAddress, String ipPrefix) { + // TODO rossi 27.06.2011 Auto-generated method stub + return false; + } + + /************************************************************************* + * dnsResolveEx + * @see com.btr.proxy.selector.pac.ScriptMethods#dnsResolveEx(java.lang.String) + ************************************************************************/ + + public String dnsResolveEx(String host) { + StringBuilder result = new StringBuilder(); + try { + InetAddress[] list = InetAddress.getAllByName(host); + for (InetAddress inetAddress : list) { + result.append(inetAddress.getHostAddress()); + result.append("; "); + } + } catch (UnknownHostException e) { + Logger.log(JavaxPacScriptParser.class, LogLevel.DEBUG, + "DNS name not resolvable {0}.", host); + } + return result.toString(); + } + + /************************************************************************* + * myIpAddressEx + * @see com.btr.proxy.selector.pac.ScriptMethods#myIpAddressEx() + ************************************************************************/ + + public String myIpAddressEx() { + return getLocalAddressOfType(Inet6Address.class); + } + + /************************************************************************* + * sortIpAddressList + * @see com.btr.proxy.selector.pac.ScriptMethods#sortIpAddressList(java.lang.String) + ************************************************************************/ + + public String sortIpAddressList(String ipAddressList) { + if (ipAddressList == null || ipAddressList.trim().length() == 0) { + return ""; + } + String[] ipAddressToken = ipAddressList.split(";"); + List
+ * More information about PAC can be found there:
+ * Proxy_auto-config
+ * web-browser-auto-proxy-configuration
+ *
+ * timeRange(hour) + * timeRange(hour1, hour2) + * timeRange(hour1, min1, hour2, min2) + * timeRange(hour1, min1, sec1, hour2, min2, sec2) + * timeRange(hour1, min1, sec1, hour2, min2, sec2, gmt) + *+ * + * @param hour1 is the hour from 0 to 23. (0 is midnight, 23 is 11 pm.) + * @param min1 minutes from 0 to 59. + * @param sec1 seconds from 0 to 59. + * @param hour2 is the hour from 0 to 23. (0 is midnight, 23 is 11 pm.) + * @param min2 minutes from 0 to 59. + * @param sec2 seconds from 0 to 59. + * @param gmt "GMT" for gmt time format else "undefined" + * @return true if the current time matches the given range. + ************************************************************************/ + + public static boolean timeRange(Object hour1, Object min1, Object sec1, Object hour2, Object min2, Object sec2, Object gmt) { + return SCRIPT_METHODS.timeRange(hour1, min1, sec1, hour2, min2, sec2, gmt); + } + +} diff --git a/src/main/java/com/btr/proxy/selector/pac/ScriptAvailability.java b/src/main/java/com/btr/proxy/selector/pac/ScriptAvailability.java new file mode 100644 index 0000000..d7f6e04 --- /dev/null +++ b/src/main/java/com/btr/proxy/selector/pac/ScriptAvailability.java @@ -0,0 +1,46 @@ +package com.btr.proxy.selector.pac; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/**************************************************************************** + * Utility to check availablility of javax.script + * + * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009 + ***************************************************************************/ +abstract class ScriptAvailability { + + /************************************************************************* + * Checks whether javax.script is available or not. + * Completely done per Reflection to allow compilation under Java 1.5 + * @return true if javax.script is available; false otherwise + ************************************************************************/ + public static boolean isJavaxScriptingAvailable() { + Object engine = null; + try { + Class> managerClass = Class.forName("javax.script.ScriptEngineManager"); + Method m = managerClass.getMethod("getEngineByMimeType", String.class); + engine = m.invoke(managerClass.newInstance(), "text/javascript"); + } catch (ClassNotFoundException e) { + // javax.script not available + } catch (NoSuchMethodException e) { + // javax.script not available + } catch (IllegalAccessException e) { + // javax.script not available + } catch (InvocationTargetException e) { + // javax.script not available + } catch (InstantiationException e) { + // javax.script not available + } + + return engine != null; + } + + /************************************************************************* + * Constructor + ************************************************************************/ + + ScriptAvailability() { + super(); + } +} diff --git a/src/main/java/com/btr/proxy/selector/pac/ScriptMethods.java b/src/main/java/com/btr/proxy/selector/pac/ScriptMethods.java new file mode 100644 index 0000000..9f559c5 --- /dev/null +++ b/src/main/java/com/btr/proxy/selector/pac/ScriptMethods.java @@ -0,0 +1,256 @@ +package com.btr.proxy.selector.pac; + +/*************************************************************************** + * Defines the public interface for PAC scripts. + * + * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009 + ***************************************************************************/ +public interface ScriptMethods { + + public boolean isPlainHostName(String host); + + /************************************************************************* + * Tests if an URL is in a given domain. + * + * @param host + * is the host name from the URL. + * @param domain + * is the domain name to test the host name against. + * @return true if the domain of host name matches. + ************************************************************************/ + + public boolean dnsDomainIs(String host, String domain); + + /************************************************************************* + * Is true if the host name matches exactly the specified host name, or if + * there is no domain name part in the host name, but the unqualified host + * name matches. + * + * @param host + * the host name from the URL. + * @param domain + * fully qualified host name with domain to match against. + * @return true if matches else false. + ************************************************************************/ + + public boolean localHostOrDomainIs(String host, String domain); + + /************************************************************************* + * Tries to resolve the host name. Returns true if succeeds. + * + * @param host + * is the host name from the URL. + * @return true if resolvable else false. + ************************************************************************/ + + public boolean isResolvable(String host); + + /************************************************************************* + * Tries to resolve the host name. Returns true if succeeds to resolve + * the host to an IPv4 or IPv6 address. + * + * @param host + * is the host name from the URL. + * @return true if resolvable else false. + ************************************************************************/ + + public boolean isResolvableEx(String host); + + /************************************************************************* + * Returns true if the IP address of the host matches the specified IP + * address pattern. Pattern and mask specification is done the same way as + * for SOCKS configuration. + * + * Example: isInNet(host, "198.95.0.0", "255.255.0.0") is true if the IP + * address of the host matches 198.95.*.*. + * + * @param host + * a DNS host name, or IP address. If a host name is passed, it + * will be resolved into an IP address by this function. + * @param pattern + * an IP address pattern in the dot-separated format. + * @param mask + * mask for the IP address pattern informing which parts of the + * IP address should be matched against. 0 means ignore, 255 + * means match. + * @return true if it matches else false. + ************************************************************************/ + + public boolean isInNet(String host, String pattern, String mask); + + /************************************************************************* + * Extension of the isInNet method to support IPv6. + * @param ipAddress an IP4 or IP6 address + * @param ipPrefix A string containing colon delimited IP prefix + * with top n bits specified in the bit field + * (i.e. 3ffe:8311:ffff::/48 or 123.112.0.0/16). + * @return true if the host is in the given subnet, else false. + ************************************************************************/ + + public boolean isInNetEx(String ipAddress, String ipPrefix); + + /************************************************************************* + * Resolves the given DNS host name into an IP address, and returns it in + * the dot separated format as a string. + * + * @param host + * the host to resolve. + * @return the resolved IP, empty string if not resolvable. + ************************************************************************/ + + public String dnsResolve(String host); + + /************************************************************************* + * @param host the host to resolve + * @return a semicolon separated list of IP6 and IP4 addresses the host + * name resolves to, empty string if not resolvable. + ************************************************************************/ + + public String dnsResolveEx(String host); + + /************************************************************************* + * Returns the IP address of the host that the process is running on, as a + * string in the dot-separated integer format. + * + * @return an IP as string. + ************************************************************************/ + + public String myIpAddress(); + + /************************************************************************* + * Returns a list of IP4 and IP6 addresses of the host that the process + * is running on. The list is separated with semicolons. + * @return the list, empty string if not available. + ************************************************************************/ + + public String myIpAddressEx(); + + /************************************************************************* + * Returns the number of DNS domain levels (number of dots) in the host + * name. + * + * @param host + * is the host name from the URL. + * @return number of DNS domain levels. + ************************************************************************/ + + public int dnsDomainLevels(String host); + + /************************************************************************* + * Returns true if the string matches the specified shell expression. + * Actually, currently the patterns are shell expressions, not regular + * expressions. + * + * @param str + * is any string to compare (e.g. the URL, or the host name). + * @param shexp + * is a shell expression to compare against. + * @return true if the string matches, else false. + ************************************************************************/ + + public boolean shExpMatch(String str, String shexp); + + /************************************************************************* + * Only the first parameter is mandatory. Either the second, the third, or + * both may be left out. If only one parameter is present, the function + * yields a true value on the weekday that the parameter represents. If the + * string "GMT" is specified as a second parameter, times are taken to be in + * GMT, otherwise in local time zone. If both wd1 and wd2 are defined, the + * condition is true if the current weekday is in between those two + * weekdays. Bounds are inclusive. If the "GMT" parameter is specified, + * times are taken to be in GMT, otherwise the local time zone is used. + * + * @param wd1 + * weekday 1 is one of SUN MON TUE WED THU FRI SAT + * @param wd2 + * weekday 2 is one of SUN MON TUE WED THU FRI SAT + * @param gmt + * "GMT" for gmt time format else "undefined" + * @return true if current day matches the criteria. + ************************************************************************/ + + public boolean weekdayRange(String wd1, String wd2, String gmt); + + /************************************************************************* + * Only the first parameter is mandatory. All other parameters can be left + * out therefore the meaning of the parameters changes. The method + * definition shows the version with the most possible parameters filled. + * The real meaning of the parameters is guessed from it's value. If "from" + * and "to" are specified then the bounds are inclusive. If the "GMT" + * parameter is specified, times are taken to be in GMT, otherwise the local + * time zone is used. + * + * @param day1 + * is the day of month between 1 and 31 (as an integer). + * @param month1 + * one of JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC + * @param year1 + * is the full year number, for example 1995 (but not 95). + * Integer. + * @param day2 + * is the day of month between 1 and 31 (as an integer). + * @param month2 + * one of JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC + * @param year2 + * is the full year number, for example 1995 (but not 95). + * Integer. + * @param gmt + * "GMT" for gmt time format else "undefined" + * @return true if the current date matches the given range. + ************************************************************************/ + + public boolean dateRange(Object day1, Object month1, Object year1, Object day2, Object month2, Object year2, + Object gmt); + + /************************************************************************* + * Some parameters can be left out therefore the meaning of the parameters + * changes. The method definition shows the version with the most possible + * parameters filled. The real meaning of the parameters is guessed from + * it's value. If "from" and "to" are specified then the bounds are + * inclusive. If the "GMT" parameter is specified, times are taken to be in + * GMT, otherwise the local time zone is used.
+ * timeRange(hour) + * timeRange(hour1, hour2) + * timeRange(hour1, min1, hour2, min2) + * timeRange(hour1, min1, sec1, hour2, min2, sec2) + * timeRange(hour1, min1, sec1, hour2, min2, sec2, gmt) + *+ * + * @param hour1 + * is the hour from 0 to 23. (0 is midnight, 23 is 11 pm.) + * @param min1 + * minutes from 0 to 59. + * @param sec1 + * seconds from 0 to 59. + * @param hour2 + * is the hour from 0 to 23. (0 is midnight, 23 is 11 pm.) + * @param min2 + * minutes from 0 to 59. + * @param sec2 + * seconds from 0 to 59. + * @param gmt + * "GMT" for gmt time format else "undefined" + * @return true if the current time matches the given range. + ************************************************************************/ + + public boolean timeRange(Object hour1, Object min1, Object sec1, Object hour2, Object min2, Object sec2, Object gmt); + + /************************************************************************* + * Sorts a list of IP4 and IP6 addresses. Separated by semicolon. + * Dual addresses first, then IPv6 and last IPv4. + * @param ipAddressList the address list. + * @return the sorted list, empty string if sort is not possible + ************************************************************************/ + + public String sortIpAddressList(String ipAddressList); + + /************************************************************************* + * Gets the version of the PAC extension that is available. + * @return the extension version, currently 1.0 + ************************************************************************/ + + public String getClientVersion(); + +} diff --git a/src/main/java/com/btr/proxy/selector/pac/UrlPacScriptSource.java b/src/main/java/com/btr/proxy/selector/pac/UrlPacScriptSource.java new file mode 100644 index 0000000..6ac8faa --- /dev/null +++ b/src/main/java/com/btr/proxy/selector/pac/UrlPacScriptSource.java @@ -0,0 +1,270 @@ +package com.btr.proxy.selector.pac; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.Proxy; +import java.net.URISyntaxException; +import java.net.URL; + +import com.btr.proxy.util.Logger; +import com.btr.proxy.util.Logger.LogLevel; + +/***************************************************************************** + * Script source that will load the content of a PAC file from an webserver. + * The script content is cached once it was downloaded. + * + * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009 + ****************************************************************************/ + +public class UrlPacScriptSource implements PacScriptSource { + + private static final int DEFAULT_CONNECT_TIMEOUT = 15 * 1000; // seconds + private static final int DEFAULT_READ_TIMEOUT = 20 * 1000; // seconds + public static final String OVERRIDE_CONNECT_TIMEOUT = "com.btr.proxy.url.connectTimeout"; + public static final String OVERRIDE_READ_TIMEOUT = "com.btr.proxy.url.readTimeout"; + + private final String scriptUrl; + private String scriptContent; + private long expireAtMillis; + + /************************************************************************* + * Constructor + * @param url the URL to download the script from. + ************************************************************************/ + + public UrlPacScriptSource(String url) { + super(); + this.expireAtMillis = 0; + this.scriptUrl = url; + } + + /************************************************************************* + * getScriptContent + * @see com.btr.proxy.selector.pac.PacScriptSource#getScriptContent() + ************************************************************************/ + + public synchronized String getScriptContent() throws IOException { + if (this.scriptContent == null || + (this.expireAtMillis > 0 + && this.expireAtMillis > System.currentTimeMillis())) { + try { + if (this.scriptUrl.startsWith("file:/") || this.scriptUrl.indexOf(":/") == -1) { + this.scriptContent = readPacFileContent(this.scriptUrl); + } else { + this.scriptContent = downloadPacContent(this.scriptUrl); + } + } catch (IOException e) { + Logger.log(getClass(), LogLevel.ERROR, "Loading script failed from: {0} with error {1}", this.scriptUrl, e); + this.scriptContent = ""; + throw e; + } + } + return this.scriptContent; + } + + /************************************************************************* + * Reads a PAC script from a local file. + * @param scriptUrl + * @return the content of the script file. + * @throws IOException + * @throws URISyntaxException + ************************************************************************/ + + private String readPacFileContent(String scriptUrl) throws IOException { + try { + File file = null; + if (scriptUrl.indexOf(":/") == -1) { + file = new File(scriptUrl); + } else { + file = new File(new URL(scriptUrl).toURI()); + } + BufferedReader r = new BufferedReader(new FileReader(file)); + StringBuilder result = new StringBuilder(); + try { + String line; + while ((line = r.readLine()) != null) { + result.append(line).append("\n"); + } + } finally { + r.close(); + } + return result.toString(); + } catch (Exception e) { + Logger.log(getClass(), LogLevel.ERROR, "File reading error.", e); + throw new IOException(e.getMessage()); + } + } + + /************************************************************************* + * Downloads the script from a webserver. + * @param url the URL to the script file. + * @return the script content. + * @throws IOException on read error. + ************************************************************************/ + + private String downloadPacContent(String url) throws IOException { + if (url == null) { + throw new IOException("Invalid PAC script URL: null"); + } + + setPacProxySelectorEnabled(false); + + HttpURLConnection con = null; + try { + con = setupHTTPConnection(url); + if (con.getResponseCode() != 200) { + throw new IOException("Server returned: "+con.getResponseCode()+" "+con.getResponseMessage()); + } + // Read expire date. + this.expireAtMillis = con.getExpiration(); + + BufferedReader r = getReader(con); + String result = readAllContent(r); + r.close(); + return result; + } finally { + setPacProxySelectorEnabled(true); + if (con != null) { + con.disconnect(); + } + } + } + + /************************************************************************* + * Enables/disables the PAC proxy selector while we download to prevent recursion. + * See issue: 26 in the change tracker. + ************************************************************************/ + + private void setPacProxySelectorEnabled(boolean enable) { + PacProxySelector.setEnabled(enable); + } + + /************************************************************************* + * Reads the whole content available into a String. + * @param r to read from. + * @return the complete PAC file content. + * @throws IOException + ************************************************************************/ + + private String readAllContent(BufferedReader r) throws IOException { + StringBuilder result = new StringBuilder(); + String line; + while ((line = r.readLine()) != null) { + result.append(line).append("\n"); + } + return result.toString(); + } + + /************************************************************************* + * Build a BufferedReader around the open HTTP connection. + * @param con to read from + * @return the BufferedReader. + * @throws UnsupportedEncodingException + * @throws IOException + ************************************************************************/ + + private BufferedReader getReader(HttpURLConnection con) + throws UnsupportedEncodingException, IOException { + String charsetName = parseCharsetFromHeader(con.getContentType()); + BufferedReader r = new BufferedReader(new InputStreamReader(con.getInputStream(), charsetName)); + return r; + } + + /************************************************************************* + * Configure the connection to download from. + * @param url to get the pac file content from + * @return a HTTPUrlConnecion to this url. + * @throws IOException + * @throws MalformedURLException + ************************************************************************/ + + private HttpURLConnection setupHTTPConnection(String url) + throws IOException, MalformedURLException { + HttpURLConnection con = (HttpURLConnection) new URL(url).openConnection(Proxy.NO_PROXY); + con.setConnectTimeout(getTimeOut(OVERRIDE_CONNECT_TIMEOUT, DEFAULT_CONNECT_TIMEOUT)); + con.setReadTimeout(getTimeOut(OVERRIDE_READ_TIMEOUT, DEFAULT_READ_TIMEOUT)); + con.setInstanceFollowRedirects(true); + con.setRequestProperty("accept", "application/x-ns-proxy-autoconfig, */*;q=0.8"); + return con; + } + + /************************************************************************* + * Gets the timeout value from a property or uses the given default value if + * the property cannot be parsed. + * @param overrideProperty the property to define the timeout value in milliseconds + * @param defaultValue the default timeout value in milliseconds. + * @return the value to use. + ************************************************************************/ + + protected int getTimeOut(String overrideProperty, int defaultValue) { + int timeout = defaultValue; + String prop = System.getProperty(overrideProperty); + if (prop != null && prop.trim().length() > 0) { + try { + timeout = Integer.parseInt(prop.trim()); + } catch (NumberFormatException e) { + Logger.log(getClass(), LogLevel.DEBUG, "Invalid override property : {0}={1}", overrideProperty, prop); + // In this case use the default value. + } + } + return timeout; + } + + /************************************************************************* + * Response Content-Type could be something like this: + * application/x-ns-proxy-autoconfig; charset=UTF-8 + * @param contentType header field. + * @return the extracted charset if set else a default charset. + ************************************************************************/ + + String parseCharsetFromHeader(String contentType) { + String result = "ISO-8859-1"; + if (contentType != null) { + String[] paramList = contentType.split(";"); + for (String param : paramList) { + if (param.toLowerCase().trim().startsWith("charset") && param.indexOf("=") != -1) { + result = param.substring(param.indexOf("=")+1).trim(); + } + } + } + return result; + } + + /*************************************************************************** + * @see java.lang.Object#toString() + **************************************************************************/ + @Override + public String toString() { + return this.scriptUrl; + } + + /************************************************************************* + * isScriptValid + * @see com.btr.proxy.selector.pac.PacScriptSource#isScriptValid() + ************************************************************************/ + + public boolean isScriptValid() { + try { + String script = getScriptContent(); + if (script == null || script.trim().length() == 0) { + Logger.log(getClass(), LogLevel.DEBUG, "PAC script is empty. Skipping script!"); + return false; + } + if (script.indexOf("FindProxyForURL") == -1) { + Logger.log(getClass(), LogLevel.DEBUG, "PAC script entry point FindProxyForURL not found. Skipping script!"); + return false; + } + return true; + } catch (IOException e) { + Logger.log(getClass(), LogLevel.DEBUG, "File reading error: {0}", e); + return false; + } + } + +} diff --git a/src/main/java/com/btr/proxy/selector/whitelist/DefaultWhiteListParser.java b/src/main/java/com/btr/proxy/selector/whitelist/DefaultWhiteListParser.java new file mode 100644 index 0000000..0f949e1 --- /dev/null +++ b/src/main/java/com/btr/proxy/selector/whitelist/DefaultWhiteListParser.java @@ -0,0 +1,78 @@ +package com.btr.proxy.selector.whitelist; + +import java.util.ArrayList; +import java.util.List; + +import com.btr.proxy.selector.whitelist.HostnameFilter.Mode; +import com.btr.proxy.util.UriFilter; + +/***************************************************************************** + * Default implementation for an white list parser. This will support the most + * common forms of filters found in white lists. + * The white list is a comma (or space) separated list of domain names or IP addresses. + * The following section shows some examples. + * + * .mynet.com - Filters all host names ending with .mynet.com + * *.mynet.com - Filters all host names ending with .mynet.com + * www.mynet.* - Filters all host names starting with www.mynet. + * 123.12.32.1 - Filters the IP 123.12.32.1 + * 123.12.32.1/255 - Filters the IP range + * http://www.mynet.com - Filters only HTTP protocol not FTP and no HTTPS + * + * Example of a list: + * + * .mynet.com, *.my-other-net.org, 123.55.23.222, 123.55.23.0/24 + * + * Some info about this topic can be found here: + * http://kb.mozillazine.org/No_proxy_for + * http://technet.microsoft.com/en-us/library/dd361953.aspx + * + * Note that this implementation does not cover all variations of all browsers + * but should cover the most used formats. + * + * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009 + ****************************************************************************/ + +public class DefaultWhiteListParser implements WhiteListParser { + + /************************************************************************* + * parseWhiteList + * @see com.btr.proxy.selector.whitelist.WhiteListParser#parseWhiteList(java.lang.String) + ************************************************************************/ + + public List
+ * The xml plist dtd can be found at http://www.apple.com/DTDs/PropertyList-1.0.dtd + *
+ * The plist spec handles 8 types of objects: booleans, real, integers, dates, binary data, + * strings, arrays (lists) and dictionaries (maps). + *
+ * The java Plist lib handles converting xml plists to a nested {@code Map
+ * The following mapping will be done when converting from plist to Map:
+ *
+ * When converting from Map -> plist the conversion is as follows:
+ *
+ * true/false -> Boolean
+ * real -> Double
+ * integer -> Integer/Long (depends on size, values exceeding an int will be rendered as longs)
+ * data -> byte[]
+ * string -> String
+ * array -> List
+ * dict -> Map
+ *
+ *
+ * Boolean -> true/false
+ * Float/Double -> real
+ * Byte/Short/Integer/Long -> integer
+ * byte[] -> data
+ * List -> array
+ * Map -> dict
+ *
+ *
+ * @author Christoffer Lerno / Modified by Bernd Rosstauscher
+ */
+public final class PListParser
+{
+ /*****************************************************************************
+ * Exception is used for XML parse problems.
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+ public static class XmlParseException extends Exception {
+
+ /** Comment for serialVersionUID
*/
+ private static final long serialVersionUID = 1L;
+
+ /*************************************************************************
+ * Constructor
+ ************************************************************************/
+
+ public XmlParseException() {
+ super();
+ }
+
+ /*************************************************************************
+ * Constructor
+ * @param msg the error message
+ ************************************************************************/
+
+ public XmlParseException(String msg) {
+ super(msg);
+ }
+
+ /*************************************************************************
+ * Constructor
+ * @param msg error message
+ * @param e the cause.
+ ************************************************************************/
+
+ public XmlParseException(String msg, Exception e) {
+ super(msg, e);
+ }
+
+ }
+
+ /*****************************************************************************
+ * Small helper class representing a tree node.
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+ public static class Dict implements Iterable