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
|
StressRunner
|
||||||
SuicideManager
|
SuicideManager
|
||||||
SuicideThread
|
SuicideThread
|
||||||
|
System Proxy
|
||||||
TEQ AwaitIdle
|
TEQ AwaitIdle
|
||||||
TelemetryModule
|
TelemetryModule
|
||||||
Test Thread
|
Test Thread
|
||||||
|
|
|
||||||
|
|
@ -9673,6 +9673,11 @@
|
||||||
value: false
|
value: false
|
||||||
mirror: always
|
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.
|
# 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
|
# This pref controls whether such requests are put to the tail, behind other requests
|
||||||
# emerging during page loading process.
|
# 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"
|
FINAL_LIBRARY = "xul"
|
||||||
|
|
||||||
|
LOCAL_INCLUDES += [
|
||||||
|
"/netwerk/base",
|
||||||
|
]
|
||||||
|
|
|
||||||
|
|
@ -6,30 +6,52 @@
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <ras.h>
|
#include <ras.h>
|
||||||
#include <wininet.h>
|
#include <wininet.h>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
#include "mozilla/ArrayUtils.h"
|
#include "mozilla/ArrayUtils.h"
|
||||||
#include "mozilla/Attributes.h"
|
#include "mozilla/Attributes.h"
|
||||||
|
#include "mozilla/Atomics.h"
|
||||||
#include "nsISystemProxySettings.h"
|
#include "nsISystemProxySettings.h"
|
||||||
#include "mozilla/Components.h"
|
#include "mozilla/Components.h"
|
||||||
|
#include "mozilla/Mutex.h"
|
||||||
|
#include "mozilla/Services.h"
|
||||||
#include "mozilla/ProfilerLabels.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 "nsPrintfCString.h"
|
||||||
#include "nsNetCID.h"
|
#include "nsNetCID.h"
|
||||||
#include "nsThreadUtils.h"
|
#include "nsThreadUtils.h"
|
||||||
#include "prnetdb.h"
|
#include "prnetdb.h"
|
||||||
#include "ProxyUtils.h"
|
#include "ProxyUtils.h"
|
||||||
|
#include "ProxyConfig.h"
|
||||||
|
|
||||||
class nsWindowsSystemProxySettings final : public nsISystemProxySettings {
|
using namespace mozilla::net;
|
||||||
|
|
||||||
|
class nsWindowsSystemProxySettings : public nsISystemProxySettings {
|
||||||
public:
|
public:
|
||||||
NS_DECL_THREADSAFE_ISUPPORTS
|
NS_DECL_THREADSAFE_ISUPPORTS
|
||||||
NS_DECL_NSISYSTEMPROXYSETTINGS
|
NS_DECL_NSISYSTEMPROXYSETTINGS
|
||||||
|
|
||||||
nsWindowsSystemProxySettings(){};
|
nsWindowsSystemProxySettings(){};
|
||||||
|
virtual nsresult Init() { return NS_OK; }
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
~nsWindowsSystemProxySettings(){};
|
virtual ~nsWindowsSystemProxySettings() = default;
|
||||||
|
|
||||||
bool MatchOverride(const nsACString& aHost);
|
bool MatchOverride(const nsACString& aHost);
|
||||||
|
bool MatchOverrideInternal(const nsACString& aHost,
|
||||||
|
const nsACString& aOverrideRule);
|
||||||
bool PatternMatch(const nsACString& aHost, const nsACString& aOverride);
|
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)
|
NS_IMPL_ISUPPORTS(nsWindowsSystemProxySettings, nsISystemProxySettings)
|
||||||
|
|
@ -43,18 +65,6 @@ nsWindowsSystemProxySettings::GetMainThreadOnly(bool* aMainThreadOnly) {
|
||||||
return NS_OK;
|
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,
|
static nsresult ReadInternetOption(uint32_t aOption, uint32_t& aFlags,
|
||||||
nsAString& aValue) {
|
nsAString& aValue) {
|
||||||
// Bug 1366133: InternetGetConnectedStateExW() may cause hangs
|
// Bug 1366133: InternetGetConnectedStateExW() may cause hangs
|
||||||
|
|
@ -93,20 +103,8 @@ static nsresult ReadInternetOption(uint32_t aOption, uint32_t& aFlags,
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool nsWindowsSystemProxySettings::MatchOverride(const nsACString& aHost) {
|
bool nsWindowsSystemProxySettings::MatchOverrideInternal(
|
||||||
nsresult rv;
|
const nsACString& aHost, const nsACString& aOverrideRule) {
|
||||||
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:
|
// Windows formats its proxy override list in the form:
|
||||||
// server;server;server where 'server' is a server name pattern or IP
|
// server;server;server where 'server' is a server name pattern or IP
|
||||||
// address, or "<local>". "<local>" must be translated to
|
// 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
|
// In a server name pattern, a '*' character matches any substring and
|
||||||
// all other characters must match themselves; the whole pattern must match
|
// all other characters must match themselves; the whole pattern must match
|
||||||
// the whole hostname.
|
// 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) {
|
while (true) {
|
||||||
int32_t delimiter = cbuf.FindCharInSet(" ;", start);
|
int32_t delimiter = cbuf.FindCharInSet(" ;", start);
|
||||||
if (delimiter == -1) delimiter = end;
|
if (delimiter == -1) delimiter = end;
|
||||||
|
|
||||||
if (delimiter != start) {
|
if (delimiter != start) {
|
||||||
const nsAutoCString override(Substring(cbuf, start, delimiter - start));
|
const nsAutoCString rule(Substring(cbuf, start, delimiter - start));
|
||||||
if (override.EqualsLiteral("<local>")) {
|
bool continueProcessing = false;
|
||||||
PRNetAddr addr;
|
aRuleHandler(rule, continueProcessing);
|
||||||
bool isIpAddr = (PR_StringToNetAddr(host.get(), &addr) == PR_SUCCESS);
|
if (!continueProcessing) {
|
||||||
|
return NS_OK;
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -141,7 +186,7 @@ bool nsWindowsSystemProxySettings::MatchOverride(const nsACString& aHost) {
|
||||||
start = ++delimiter;
|
start = ++delimiter;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool nsWindowsSystemProxySettings::PatternMatch(const nsACString& aHost,
|
bool nsWindowsSystemProxySettings::PatternMatch(const nsACString& aHost,
|
||||||
|
|
@ -170,78 +215,322 @@ nsresult nsWindowsSystemProxySettings::GetProxyForURI(const nsACString& aSpec,
|
||||||
const nsACString& aHost,
|
const nsACString& aHost,
|
||||||
const int32_t aPort,
|
const int32_t aPort,
|
||||||
nsACString& aResult) {
|
nsACString& aResult) {
|
||||||
nsresult rv;
|
auto flagHandler = [&](uint32_t aFlags) {
|
||||||
uint32_t flags = 0;
|
if (!(aFlags & PROXY_TYPE_PROXY)) {
|
||||||
nsAutoString buf;
|
ProxyConfig::SetProxyResultDirect(aResult);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
rv = ReadInternetOption(INTERNET_PER_CONN_PROXY_SERVER, flags, buf);
|
if (MatchOverride(aHost)) {
|
||||||
if (NS_FAILED(rv) || !(flags & PROXY_TYPE_PROXY)) {
|
ProxyConfig::SetProxyResultDirect(aResult);
|
||||||
SetProxyResultDirect(aResult);
|
return false;
|
||||||
return NS_OK;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (MatchOverride(aHost)) {
|
return true;
|
||||||
SetProxyResultDirect(aResult);
|
};
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_ConvertUTF16toUTF8 cbuf(buf);
|
|
||||||
|
|
||||||
constexpr auto kSocksPrefix = "socks="_ns;
|
constexpr auto kSocksPrefix = "socks="_ns;
|
||||||
nsAutoCString prefix;
|
nsAutoCString prefix;
|
||||||
ToLowerCase(aScheme, prefix);
|
ToLowerCase(aScheme, prefix);
|
||||||
|
|
||||||
prefix.Append('=');
|
prefix.Append('=');
|
||||||
|
|
||||||
nsAutoCString specificProxy;
|
nsAutoCString specificProxy;
|
||||||
nsAutoCString defaultProxy;
|
nsAutoCString defaultProxy;
|
||||||
nsAutoCString socksProxy;
|
nsAutoCString socksProxy;
|
||||||
int32_t start = 0;
|
|
||||||
int32_t end = cbuf.Length();
|
|
||||||
|
|
||||||
while (true) {
|
auto ruleHandler = [&](const nsACString& aRule, bool& aContinue) {
|
||||||
int32_t delimiter = cbuf.FindCharInSet(" ;", start);
|
const nsCString proxy(aRule);
|
||||||
if (delimiter == -1) delimiter = end;
|
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) {
|
nsresult rv =
|
||||||
const nsAutoCString proxy(Substring(cbuf, start, delimiter - start));
|
ReadProxyRules(INTERNET_PER_CONN_PROXY_SERVER, flagHandler, ruleHandler);
|
||||||
if (proxy.FindChar('=') == -1) {
|
if (NS_FAILED(rv)) {
|
||||||
// If a proxy name is listed by itself, it is used as the
|
ProxyConfig::SetProxyResultDirect(aResult);
|
||||||
// default proxy for any protocols that do not have a specific
|
return rv;
|
||||||
// proxy specified.
|
}
|
||||||
// (http://msdn.microsoft.com/en-us/library/aa383996%28VS.85%29.aspx)
|
|
||||||
defaultProxy = proxy;
|
ProxyConfig::ProxyStrToResult(specificProxy, defaultProxy, socksProxy,
|
||||||
} else if (proxy.Find(prefix) == 0) {
|
aResult);
|
||||||
// To list a proxy for a specific protocol, the string must
|
return NS_OK;
|
||||||
// follow the format "<protocol>=<protocol>://<proxy_name>".
|
}
|
||||||
// (http://msdn.microsoft.com/en-us/library/aa383996%28VS.85%29.aspx)
|
|
||||||
specificProxy = Substring(proxy, prefix.Length());
|
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;
|
break;
|
||||||
} else if (proxy.Find(kSocksPrefix) == 0) {
|
}
|
||||||
// SOCKS proxy.
|
|
||||||
socksProxy =
|
if (token.Equals(mozilla::Tokenizer::Token::Char('='))) {
|
||||||
Substring(proxy, kSocksPrefix.Length()); // "socks=" length.
|
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;
|
if (parsingPort) {
|
||||||
start = ++delimiter;
|
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())
|
mozilla::MutexAutoLock lock(mLock);
|
||||||
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.
|
|
||||||
|
|
||||||
|
for (const auto& bypassRule : mConfig.ByPassRules().mExceptions) {
|
||||||
|
if (MatchOverrideInternal(aHost, bypassRule)) {
|
||||||
|
ProxyConfig::SetProxyResultDirect(aResult);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mConfig.GetProxyString(aScheme, aResult);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMPL_COMPONENT_FACTORY(nsWindowsSystemProxySettings) {
|
NS_IMPL_COMPONENT_FACTORY(nsWindowsSystemProxySettings) {
|
||||||
return mozilla::MakeAndAddRef<nsWindowsSystemProxySettings>()
|
auto settings =
|
||||||
.downcast<nsISupports>();
|
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