forked from mirrors/gecko-dev
Bug 1392272 - P1: [windows] Monitor system proxy changes, r=necko-reviewers,dragana
Differential Revision: https://phabricator.services.mozilla.com/D127724
This commit is contained in:
parent
0992acc367
commit
1b01ccfacd
5 changed files with 593 additions and 95 deletions
|
|
@ -72,6 +72,7 @@ SpinEventLoop
|
|||
StressRunner
|
||||
SuicideManager
|
||||
SuicideThread
|
||||
System Proxy
|
||||
TEQ AwaitIdle
|
||||
TelemetryModule
|
||||
Test Thread
|
||||
|
|
|
|||
|
|
@ -9673,6 +9673,11 @@
|
|||
value: false
|
||||
mirror: always
|
||||
|
||||
- name: network.proxy.detect_system_proxy_changes
|
||||
type: RelaxedAtomicBool
|
||||
value: false
|
||||
mirror: always
|
||||
|
||||
# Some requests during a page load are marked as "tail", mainly trackers, but not only.
|
||||
# This pref controls whether such requests are put to the tail, behind other requests
|
||||
# emerging during page loading process.
|
||||
|
|
|
|||
199
netwerk/base/ProxyConfig.h
Normal file
199
netwerk/base/ProxyConfig.h
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
/* -*- 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_netwerk_base_proxy_config_h
|
||||
#define mozilla_netwerk_base_proxy_config_h
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "nsCRT.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
// NOTE: This file is inspired by Chromium's code.
|
||||
// https://source.chromium.org/chromium/chromium/src/+/main:net/proxy_resolution/proxy_config.h.
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
// ProxyServer stores the {type, host, port} of a proxy server.
|
||||
// ProxyServer is immutable.
|
||||
class ProxyServer final {
|
||||
public:
|
||||
enum class ProxyType {
|
||||
DIRECT = 0,
|
||||
HTTP,
|
||||
HTTPS,
|
||||
SOCKS,
|
||||
SOCKS4,
|
||||
SOCKS5,
|
||||
FTP,
|
||||
// DEFAULT is a special type used on windows only.
|
||||
DEFAULT
|
||||
};
|
||||
|
||||
ProxyServer() = default;
|
||||
|
||||
ProxyServer(ProxyType aType, const nsACString& aHost, int32_t aPort)
|
||||
: mType(aType), mHost(aHost), mPort(aPort) {}
|
||||
|
||||
const nsCString& Host() const { return mHost; }
|
||||
|
||||
int32_t Port() const { return mPort; }
|
||||
|
||||
ProxyType Type() const { return mType; }
|
||||
|
||||
void ToHostAndPortStr(nsACString& aOutput) {
|
||||
aOutput.Truncate();
|
||||
if (mType == ProxyType::DIRECT) {
|
||||
return;
|
||||
}
|
||||
|
||||
aOutput.Assign(mHost);
|
||||
if (mPort != -1) {
|
||||
aOutput.Append(':');
|
||||
aOutput.AppendInt(mPort);
|
||||
}
|
||||
}
|
||||
|
||||
bool operator==(const ProxyServer& aOther) const {
|
||||
return mType == aOther.mType && mHost == aOther.mHost &&
|
||||
mPort == aOther.mPort;
|
||||
}
|
||||
|
||||
bool operator!=(const ProxyServer& aOther) const {
|
||||
return !(*this == aOther);
|
||||
}
|
||||
|
||||
private:
|
||||
ProxyType mType{ProxyType::DIRECT};
|
||||
nsCString mHost;
|
||||
int32_t mPort{-1};
|
||||
};
|
||||
|
||||
// This class includes the information about proxy configuration.
|
||||
// It contains enabled proxy servers, exception list, and the url of PAC
|
||||
// script.
|
||||
class ProxyConfig {
|
||||
public:
|
||||
struct ProxyRules {
|
||||
ProxyRules() = default;
|
||||
~ProxyRules() = default;
|
||||
|
||||
std::map<ProxyServer::ProxyType, ProxyServer> mProxyServers;
|
||||
};
|
||||
|
||||
struct ProxyBypassRules {
|
||||
ProxyBypassRules() = default;
|
||||
~ProxyBypassRules() = default;
|
||||
|
||||
CopyableTArray<nsCString> mExceptions;
|
||||
};
|
||||
|
||||
ProxyConfig() = default;
|
||||
ProxyConfig(const ProxyConfig& config);
|
||||
~ProxyConfig() = default;
|
||||
|
||||
ProxyRules& Rules() { return mRules; }
|
||||
|
||||
const ProxyRules& Rules() const { return mRules; }
|
||||
|
||||
ProxyBypassRules& ByPassRules() { return mBypassRules; }
|
||||
|
||||
const ProxyBypassRules& ByPassRules() const { return mBypassRules; }
|
||||
|
||||
void SetPACUrl(const nsACString& aUrl) { mPACUrl = aUrl; }
|
||||
|
||||
const nsCString& PACUrl() const { return mPACUrl; }
|
||||
|
||||
static ProxyServer::ProxyType ToProxyType(const char* aType) {
|
||||
if (!aType) {
|
||||
return ProxyServer::ProxyType::DIRECT;
|
||||
}
|
||||
|
||||
if (nsCRT::strcasecmp(aType, "http") == 0) {
|
||||
return ProxyServer::ProxyType::HTTP;
|
||||
}
|
||||
if (nsCRT::strcasecmp(aType, "https") == 0) {
|
||||
return ProxyServer::ProxyType::HTTPS;
|
||||
}
|
||||
if (nsCRT::strcasecmp(aType, "socks") == 0) {
|
||||
return ProxyServer::ProxyType::SOCKS;
|
||||
}
|
||||
if (nsCRT::strcasecmp(aType, "socks4") == 0) {
|
||||
return ProxyServer::ProxyType::SOCKS4;
|
||||
}
|
||||
if (nsCRT::strcasecmp(aType, "socks5") == 0) {
|
||||
return ProxyServer::ProxyType::SOCKS5;
|
||||
}
|
||||
if (nsCRT::strcasecmp(aType, "ftp") == 0) {
|
||||
return ProxyServer::ProxyType::FTP;
|
||||
}
|
||||
|
||||
return ProxyServer::ProxyType::DIRECT;
|
||||
}
|
||||
|
||||
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 void ProxyStrToResult(const nsACString& aSpecificProxy,
|
||||
const nsACString& aDefaultProxy,
|
||||
const nsACString& aSocksProxy,
|
||||
nsACString& aResult) {
|
||||
if (!aSpecificProxy.IsEmpty()) {
|
||||
SetProxyResult("PROXY", aSpecificProxy,
|
||||
aResult); // Protocol-specific proxy.
|
||||
} else if (!aDefaultProxy.IsEmpty()) {
|
||||
SetProxyResult("PROXY", aDefaultProxy, aResult); // Default proxy.
|
||||
} else if (!aSocksProxy.IsEmpty()) {
|
||||
SetProxyResult("SOCKS", aSocksProxy, aResult); // SOCKS proxy.
|
||||
} else {
|
||||
SetProxyResultDirect(aResult); // Direct connection.
|
||||
}
|
||||
}
|
||||
|
||||
void GetProxyString(const nsACString& aScheme, nsACString& aResult) {
|
||||
SetProxyResultDirect(aResult);
|
||||
|
||||
nsAutoCString specificProxy;
|
||||
nsAutoCString defaultProxy;
|
||||
nsAutoCString socksProxy;
|
||||
nsAutoCString prefix;
|
||||
ToLowerCase(aScheme, prefix);
|
||||
ProxyServer::ProxyType type = ProxyConfig::ToProxyType(prefix.get());
|
||||
for (auto& [key, value] : mRules.mProxyServers) {
|
||||
// Break the loop if we found a specific proxy.
|
||||
if (key == type) {
|
||||
value.ToHostAndPortStr(specificProxy);
|
||||
break;
|
||||
} else if (key == ProxyServer::ProxyType::DEFAULT) {
|
||||
value.ToHostAndPortStr(defaultProxy);
|
||||
} else if (key == ProxyServer::ProxyType::SOCKS) {
|
||||
value.ToHostAndPortStr(socksProxy);
|
||||
}
|
||||
}
|
||||
|
||||
ProxyStrToResult(specificProxy, defaultProxy, socksProxy, aResult);
|
||||
}
|
||||
|
||||
private:
|
||||
nsCString mPACUrl;
|
||||
ProxyRules mRules;
|
||||
ProxyBypassRules mBypassRules;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_netwerk_base_proxy_config_h
|
||||
|
|
@ -16,3 +16,7 @@ XPCOM_MANIFESTS += [
|
|||
]
|
||||
|
||||
FINAL_LIBRARY = "xul"
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
"/netwerk/base",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -6,30 +6,52 @@
|
|||
#include <windows.h>
|
||||
#include <ras.h>
|
||||
#include <wininet.h>
|
||||
#include <functional>
|
||||
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "nsISystemProxySettings.h"
|
||||
#include "mozilla/Components.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/ProfilerLabels.h"
|
||||
#include "mozilla/StaticPrefs_network.h"
|
||||
#include "mozilla/Tokenizer.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsIWindowsRegKey.h"
|
||||
#include "nsPrintfCString.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "prnetdb.h"
|
||||
#include "ProxyUtils.h"
|
||||
#include "ProxyConfig.h"
|
||||
|
||||
class nsWindowsSystemProxySettings final : public nsISystemProxySettings {
|
||||
using namespace mozilla::net;
|
||||
|
||||
class nsWindowsSystemProxySettings : public nsISystemProxySettings {
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSISYSTEMPROXYSETTINGS
|
||||
|
||||
nsWindowsSystemProxySettings(){};
|
||||
virtual nsresult Init() { return NS_OK; }
|
||||
|
||||
private:
|
||||
~nsWindowsSystemProxySettings(){};
|
||||
protected:
|
||||
virtual ~nsWindowsSystemProxySettings() = default;
|
||||
|
||||
bool MatchOverride(const nsACString& aHost);
|
||||
bool MatchOverrideInternal(const nsACString& aHost,
|
||||
const nsACString& aOverrideRule);
|
||||
bool PatternMatch(const nsACString& aHost, const nsACString& aOverride);
|
||||
nsresult ReadProxyRules(
|
||||
uint32_t aOptions,
|
||||
const std::function<bool(uint32_t aFlags)>& aFlagsHandler,
|
||||
const std::function<void(const nsACString& aRule, bool& aContinue)>&
|
||||
aRuleHandler);
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsWindowsSystemProxySettings, nsISystemProxySettings)
|
||||
|
|
@ -43,18 +65,6 @@ nsWindowsSystemProxySettings::GetMainThreadOnly(bool* aMainThreadOnly) {
|
|||
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
|
||||
|
|
@ -93,20 +103,8 @@ static nsresult ReadInternetOption(uint32_t aOption, uint32_t& aFlags,
|
|||
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();
|
||||
|
||||
bool nsWindowsSystemProxySettings::MatchOverrideInternal(
|
||||
const nsACString& aHost, const nsACString& aOverrideRule) {
|
||||
// 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
|
||||
|
|
@ -114,26 +112,73 @@ bool nsWindowsSystemProxySettings::MatchOverride(const nsACString& aHost) {
|
|||
// 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.
|
||||
nsAutoCString host(aHost);
|
||||
if (aOverrideRule.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, aOverrideRule)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool nsWindowsSystemProxySettings::MatchOverride(const nsACString& aHost) {
|
||||
nsAutoCString host(aHost);
|
||||
bool foundMatch = false;
|
||||
|
||||
auto flagHandler = [](uint32_t aFlags) { return true; };
|
||||
auto ruleHandler = [&](const nsACString& aOverrideRule, bool& aContinue) {
|
||||
foundMatch = MatchOverrideInternal(aHost, aOverrideRule);
|
||||
if (foundMatch) {
|
||||
aContinue = false;
|
||||
}
|
||||
};
|
||||
|
||||
ReadProxyRules(INTERNET_PER_CONN_PROXY_BYPASS, flagHandler, ruleHandler);
|
||||
return foundMatch;
|
||||
}
|
||||
|
||||
nsresult nsWindowsSystemProxySettings::ReadProxyRules(
|
||||
uint32_t aOptions,
|
||||
const std::function<bool(uint32_t aFlags)>& aFlagsHandler,
|
||||
const std::function<void(const nsACString& aRule, bool& aContinue)>&
|
||||
aRuleHandler) {
|
||||
uint32_t flags = 0;
|
||||
nsAutoString buf;
|
||||
|
||||
nsresult rv = ReadInternetOption(aOptions, flags, buf);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (!aFlagsHandler(flags)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_ConvertUTF16toUTF8 cbuf(buf);
|
||||
|
||||
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 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;
|
||||
const nsAutoCString rule(Substring(cbuf, start, delimiter - start));
|
||||
bool continueProcessing = false;
|
||||
aRuleHandler(rule, continueProcessing);
|
||||
if (!continueProcessing) {
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -141,7 +186,7 @@ bool nsWindowsSystemProxySettings::MatchOverride(const nsACString& aHost) {
|
|||
start = ++delimiter;
|
||||
}
|
||||
|
||||
return false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool nsWindowsSystemProxySettings::PatternMatch(const nsACString& aHost,
|
||||
|
|
@ -170,78 +215,322 @@ nsresult nsWindowsSystemProxySettings::GetProxyForURI(const nsACString& aSpec,
|
|||
const nsACString& aHost,
|
||||
const int32_t aPort,
|
||||
nsACString& aResult) {
|
||||
nsresult rv;
|
||||
uint32_t flags = 0;
|
||||
nsAutoString buf;
|
||||
auto flagHandler = [&](uint32_t aFlags) {
|
||||
if (!(aFlags & PROXY_TYPE_PROXY)) {
|
||||
ProxyConfig::SetProxyResultDirect(aResult);
|
||||
return false;
|
||||
}
|
||||
|
||||
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)) {
|
||||
ProxyConfig::SetProxyResultDirect(aResult);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (MatchOverride(aHost)) {
|
||||
SetProxyResultDirect(aResult);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_ConvertUTF16toUTF8 cbuf(buf);
|
||||
return true;
|
||||
};
|
||||
|
||||
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;
|
||||
auto ruleHandler = [&](const nsACString& aRule, bool& aContinue) {
|
||||
const nsCString proxy(aRule);
|
||||
aContinue = true;
|
||||
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());
|
||||
aContinue = false;
|
||||
} else if (proxy.Find(kSocksPrefix) == 0) {
|
||||
// SOCKS proxy.
|
||||
socksProxy = Substring(proxy, kSocksPrefix.Length()); // "socks=" length.
|
||||
}
|
||||
};
|
||||
|
||||
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());
|
||||
nsresult rv =
|
||||
ReadProxyRules(INTERNET_PER_CONN_PROXY_SERVER, flagHandler, ruleHandler);
|
||||
if (NS_FAILED(rv)) {
|
||||
ProxyConfig::SetProxyResultDirect(aResult);
|
||||
return rv;
|
||||
}
|
||||
|
||||
ProxyConfig::ProxyStrToResult(specificProxy, defaultProxy, socksProxy,
|
||||
aResult);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
class WindowsSystemProxySettingsAsync final
|
||||
: public nsWindowsSystemProxySettings,
|
||||
public nsIObserver {
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_NSISYSTEMPROXYSETTINGS
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
WindowsSystemProxySettingsAsync();
|
||||
nsresult Init() override;
|
||||
|
||||
private:
|
||||
virtual ~WindowsSystemProxySettingsAsync();
|
||||
void ThreadFunc();
|
||||
void OnProxyConfigChangedInternal();
|
||||
|
||||
ProxyConfig mConfig;
|
||||
nsCOMPtr<nsIThread> mBackgroundThread;
|
||||
mozilla::Mutex mLock{"WindowsSystemProxySettingsAsync"};
|
||||
mozilla::Atomic<bool> mInited{false};
|
||||
mozilla::Atomic<bool> mTerminated{false};
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED(WindowsSystemProxySettingsAsync,
|
||||
nsWindowsSystemProxySettings, nsIObserver);
|
||||
|
||||
WindowsSystemProxySettingsAsync::WindowsSystemProxySettingsAsync() = default;
|
||||
|
||||
WindowsSystemProxySettingsAsync::~WindowsSystemProxySettingsAsync() = default;
|
||||
|
||||
nsresult WindowsSystemProxySettingsAsync::Init() {
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
if (!observerService) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
observerService->AddObserver(this, "xpcom-shutdown-threads", false);
|
||||
|
||||
nsCOMPtr<nsIThread> thread;
|
||||
if (NS_FAILED(NS_NewNamedThread("System Proxy", getter_AddRefs(thread)))) {
|
||||
NS_WARNING("NS_NewNamedThread failed!");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mBackgroundThread = std::move(thread);
|
||||
|
||||
nsCOMPtr<nsIRunnable> event = mozilla::NewRunnableMethod(
|
||||
"WindowsSystemProxySettingsAsync::ThreadFunc", this,
|
||||
&WindowsSystemProxySettingsAsync::ThreadFunc);
|
||||
return mBackgroundThread->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WindowsSystemProxySettingsAsync::Observe(nsISupports* aSubject,
|
||||
const char* aTopic,
|
||||
const char16_t* aData) {
|
||||
if (!strcmp(aTopic, "xpcom-shutdown-threads")) {
|
||||
if (mBackgroundThread) {
|
||||
mTerminated = true;
|
||||
nsCOMPtr<nsIThread> thread;
|
||||
{
|
||||
mozilla::MutexAutoLock lock(mLock);
|
||||
thread = mBackgroundThread.get();
|
||||
mBackgroundThread = nullptr;
|
||||
}
|
||||
MOZ_ALWAYS_SUCCEEDS(thread->Shutdown());
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void WindowsSystemProxySettingsAsync::ThreadFunc() {
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIWindowsRegKey> regKey =
|
||||
do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
}
|
||||
|
||||
rv = regKey->Open(
|
||||
nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
|
||||
u"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"_ns,
|
||||
nsIWindowsRegKey::ACCESS_READ);
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
}
|
||||
|
||||
OnProxyConfigChangedInternal();
|
||||
|
||||
rv = regKey->StartWatching(true);
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
}
|
||||
|
||||
mInited = true;
|
||||
|
||||
while (!mTerminated) {
|
||||
bool changed = false;
|
||||
regKey->HasChanged(&changed);
|
||||
if (changed) {
|
||||
OnProxyConfigChangedInternal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WindowsSystemProxySettingsAsync::OnProxyConfigChangedInternal() {
|
||||
ProxyConfig config;
|
||||
|
||||
// PAC
|
||||
nsAutoCString pacUrl;
|
||||
if (NS_SUCCEEDED(GetPACURI(pacUrl))) {
|
||||
config.SetPACUrl(pacUrl);
|
||||
}
|
||||
|
||||
// proxies
|
||||
auto flagHandler = [&](uint32_t aFlags) {
|
||||
if (!(aFlags & PROXY_TYPE_PROXY)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
// The format of input string is like: scheme=host:port, e.g.
|
||||
// "http=127.0.0.1:3128".
|
||||
auto processProxyStr = [](const nsCString& aInput,
|
||||
ProxyServer::ProxyType& aOutType,
|
||||
nsACString& aOutHost, int32_t& aOutPort) {
|
||||
aOutType = ProxyServer::ProxyType::DEFAULT;
|
||||
aOutHost = EmptyCString();
|
||||
aOutPort = -1;
|
||||
|
||||
mozilla::Tokenizer t(aInput);
|
||||
mozilla::Tokenizer::Token token;
|
||||
// skip over spaces
|
||||
t.SkipWhites();
|
||||
t.Record();
|
||||
|
||||
bool parsingIPv6 = false;
|
||||
bool parsingPort = false;
|
||||
while (t.Next(token)) {
|
||||
if (token.Equals(mozilla::Tokenizer::Token::EndOfFile())) {
|
||||
if (aOutHost.IsEmpty()) {
|
||||
t.Claim(aOutHost);
|
||||
}
|
||||
break;
|
||||
} else if (proxy.Find(kSocksPrefix) == 0) {
|
||||
// SOCKS proxy.
|
||||
socksProxy =
|
||||
Substring(proxy, kSocksPrefix.Length()); // "socks=" length.
|
||||
}
|
||||
|
||||
if (token.Equals(mozilla::Tokenizer::Token::Char('='))) {
|
||||
nsAutoCString typeStr;
|
||||
t.Claim(typeStr);
|
||||
aOutType = ProxyConfig::ToProxyType(typeStr.get());
|
||||
t.Record();
|
||||
}
|
||||
|
||||
if (token.Equals(mozilla::Tokenizer::Token::Char('['))) {
|
||||
parsingIPv6 = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!parsingIPv6 && token.Equals(mozilla::Tokenizer::Token::Char(':'))) {
|
||||
// Port is starting. Claim the previous as host.
|
||||
t.Claim(aOutHost);
|
||||
t.Record();
|
||||
parsingPort = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (token.Equals(mozilla::Tokenizer::Token::Char(']'))) {
|
||||
parsingIPv6 = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (delimiter == end) break;
|
||||
start = ++delimiter;
|
||||
if (parsingPort) {
|
||||
nsAutoCString portStr;
|
||||
t.Claim(portStr);
|
||||
nsresult rv = NS_OK;
|
||||
aOutPort = portStr.ToInteger(&rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
aOutPort = -1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
auto ruleHandler = [&](const nsACString& aRule, bool& aContinue) {
|
||||
const nsCString proxy(aRule);
|
||||
aContinue = true;
|
||||
ProxyServer::ProxyType type;
|
||||
nsCString host;
|
||||
int32_t port = -1;
|
||||
processProxyStr(proxy, type, host, port);
|
||||
if (!host.IsEmpty()) {
|
||||
ProxyServer server(type, host, port);
|
||||
config.Rules().mProxyServers[server.Type()] = std::move(server);
|
||||
}
|
||||
};
|
||||
|
||||
// Note that reading the proxy settings from registry directly is not
|
||||
// documented by Microsoft, so doing it could be risky. We still use system
|
||||
// API to read proxy settings for safe.
|
||||
ReadProxyRules(INTERNET_PER_CONN_PROXY_SERVER, flagHandler, ruleHandler);
|
||||
|
||||
auto bypassRuleHandler = [&](const nsACString& aOverrideRule,
|
||||
bool& aContinue) {
|
||||
aContinue = true;
|
||||
config.ByPassRules().mExceptions.AppendElement(aOverrideRule);
|
||||
};
|
||||
|
||||
auto dummyHandler = [](uint32_t aFlags) { return true; };
|
||||
ReadProxyRules(INTERNET_PER_CONN_PROXY_BYPASS, dummyHandler,
|
||||
bypassRuleHandler);
|
||||
|
||||
{
|
||||
mozilla::MutexAutoLock lock(mLock);
|
||||
mConfig = std::move(config);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WindowsSystemProxySettingsAsync::GetMainThreadOnly(bool* aMainThreadOnly) {
|
||||
return nsWindowsSystemProxySettings::GetMainThreadOnly(aMainThreadOnly);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP WindowsSystemProxySettingsAsync::GetPACURI(nsACString& aResult) {
|
||||
AUTO_PROFILER_LABEL("WindowsSystemProxySettingsAsync::GetPACURI", OTHER);
|
||||
mozilla::MutexAutoLock lock(mLock);
|
||||
aResult.Assign(mConfig.PACUrl());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP WindowsSystemProxySettingsAsync::GetProxyForURI(
|
||||
const nsACString& aSpec, const nsACString& aScheme, const nsACString& aHost,
|
||||
const int32_t aPort, nsACString& aResult) {
|
||||
// Fallback to nsWindowsSystemProxySettings::GetProxyForURI if we failed to
|
||||
// monitor the change of registry keys.
|
||||
if (!mInited) {
|
||||
return nsWindowsSystemProxySettings::GetProxyForURI(aSpec, aScheme, aHost,
|
||||
aPort, aResult);
|
||||
}
|
||||
|
||||
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.
|
||||
mozilla::MutexAutoLock lock(mLock);
|
||||
|
||||
for (const auto& bypassRule : mConfig.ByPassRules().mExceptions) {
|
||||
if (MatchOverrideInternal(aHost, bypassRule)) {
|
||||
ProxyConfig::SetProxyResultDirect(aResult);
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
mConfig.GetProxyString(aScheme, aResult);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_COMPONENT_FACTORY(nsWindowsSystemProxySettings) {
|
||||
return mozilla::MakeAndAddRef<nsWindowsSystemProxySettings>()
|
||||
.downcast<nsISupports>();
|
||||
auto settings =
|
||||
mozilla::StaticPrefs::network_proxy_detect_system_proxy_changes()
|
||||
? mozilla::MakeRefPtr<WindowsSystemProxySettingsAsync>()
|
||||
: mozilla::MakeRefPtr<nsWindowsSystemProxySettings>();
|
||||
if (NS_SUCCEEDED(settings->Init())) {
|
||||
return settings.forget().downcast<nsISupports>();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue