tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 95420b7ba8e953959bb92163af00223cadb993fd
parent 775663714117e936b5d0202bc4ed505e3290d0ae
Author: yooughtul <2733543664@qq.com>
Date:   Thu,  8 Jan 2026 09:00:22 +0000

Bug 1944213 - Migrate Unix proxy env var logic to Windows + add TestProtocolProxyService test. r=valentin,necko-reviewers

Differential Revision: https://phabricator.services.mozilla.com/D274405

Diffstat:
Mnetwerk/test/gtest/TestProtocolProxyService.cpp | 132+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtoolkit/moz.build | 5++++-
Mtoolkit/system/commonproxy/ProxyUtils.cpp | 146+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtoolkit/system/commonproxy/ProxyUtils.h | 4+++-
Mtoolkit/system/osxproxy/nsOSXSystemProxySettings.mm | 6++++++
Mtoolkit/system/unixproxy/moz.build | 4++++
Mtoolkit/system/unixproxy/nsUnixSystemProxySettings.cpp | 127++++++-------------------------------------------------------------------------
Mtoolkit/system/windowsproxy/nsWindowsSystemProxySettings.cpp | 30++++++++++++++----------------
Atoolkit/system/windowsproxy/nsWindowsSystemProxySettings.h | 27+++++++++++++++++++++++++++
9 files changed, 345 insertions(+), 136 deletions(-)

