Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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 "mozilla/ProfilerLabels.h"
#include "nsPrintfCString.h"
#include "nsNetCID.h"
#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);
};
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.
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>".
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_IMETHODIMP nsWindowsSystemProxySettings::GetSystemWPADSetting(
bool* aSystemWPADSetting) {
nsresult rv;
uint32_t flags = 0;
nsAutoString buf;
rv = ReadInternetOption(INTERNET_PER_CONN_AUTOCONFIG_URL, flags, buf);
*aSystemWPADSetting =
(flags & (PROXY_TYPE_AUTO_PROXY_URL | PROXY_TYPE_AUTO_DETECT)) ==
PROXY_TYPE_AUTO_DETECT;
return rv;
}
NS_IMPL_COMPONENT_FACTORY(nsWindowsSystemProxySettings) {
return mozilla::MakeAndAddRef<nsWindowsSystemProxySettings>()
.downcast<nsISupports>();
}