diff options
Diffstat (limited to 'toolkit/system/windowsproxy')
-rw-r--r-- | toolkit/system/windowsproxy/ProxyUtils.cpp | 162 | ||||
-rw-r--r-- | toolkit/system/windowsproxy/ProxyUtils.h | 21 | ||||
-rw-r--r-- | toolkit/system/windowsproxy/components.conf | 13 | ||||
-rw-r--r-- | toolkit/system/windowsproxy/moz.build | 18 | ||||
-rw-r--r-- | toolkit/system/windowsproxy/nsWindowsSystemProxySettings.cpp | 247 | ||||
-rw-r--r-- | toolkit/system/windowsproxy/tests/gtest/TestProxyBypassRules.cpp | 47 | ||||
-rw-r--r-- | toolkit/system/windowsproxy/tests/gtest/moz.build | 18 |
7 files changed, 526 insertions, 0 deletions
diff --git a/toolkit/system/windowsproxy/ProxyUtils.cpp b/toolkit/system/windowsproxy/ProxyUtils.cpp new file mode 100644 index 0000000000..37af2f8f4a --- /dev/null +++ b/toolkit/system/windowsproxy/ProxyUtils.cpp @@ -0,0 +1,162 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ProxyUtils.h" + +#include "mozilla/IntegerRange.h" +#include "nsReadableUtils.h" +#include "nsTArray.h" +#include "prnetdb.h" +#include "prtypes.h" + +namespace mozilla { +namespace toolkit { +namespace system { + +/** + * Normalize the short IP form into the complete form. + * For example, it converts "192.168" into "192.168.0.0" + */ +static void NormalizeAddr(const nsACString& aAddr, nsCString& aNormalized) { + nsTArray<nsCString> addr; + ParseString(aAddr, '.', addr); + aNormalized = StringJoin("."_ns, IntegerRange(4), + [&addr](nsACString& dst, const int i) { + if (i < addr.Length()) { + dst.Append(addr[i]); + } else { + dst.Append('0'); + } + }); +} + +static PRUint32 MaskIPv4Addr(PRUint32 aAddr, uint16_t aMaskLen) { + if (aMaskLen == 32) { + return aAddr; + } + return PR_htonl(PR_ntohl(aAddr) & (~0L << (32 - aMaskLen))); +} + +static void MaskIPv6Addr(PRIPv6Addr& aAddr, uint16_t aMaskLen) { + if (aMaskLen == 128) { + return; + } + + if (aMaskLen > 96) { + aAddr.pr_s6_addr32[3] = + PR_htonl(PR_ntohl(aAddr.pr_s6_addr32[3]) & (~0L << (128 - aMaskLen))); + } else if (aMaskLen > 64) { + aAddr.pr_s6_addr32[3] = 0; + aAddr.pr_s6_addr32[2] = + PR_htonl(PR_ntohl(aAddr.pr_s6_addr32[2]) & (~0L << (96 - aMaskLen))); + } else if (aMaskLen > 32) { + aAddr.pr_s6_addr32[3] = 0; + aAddr.pr_s6_addr32[2] = 0; + aAddr.pr_s6_addr32[1] = + PR_htonl(PR_ntohl(aAddr.pr_s6_addr32[1]) & (~0L << (64 - aMaskLen))); + } else { + aAddr.pr_s6_addr32[3] = 0; + aAddr.pr_s6_addr32[2] = 0; + aAddr.pr_s6_addr32[1] = 0; + aAddr.pr_s6_addr32[0] = + PR_htonl(PR_ntohl(aAddr.pr_s6_addr32[0]) & (~0L << (32 - aMaskLen))); + } + + return; +} + +static bool IsMatchMask(const nsACString& aHost, const nsACString& aOverride) { + nsresult rv; + + auto tokenEnd = aOverride.FindChar('/'); + if (tokenEnd == -1) { + return false; + } + + nsAutoCString prefixStr( + Substring(aOverride, tokenEnd + 1, aOverride.Length() - tokenEnd - 1)); + auto maskLen = prefixStr.ToInteger(&rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + + nsAutoCString override(aOverride); + NormalizeAddr(Substring(aOverride, 0, tokenEnd), override); + + PRNetAddr prAddrHost; + PRNetAddr prAddrOverride; + if (PR_SUCCESS != + PR_StringToNetAddr(PromiseFlatCString(aHost).get(), &prAddrHost) || + PR_SUCCESS != PR_StringToNetAddr(override.get(), &prAddrOverride)) { + return false; + } + + if (prAddrHost.raw.family == PR_AF_INET && + prAddrOverride.raw.family == PR_AF_INET) { + return MaskIPv4Addr(prAddrHost.inet.ip, maskLen) == + MaskIPv4Addr(prAddrOverride.inet.ip, maskLen); + } else if (prAddrHost.raw.family == PR_AF_INET6 && + prAddrOverride.raw.family == PR_AF_INET6) { + MaskIPv6Addr(prAddrHost.ipv6.ip, maskLen); + MaskIPv6Addr(prAddrOverride.ipv6.ip, maskLen); + + return memcmp(&prAddrHost.ipv6.ip, &prAddrOverride.ipv6.ip, + sizeof(PRIPv6Addr)) == 0; + } + + return false; +} + +static bool IsMatchWildcard(const nsACString& aHost, + const nsACString& aOverride) { + nsAutoCString host(aHost); + nsAutoCString override(aOverride); + + int32_t overrideLength = override.Length(); + int32_t tokenStart = 0; + int32_t offset = 0; + bool star = false; + + while (tokenStart < overrideLength) { + int32_t tokenEnd = override.FindChar('*', tokenStart); + if (tokenEnd == tokenStart) { + // Star is the first character in the token. + star = true; + tokenStart++; + // If the character following the '*' is a '.' character then skip + // it so that "*.foo.com" allows "foo.com". + if (override.FindChar('.', tokenStart) == tokenStart) { + nsAutoCString token(Substring(override, tokenStart + 1, + overrideLength - tokenStart - 1)); + if (host.Equals(token)) { + return true; + } + } + } else { + if (tokenEnd == -1) { + tokenEnd = overrideLength; // no '*' char, match rest of string + } + nsAutoCString token( + Substring(override, tokenStart, tokenEnd - tokenStart)); + offset = host.Find(token, /* aIgnoreCase = */ false, offset); + if (offset == -1 || (!star && offset)) { + return false; + } + star = false; + tokenStart = tokenEnd; + offset += token.Length(); + } + } + + return (star || (offset == static_cast<int32_t>(host.Length()))); +} + +bool IsHostProxyEntry(const nsACString& aHost, const nsACString& aOverride) { + return IsMatchMask(aHost, aOverride) || IsMatchWildcard(aHost, aOverride); +} + +} // namespace system +} // namespace toolkit +} // namespace mozilla diff --git a/toolkit/system/windowsproxy/ProxyUtils.h b/toolkit/system/windowsproxy/ProxyUtils.h new file mode 100644 index 0000000000..547ef643b5 --- /dev/null +++ b/toolkit/system/windowsproxy/ProxyUtils.h @@ -0,0 +1,21 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_toolkit_system_windowsproxy_ProxyUtils_h +#define mozilla_toolkit_system_windowsproxy_ProxyUtils_h + +#include "nsString.h" + +namespace mozilla { +namespace toolkit { +namespace system { + +bool IsHostProxyEntry(const nsACString& aHost, const nsACString& aOverride); + +} // namespace system +} // namespace toolkit +} // namespace mozilla + +#endif // mozilla_toolkit_system_windowsproxy_ProxyUtils_h diff --git a/toolkit/system/windowsproxy/components.conf b/toolkit/system/windowsproxy/components.conf new file mode 100644 index 0000000000..4c8c62fc43 --- /dev/null +++ b/toolkit/system/windowsproxy/components.conf @@ -0,0 +1,13 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +Classes = [ + { + 'cid': '{4e22d3ea-aaa2-436e-ada4-9247de57d367}', + 'contract_ids': ['@mozilla.org/system-proxy-settings;1'], + 'type': 'nsWindowsSystemProxySettings', + }, +] diff --git a/toolkit/system/windowsproxy/moz.build b/toolkit/system/windowsproxy/moz.build new file mode 100644 index 0000000000..8cc1f085f7 --- /dev/null +++ b/toolkit/system/windowsproxy/moz.build @@ -0,0 +1,18 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +with Files("**"): + BUG_COMPONENT = ("Core", "Networking") + +TEST_DIRS += ["tests/gtest"] + +SOURCES += ["nsWindowsSystemProxySettings.cpp", "ProxyUtils.cpp"] + +XPCOM_MANIFESTS += [ + "components.conf", +] + +FINAL_LIBRARY = "xul" diff --git a/toolkit/system/windowsproxy/nsWindowsSystemProxySettings.cpp b/toolkit/system/windowsproxy/nsWindowsSystemProxySettings.cpp new file mode 100644 index 0000000000..72ff740788 --- /dev/null +++ b/toolkit/system/windowsproxy/nsWindowsSystemProxySettings.cpp @@ -0,0 +1,247 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <windows.h> +#include <ras.h> +#include <wininet.h> + +#include "mozilla/ArrayUtils.h" +#include "mozilla/Attributes.h" +#include "nsISystemProxySettings.h" +#include "mozilla/Components.h" +#include "nsPrintfCString.h" +#include "nsNetCID.h" +#include "nsThreadUtils.h" +#include "GeckoProfiler.h" +#include "prnetdb.h" +#include "ProxyUtils.h" + +class nsWindowsSystemProxySettings final : public nsISystemProxySettings { + public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSISYSTEMPROXYSETTINGS + + nsWindowsSystemProxySettings(){}; + + private: + ~nsWindowsSystemProxySettings(){}; + + bool MatchOverride(const nsACString& aHost); + bool PatternMatch(const nsACString& aHost, const nsACString& aOverride); +}; + +NS_IMPL_ISUPPORTS(nsWindowsSystemProxySettings, nsISystemProxySettings) + +NS_IMETHODIMP +nsWindowsSystemProxySettings::GetMainThreadOnly(bool* aMainThreadOnly) { + // bug 1366133: if you change this to main thread only, please handle + // nsProtocolProxyService::Resolve_Internal carefully to avoid hang on main + // thread. + *aMainThreadOnly = false; + return NS_OK; +} + +static void SetProxyResult(const char* aType, const nsACString& aHostPort, + nsACString& aResult) { + aResult.AssignASCII(aType); + aResult.Append(' '); + aResult.Append(aHostPort); +} + +static void SetProxyResultDirect(nsACString& aResult) { + // For whatever reason, a proxy is not to be used. + aResult.AssignLiteral("DIRECT"); +} + +static nsresult ReadInternetOption(uint32_t aOption, uint32_t& aFlags, + nsAString& aValue) { + // Bug 1366133: InternetGetConnectedStateExW() may cause hangs + MOZ_ASSERT(!NS_IsMainThread()); + + DWORD connFlags = 0; + WCHAR connName[RAS_MaxEntryName + 1]; + MOZ_SEH_TRY { + InternetGetConnectedStateExW(&connFlags, connName, + mozilla::ArrayLength(connName), 0); + } + MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { return NS_ERROR_FAILURE; } + + INTERNET_PER_CONN_OPTIONW options[2]; + options[0].dwOption = INTERNET_PER_CONN_FLAGS_UI; + options[1].dwOption = aOption; + + INTERNET_PER_CONN_OPTION_LISTW list; + list.dwSize = sizeof(INTERNET_PER_CONN_OPTION_LISTW); + list.pszConnection = + connFlags & INTERNET_CONNECTION_MODEM ? connName : nullptr; + list.dwOptionCount = mozilla::ArrayLength(options); + list.dwOptionError = 0; + list.pOptions = options; + + unsigned long size = sizeof(INTERNET_PER_CONN_OPTION_LISTW); + if (!InternetQueryOptionW(nullptr, INTERNET_OPTION_PER_CONNECTION_OPTION, + &list, &size)) { + return NS_ERROR_FAILURE; + } + + aFlags = options[0].Value.dwValue; + aValue.Assign(options[1].Value.pszValue); + GlobalFree(options[1].Value.pszValue); + + return NS_OK; +} + +bool nsWindowsSystemProxySettings::MatchOverride(const nsACString& aHost) { + nsresult rv; + uint32_t flags = 0; + nsAutoString buf; + + rv = ReadInternetOption(INTERNET_PER_CONN_PROXY_BYPASS, flags, buf); + if (NS_FAILED(rv)) return false; + + NS_ConvertUTF16toUTF8 cbuf(buf); + + nsAutoCString host(aHost); + int32_t start = 0; + int32_t end = cbuf.Length(); + + // Windows formats its proxy override list in the form: + // server;server;server where 'server' is a server name pattern or IP + // address, or "<local>". "<local>" must be translated to + // "localhost;127.0.0.1". + // In a server name pattern, a '*' character matches any substring and + // all other characters must match themselves; the whole pattern must match + // the whole hostname. + while (true) { + int32_t delimiter = cbuf.FindCharInSet(" ;", start); + if (delimiter == -1) delimiter = end; + + if (delimiter != start) { + const nsAutoCString override(Substring(cbuf, start, delimiter - start)); + if (override.EqualsLiteral("<local>")) { + PRNetAddr addr; + bool isIpAddr = (PR_StringToNetAddr(host.get(), &addr) == PR_SUCCESS); + + // Don't use proxy for local hosts (plain hostname, no dots) + if (!isIpAddr && !host.Contains('.')) { + return true; + } + + if (host.EqualsLiteral("127.0.0.1") || host.EqualsLiteral("::1")) { + return true; + } + } else if (PatternMatch(host, override)) { + return true; + } + } + + if (delimiter == end) break; + start = ++delimiter; + } + + return false; +} + +bool nsWindowsSystemProxySettings::PatternMatch(const nsACString& aHost, + const nsACString& aOverride) { + return mozilla::toolkit::system::IsHostProxyEntry(aHost, aOverride); +} + +nsresult nsWindowsSystemProxySettings::GetPACURI(nsACString& aResult) { + AUTO_PROFILER_LABEL("nsWindowsSystemProxySettings::GetPACURI", OTHER); + nsresult rv; + uint32_t flags = 0; + nsAutoString buf; + + rv = ReadInternetOption(INTERNET_PER_CONN_AUTOCONFIG_URL, flags, buf); + if (!(flags & PROXY_TYPE_AUTO_PROXY_URL)) { + aResult.Truncate(); + return rv; + } + + if (NS_SUCCEEDED(rv)) aResult = NS_ConvertUTF16toUTF8(buf); + return rv; +} + +nsresult nsWindowsSystemProxySettings::GetProxyForURI(const nsACString& aSpec, + const nsACString& aScheme, + const nsACString& aHost, + const int32_t aPort, + nsACString& aResult) { + nsresult rv; + uint32_t flags = 0; + nsAutoString buf; + + rv = ReadInternetOption(INTERNET_PER_CONN_PROXY_SERVER, flags, buf); + if (NS_FAILED(rv) || !(flags & PROXY_TYPE_PROXY)) { + SetProxyResultDirect(aResult); + return NS_OK; + } + + if (MatchOverride(aHost)) { + SetProxyResultDirect(aResult); + return NS_OK; + } + + NS_ConvertUTF16toUTF8 cbuf(buf); + + constexpr auto kSocksPrefix = "socks="_ns; + nsAutoCString prefix; + ToLowerCase(aScheme, prefix); + + prefix.Append('='); + + nsAutoCString specificProxy; + nsAutoCString defaultProxy; + nsAutoCString socksProxy; + int32_t start = 0; + int32_t end = cbuf.Length(); + + while (true) { + int32_t delimiter = cbuf.FindCharInSet(" ;", start); + if (delimiter == -1) delimiter = end; + + if (delimiter != start) { + const nsAutoCString proxy(Substring(cbuf, start, delimiter - start)); + if (proxy.FindChar('=') == -1) { + // If a proxy name is listed by itself, it is used as the + // default proxy for any protocols that do not have a specific + // proxy specified. + // (http://msdn.microsoft.com/en-us/library/aa383996%28VS.85%29.aspx) + defaultProxy = proxy; + } else if (proxy.Find(prefix) == 0) { + // To list a proxy for a specific protocol, the string must + // follow the format "<protocol>=<protocol>://<proxy_name>". + // (http://msdn.microsoft.com/en-us/library/aa383996%28VS.85%29.aspx) + specificProxy = Substring(proxy, prefix.Length()); + break; + } else if (proxy.Find(kSocksPrefix) == 0) { + // SOCKS proxy. + socksProxy = + Substring(proxy, kSocksPrefix.Length()); // "socks=" length. + } + } + + if (delimiter == end) break; + start = ++delimiter; + } + + if (!specificProxy.IsEmpty()) + SetProxyResult("PROXY", specificProxy, + aResult); // Protocol-specific proxy. + else if (!defaultProxy.IsEmpty()) + SetProxyResult("PROXY", defaultProxy, aResult); // Default proxy. + else if (!socksProxy.IsEmpty()) + SetProxyResult("SOCKS", socksProxy, aResult); // SOCKS proxy. + else + SetProxyResultDirect(aResult); // Direct connection. + + return NS_OK; +} + +NS_IMPL_COMPONENT_FACTORY(nsWindowsSystemProxySettings) { + return mozilla::MakeAndAddRef<nsWindowsSystemProxySettings>() + .downcast<nsISupports>(); +} diff --git a/toolkit/system/windowsproxy/tests/gtest/TestProxyBypassRules.cpp b/toolkit/system/windowsproxy/tests/gtest/TestProxyBypassRules.cpp new file mode 100644 index 0000000000..9ccb216388 --- /dev/null +++ b/toolkit/system/windowsproxy/tests/gtest/TestProxyBypassRules.cpp @@ -0,0 +1,47 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "gtest/gtest.h" +#include "ProxyUtils.h" + +using namespace mozilla::toolkit::system; + +TEST(WindowsProxy, TestProxyBypassRules) +{ + EXPECT_TRUE(IsHostProxyEntry("mozilla.org"_ns, "mozilla.org"_ns)); + EXPECT_TRUE(IsHostProxyEntry("mozilla.org"_ns, "*mozilla.org"_ns)); + EXPECT_TRUE(IsHostProxyEntry("mozilla.org"_ns, "*.mozilla.org"_ns)); + EXPECT_FALSE(IsHostProxyEntry("notmozilla.org"_ns, "*.mozilla.org"_ns)); + EXPECT_TRUE(IsHostProxyEntry("www.mozilla.org"_ns, "*mozilla.org"_ns)); + EXPECT_TRUE(IsHostProxyEntry("www.mozilla.org"_ns, "*.mozilla.org"_ns)); + EXPECT_TRUE(IsHostProxyEntry("www.mozilla.com"_ns, "*.mozilla.*"_ns)); +} + +TEST(WindowsProxy, TestProxyBypassRulesIPv4) +{ + EXPECT_TRUE(IsHostProxyEntry("192.168.1.1"_ns, "192.168.1.*"_ns)); + EXPECT_FALSE(IsHostProxyEntry("192.168.1.1"_ns, "192.168.2.*"_ns)); + + EXPECT_TRUE(IsHostProxyEntry("10.1.2.3"_ns, "10.0.0.0/8"_ns)); + EXPECT_TRUE(IsHostProxyEntry("192.168.192.1"_ns, "192.168/16"_ns)); + EXPECT_FALSE(IsHostProxyEntry("192.168.192.1"_ns, "192.168/17"_ns)); + EXPECT_TRUE(IsHostProxyEntry("192.168.192.1"_ns, "192.168.128/17"_ns)); + EXPECT_TRUE(IsHostProxyEntry("192.168.1.1"_ns, "192.168.1.1/32"_ns)); +} + +TEST(WindowsProxy, TestProxyBypassRulesIPv6) +{ + EXPECT_TRUE(IsHostProxyEntry("2001:0DB8:ABCD:0012:0123:4567:89AB:CDEF"_ns, + "2001:db8:abcd:0012::0/64"_ns)); + EXPECT_TRUE(IsHostProxyEntry("2001:0DB8:ABCD:0012:0000:4567:89AB:CDEF"_ns, + "2001:db8:abcd:0012::0/80"_ns)); + EXPECT_FALSE(IsHostProxyEntry("2001:0DB8:ABCD:0012:0123:4567:89AB:CDEF"_ns, + "2001:db8:abcd:0012::0/80"_ns)); + EXPECT_TRUE(IsHostProxyEntry("2001:0DB8:ABCD:0012:0000:0000:89AB:CDEF"_ns, + "2001:db8:abcd:0012::0/96"_ns)); + EXPECT_FALSE(IsHostProxyEntry("2001:0DB8:ABCD:0012:0123:4567:89AB:CDEF"_ns, + "2001:db8:abcd:0012::0/96"_ns)); +} diff --git a/toolkit/system/windowsproxy/tests/gtest/moz.build b/toolkit/system/windowsproxy/tests/gtest/moz.build new file mode 100644 index 0000000000..46501cdf86 --- /dev/null +++ b/toolkit/system/windowsproxy/tests/gtest/moz.build @@ -0,0 +1,18 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at http://mozilla.org/MPL/2.0/. + +UNIFIED_SOURCES += [ + "TestProxyBypassRules.cpp", +] + +LOCAL_INCLUDES += [ + "/toolkit/system/windowsproxy", +] + +FINAL_LIBRARY = "xul-gtest" + +if CONFIG["CC_TYPE"] in ("clang", "gcc"): + CXXFLAGS += ["-Wshadow"] |