diff --git a/netwerk/test/gtest/TestProtocolProxyService.cpp b/netwerk/test/gtest/TestProtocolProxyService.cpp @@ -8,6 +8,8 @@ #include "nsServiceManagerUtils.h" #include "mozilla/Preferences.h" #include "nsNetUtil.h" +#include "prenv.h" +#include "nsISystemProxySettings.h" namespace mozilla { namespace net { @@ -160,5 +162,135 @@ TEST(TestProtocolProxyService, LoadHostFilters) CheckPortDomain(false); } +TEST(TestProtocolProxyService, Proxy_Env_Vars) +{ +#if defined(ANDROID) + GTEST_SKIP() << "Environment variable proxy support not available on Android"; +#endif + + nsCOMPtr<nsISystemProxySettings> systemProxy = + do_GetService(NS_SYSTEMPROXYSETTINGS_CONTRACTID); + ASSERT_TRUE(systemProxy != nullptr); + + auto CheckProxy = [&](const char* url, const char* expected) { + nsCOMPtr<nsIURI> uri; + ASSERT_EQ(NS_NewURI(getter_AddRefs(uri), url), NS_OK); + nsAutoCString spec, scheme, host; + int32_t port = -1; + uri->GetSpec(spec); + uri->GetScheme(scheme); + uri->GetHost(host); + uri->GetPort(&port); + + nsAutoCString result; + nsresult rv = systemProxy->GetProxyForURI(spec, scheme, host, port, result); + ASSERT_EQ(rv, NS_OK); + // Check if result contains expected string. + EXPECT_TRUE(result.Find(expected) != kNotFound) + << "URL: " << url << ", Result: " << result.get() + << ", Expected: " << expected; + }; + + // 1. HTTP Proxy + { + PR_SetEnv("http_proxy=http://127.0.0.1:8080"); + CheckProxy("http://example.com", "PROXY 127.0.0.1:8080"); + PR_SetEnv("http_proxy="); + } + + // 2. HTTPS Proxy + { + PR_SetEnv("https_proxy=http://127.0.0.1:8443"); + CheckProxy("https://example.com", "PROXY 127.0.0.1:8443"); + PR_SetEnv("https_proxy="); + } + + // 3. All Proxy (fallback) + { + PR_SetEnv("all_proxy=http://127.0.0.1:9090"); + CheckProxy("ftp://example.com", "PROXY 127.0.0.1:9090"); + PR_SetEnv("all_proxy="); + } + + // 4. No Proxy + { + PR_SetEnv("http_proxy=http://127.0.0.1:8080"); + PR_SetEnv("no_proxy=example.com,.test.com"); + + // Matches example.com + CheckProxy("http://example.com", "DIRECT"); + // Matches .test.com suffix + CheckProxy("http://sub.test.com", "DIRECT"); + // Does not match + CheckProxy("http://other.com", "PROXY 127.0.0.1:8080"); + + PR_SetEnv("http_proxy="); + PR_SetEnv("no_proxy="); + } + + // 5. No Proxy with specific port + { + PR_SetEnv("http_proxy=http://127.0.0.1:8080"); + PR_SetEnv("no_proxy=example.com:8080"); + + // Matches example.com:8080 + CheckProxy("http://example.com:8080", "DIRECT"); + // Does not match example.com on default port (80) + CheckProxy("http://example.com", "PROXY 127.0.0.1:8080"); + // Does not match example.com:9090 + CheckProxy("http://example.com:9090", "PROXY 127.0.0.1:8080"); + + PR_SetEnv("http_proxy="); + PR_SetEnv("no_proxy="); + } + + // 6. No Proxy with mixed port rules + { + PR_SetEnv("http_proxy=http://127.0.0.1:8080"); + PR_SetEnv("https_proxy=http://127.0.0.1:8443"); + PR_SetEnv("no_proxy=exact.com:9443,wildcard.com"); + + // Matches exact.com:9443 + CheckProxy("https://exact.com:9443", "DIRECT"); + // Does not match exact.com on default HTTPS port + CheckProxy("https://exact.com", "PROXY 127.0.0.1:8443"); + // Does not match exact.com on a different port + CheckProxy("https://exact.com:8443", "PROXY 127.0.0.1:8443"); + // Matches wildcard.com on any port + CheckProxy("http://wildcard.com", "DIRECT"); + CheckProxy("http://wildcard.com:8080", "DIRECT"); + CheckProxy("https://wildcard.com:443", "DIRECT"); + + PR_SetEnv("http_proxy="); + PR_SetEnv("https_proxy="); + PR_SetEnv("no_proxy="); + } + + // 7. WebSocket (ws -> http_proxy) + { + PR_SetEnv("http_proxy=http://127.0.0.1:8080"); + CheckProxy("ws://example.com", "PROXY 127.0.0.1:8080"); + PR_SetEnv("http_proxy="); + } + + // 8. WebSocket Secure (wss -> https_proxy) + { + PR_SetEnv("https_proxy=http://127.0.0.1:8443"); + CheckProxy("wss://example.com", "PROXY 127.0.0.1:8443"); + PR_SetEnv("https_proxy="); + } + + // 9. default port + { + PR_SetEnv("http_proxy=http://127.0.0.1"); + CheckProxy("http://example.com", "PROXY 127.0.0.1"); + PR_SetEnv("http_proxy="); + + PR_SetEnv("https_proxy=http://127.0.0.1"); + CheckProxy("https://example.com", "PROXY 127.0.0.1"); + PR_SetEnv("https_proxy="); + } +} + } // namespace net } // namespace mozilla diff --git a/toolkit/moz.build b/toolkit/moz.build @@ -52,7 +52,10 @@ DIRS += ["xre"] if CONFIG["MOZ_WIDGET_TOOLKIT"] != "android": DIRS += ["mozapps/handling"] if CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk": - DIRS += ["system/unixproxy"] + DIRS += [ + "system/commonproxy", + "system/unixproxy", + ] elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "cocoa": DIRS += [ "mozapps/macos-frameworks", diff --git a/toolkit/system/commonproxy/ProxyUtils.cpp b/toolkit/system/commonproxy/ProxyUtils.cpp @@ -10,6 +10,10 @@ #include "nsTArray.h" #include "prnetdb.h" #include "prtypes.h" +#include "prenv.h" +#include "nsCOMPtr.h" +#include "nsNetUtil.h" +#include "nsIURI.h" namespace mozilla { namespace toolkit { @@ -153,6 +157,148 @@ static bool IsMatchWildcard(const nsACString& aHost, return (star || (offset == static_cast<int32_t>(host.Length()))); } +static const char* GetEnvRetryUppercase(const nsCString& aEnv) { + nsAutoCString env(aEnv); + const char* proxyVal = PR_GetEnv(env.get()); + if (proxyVal) { + return proxyVal; + } + ToUpperCase(env); + proxyVal = PR_GetEnv(env.get()); + return proxyVal; +} + +static bool IsInNoProxyList(const nsACString& aScheme, const nsACString& aHost, + int32_t aPort, const char* noProxyVal) { + NS_ASSERTION(aPort >= -1, "Invalid port"); + + int32_t effectivePort = aPort; + if (aPort == -1) { + if (aScheme.EqualsLiteral("http") || aScheme.EqualsLiteral("ws")) { + effectivePort = 80; + } else if (aScheme.EqualsLiteral("https") || aScheme.EqualsLiteral("wss")) { + effectivePort = 443; + } else if (aScheme.EqualsLiteral("ftp")) { + effectivePort = 21; + } + } + + nsAutoCString noProxy(noProxyVal); + if (noProxy.EqualsLiteral("*")) return true; + + noProxy.StripWhitespace(); + + nsReadingIterator<char> pos; + nsReadingIterator<char> end; + noProxy.BeginReading(pos); + noProxy.EndReading(end); + while (pos != end) { + nsReadingIterator<char> last = pos; + nsReadingIterator<char> nextPos; + if (FindCharInReadable(',', last, end)) { + nextPos = last; + ++nextPos; + } else { + last = end; + nextPos = end; + } + + nsReadingIterator<char> colon = pos; + int32_t port = -1; + if (FindCharInReadable(':', colon, last)) { + ++colon; + nsDependentCSubstring portStr(colon, last); + nsAutoCString portStr2( + portStr); // We need this for ToInteger. String API's suck. + nsresult err; + port = portStr2.ToInteger(&err); + if (NS_FAILED(err)) { + port = -2; // don't match any port, so we ignore this pattern + } + --colon; + } else { + colon = last; + } + + if (port == -1 || port == aPort || (aPort == -1 && port == effectivePort)) { + nsDependentCSubstring hostStr(pos, colon); + // By using StringEndsWith instead of an equality comparator, we can + // include sub-domains + if (StringEndsWith(aHost, hostStr, nsCaseInsensitiveCStringComparator)) + return true; + } + + pos = nextPos; + } + + return false; +} + +static void SetProxyResultDirect(nsACString& aResult) { + aResult.AssignLiteral("DIRECT"); +} + +static void SetProxyResult(const char* aType, const nsACString& aHost, + int32_t aPort, nsACString& aResult) { + aResult.AssignASCII(aType); + aResult.Append(' '); + aResult.Append(aHost); + if (aPort > 0) { + aResult.Append(':'); + aResult.AppendInt(aPort); + } +} + +nsresult GetProxyFromEnvironment(const nsACString& aScheme, + const nsACString& aHost, int32_t aPort, + nsACString& aResult) { + nsAutoCString envVar; + envVar.Append(aScheme); + envVar.AppendLiteral("_proxy"); + const char* proxyVal = GetEnvRetryUppercase(envVar); + if (!proxyVal && aScheme == "ws") { + proxyVal = GetEnvRetryUppercase("http_proxy"_ns); + } else if (!proxyVal && aScheme == "wss") { + proxyVal = GetEnvRetryUppercase("https_proxy"_ns); + } + if (!proxyVal) { + proxyVal = GetEnvRetryUppercase("all_proxy"_ns); + if (!proxyVal) { + // Return failure so that the caller can detect the failure and + // fall back to other proxy detection (e.g., WPAD) + return NS_ERROR_FAILURE; + } + } + + const char* noProxyVal = GetEnvRetryUppercase("no_proxy"_ns); + if (noProxyVal && IsInNoProxyList(aScheme, aHost, aPort, noProxyVal)) { + SetProxyResultDirect(aResult); + return NS_OK; + } + + // Use our URI parser to crack the proxy URI + nsCOMPtr<nsIURI> proxyURI; + nsresult rv = NS_NewURI(getter_AddRefs(proxyURI), proxyVal); + NS_ENSURE_SUCCESS(rv, rv); + + // Is there a way to specify "socks://" or something in these environment + // variables? I can't find any documentation. + if (!proxyURI->SchemeIs("http")) { + return NS_ERROR_UNKNOWN_PROTOCOL; + } + + nsAutoCString proxyHost; + rv = proxyURI->GetHost(proxyHost); + NS_ENSURE_SUCCESS(rv, rv); + + int32_t proxyPort; + rv = proxyURI->GetPort(&proxyPort); + NS_ENSURE_SUCCESS(rv, rv); + + SetProxyResult("PROXY", proxyHost, proxyPort, aResult); + return NS_OK; +} + bool IsHostProxyEntry(const nsACString& aHost, const nsACString& aOverride) { return IsMatchMask(aHost, aOverride) || IsMatchWildcard(aHost, aOverride); } diff --git a/toolkit/system/commonproxy/ProxyUtils.h b/toolkit/system/commonproxy/ProxyUtils.h @@ -12,8 +12,10 @@ namespace mozilla { namespace toolkit { namespace system { +nsresult GetProxyFromEnvironment(const nsACString& aScheme, + const nsACString& aHost, int32_t aPort, + nsACString& aResult); bool IsHostProxyEntry(const nsACString& aHost, const nsACString& aOverride); - } // namespace system } // namespace toolkit } // namespace mozilla diff --git a/toolkit/system/osxproxy/nsOSXSystemProxySettings.mm b/toolkit/system/osxproxy/nsOSXSystemProxySettings.mm @@ -293,6 +293,12 @@ nsresult nsOSXSystemProxySettings::GetProxyForURI(const nsACString& aSpec, const nsACString& aHost, const int32_t aPort, nsACString& aResult) { + nsresult rv = mozilla::toolkit::system::GetProxyFromEnvironment( + aScheme, aHost, aPort, aResult); + if (NS_SUCCEEDED(rv)) { + return rv; + } + NS_OBJC_BEGIN_TRY_BLOCK_RETURN; int32_t proxyPort; diff --git a/toolkit/system/unixproxy/moz.build b/toolkit/system/unixproxy/moz.build @@ -21,4 +21,8 @@ XPCOM_MANIFESTS += [ "components.conf", ] +LOCAL_INCLUDES += [ + "../commonproxy", +] + FINAL_LIBRARY = "xul" diff --git a/toolkit/system/unixproxy/nsUnixSystemProxySettings.cpp b/toolkit/system/unixproxy/nsUnixSystemProxySettings.cpp @@ -16,6 +16,7 @@ #include "nsISupportsPrimitives.h" #include "mozilla/widget/GSettings.h" #include "nsReadableUtils.h" +#include "ProxyUtils.h" using namespace mozilla; @@ -64,61 +65,6 @@ nsresult nsUnixSystemProxySettings::GetPACURI(nsACString& aResult) { return NS_OK; } -static bool IsInNoProxyList(const nsACString& aHost, int32_t aPort, - const char* noProxyVal) { - NS_ASSERTION(aPort >= 0, "Negative port?"); - - nsAutoCString noProxy(noProxyVal); - if (noProxy.EqualsLiteral("*")) return true; - - noProxy.StripWhitespace(); - - nsReadingIterator<char> pos; - nsReadingIterator<char> end; - noProxy.BeginReading(pos); - noProxy.EndReading(end); - while (pos != end) { - nsReadingIterator<char> last = pos; - nsReadingIterator<char> nextPos; - if (FindCharInReadable(',', last, end)) { - nextPos = last; - ++nextPos; - } else { - last = end; - nextPos = end; - } - - nsReadingIterator<char> colon = pos; - int32_t port = -1; - if (FindCharInReadable(':', colon, last)) { - ++colon; - nsDependentCSubstring portStr(colon, last); - nsAutoCString portStr2( - portStr); // We need this for ToInteger. String API's suck. - nsresult err; - port = portStr2.ToInteger(&err); - if (NS_FAILED(err)) { - port = -2; // don't match any port, so we ignore this pattern - } - --colon; - } else { - colon = last; - } - - if (port == -1 || port == aPort) { - nsDependentCSubstring hostStr(pos, colon); - // By using StringEndsWith instead of an equality comparator, we can - // include sub-domains - if (StringEndsWith(aHost, hostStr, nsCaseInsensitiveCStringComparator)) - return true; - } - - pos = nextPos; - } - - return false; -} - static void SetProxyResult(const char* aType, const nsACString& aHost, int32_t aPort, nsACString& aResult) { aResult.AssignASCII(aType); @@ -134,67 +80,6 @@ static void SetProxyResultDirect(nsACString& aResult) { aResult.AssignLiteral("DIRECT"); } -static const char* GetEnvRetryUppercase(const nsCString& aEnv) { - nsAutoCString env(aEnv); - const char* proxyVal = PR_GetEnv(env.get()); - if (proxyVal) { - return proxyVal; - } - ToUpperCase(env); - proxyVal = PR_GetEnv(env.get()); - return proxyVal; -} - -static nsresult GetProxyFromEnvironment(const nsACString& aScheme, - const nsACString& aHost, int32_t aPort, - nsACString& aResult) { - nsAutoCString envVar; - envVar.Append(aScheme); - envVar.AppendLiteral("_proxy"); - const char* proxyVal = GetEnvRetryUppercase(envVar); - if (!proxyVal && aScheme == "ws") { - proxyVal = GetEnvRetryUppercase("http_proxy"_ns); - } else if (!proxyVal && aScheme == "wss") { - proxyVal = GetEnvRetryUppercase("https_proxy"_ns); - } - if (!proxyVal) { - proxyVal = GetEnvRetryUppercase("all_proxy"_ns); - if (!proxyVal) { - // Return failure so that the caller can detect the failure and - // fall back to other proxy detection (e.g., WPAD) - return NS_ERROR_FAILURE; - } - } - - const char* noProxyVal = GetEnvRetryUppercase("no_proxy"_ns); - if (noProxyVal && IsInNoProxyList(aHost, aPort, noProxyVal)) { - SetProxyResultDirect(aResult); - return NS_OK; - } - - // Use our URI parser to crack the proxy URI - nsCOMPtr<nsIURI> proxyURI; - nsresult rv = NS_NewURI(getter_AddRefs(proxyURI), proxyVal); - NS_ENSURE_SUCCESS(rv, rv); - - // Is there a way to specify "socks://" or something in these environment - // variables? I can't find any documentation. - if (!proxyURI->SchemeIs("http")) { - return NS_ERROR_UNKNOWN_PROTOCOL; - } - - nsAutoCString proxyHost; - rv = proxyURI->GetHost(proxyHost); - NS_ENSURE_SUCCESS(rv, rv); - - int32_t proxyPort; - rv = proxyURI->GetPort(&proxyPort); - NS_ENSURE_SUCCESS(rv, rv); - - SetProxyResult("PROXY", proxyHost, proxyPort, aResult); - return NS_OK; -} - nsresult nsUnixSystemProxySettings::SetProxyResultFromGSettings( const nsCString& aSchema, const char* aType, nsACString& aResult) { auto& settings = mSchemeProxySettings.LookupOrInsertWith(aSchema, [&] { @@ -377,12 +262,18 @@ nsresult nsUnixSystemProxySettings::GetProxyForURI(const nsACString& aSpec, const nsACString& aHost, const int32_t aPort, nsACString& aResult) { + nsresult rv = mozilla::toolkit::system::GetProxyFromEnvironment( + aScheme, aHost, aPort, aResult); + if (NS_SUCCEEDED(rv)) { + return rv; + } + if (mProxySettings) { - nsresult rv = GetProxyFromGSettings(aScheme, aHost, aPort, aResult); + rv = GetProxyFromGSettings(aScheme, aHost, aPort, aResult); if (NS_SUCCEEDED(rv)) return rv; } - return GetProxyFromEnvironment(aScheme, aHost, aPort, aResult); + return rv; } NS_IMETHODIMP diff --git a/toolkit/system/windowsproxy/nsWindowsSystemProxySettings.cpp b/toolkit/system/windowsproxy/nsWindowsSystemProxySettings.cpp @@ -15,25 +15,12 @@ #include "nsThreadUtils.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); -}; +#include "nsWindowsSystemProxySettings.h" NS_IMPL_ISUPPORTS(nsWindowsSystemProxySettings, nsISystemProxySettings) -NS_IMETHODIMP -nsWindowsSystemProxySettings::GetMainThreadOnly(bool* aMainThreadOnly) { +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. @@ -41,6 +28,9 @@ nsWindowsSystemProxySettings::GetMainThreadOnly(bool* aMainThreadOnly) { return NS_OK; } +nsWindowsSystemProxySettings::nsWindowsSystemProxySettings() {} +nsWindowsSystemProxySettings::~nsWindowsSystemProxySettings() {} + static void SetProxyResult(const char* aType, const nsACString& aHostPort, nsACString& aResult) { aResult.AssignASCII(aType); @@ -178,6 +168,14 @@ nsresult nsWindowsSystemProxySettings::GetProxyForURI(const nsACString& aSpec, uint32_t flags = 0; nsAutoString buf; + rv = mozilla::toolkit::system::GetProxyFromEnvironment(aScheme, aHost, aPort, + aResult); + // GetProxyFromEnvironment has already formatted and set the proxy result + // string in |aResult|. + if (NS_SUCCEEDED(rv) && !aResult.IsEmpty()) { + return NS_OK; + } + rv = ReadInternetOption(INTERNET_PER_CONN_PROXY_SERVER, flags, buf); if (NS_FAILED(rv) || !(flags & PROXY_TYPE_PROXY)) { SetProxyResultDirect(aResult); diff --git a/toolkit/system/windowsproxy/nsWindowsSystemProxySettings.h b/toolkit/system/windowsproxy/nsWindowsSystemProxySettings.h @@ -0,0 +1,27 @@ +/* -*- 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/. */ + +#ifndef mozilla_toolkit_system_nsWindowsSystemProxySettings_h +#define mozilla_toolkit_system_nsWindowsSystemProxySettings_h + +#include "nsISystemProxySettings.h" +#include "nsStringFwd.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); +}; + +#endif // mozilla_toolkit_system_nsWindowsSystemProxySettings_h