michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "mozilla/ArrayUtils.h" michael@0: #include "mozilla/Attributes.h" michael@0: #include "nsISystemProxySettings.h" michael@0: #include "nsIServiceManager.h" michael@0: #include "mozilla/ModuleUtils.h" michael@0: #include "nsPrintfCString.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsISupportsPrimitives.h" michael@0: #include "nsIURI.h" michael@0: michael@0: class nsWindowsSystemProxySettings MOZ_FINAL : public nsISystemProxySettings michael@0: { michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: NS_DECL_NSISYSTEMPROXYSETTINGS michael@0: michael@0: nsWindowsSystemProxySettings() {}; michael@0: nsresult Init(); michael@0: michael@0: private: michael@0: ~nsWindowsSystemProxySettings() {}; michael@0: michael@0: bool MatchOverride(const nsACString& aHost); michael@0: bool PatternMatch(const nsACString& aHost, const nsACString& aOverride); michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(nsWindowsSystemProxySettings, nsISystemProxySettings) michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindowsSystemProxySettings::GetMainThreadOnly(bool *aMainThreadOnly) michael@0: { michael@0: *aMainThreadOnly = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: nsWindowsSystemProxySettings::Init() michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: static void SetProxyResult(const char* aType, const nsACString& aHostPort, michael@0: nsACString& aResult) michael@0: { michael@0: aResult.AssignASCII(aType); michael@0: aResult.Append(' '); michael@0: aResult.Append(aHostPort); michael@0: } michael@0: michael@0: static void SetProxyResultDirect(nsACString& aResult) michael@0: { michael@0: // For whatever reason, a proxy is not to be used. michael@0: aResult.AssignASCII("DIRECT"); michael@0: } michael@0: michael@0: static nsresult ReadInternetOption(uint32_t aOption, uint32_t& aFlags, michael@0: nsAString& aValue) michael@0: { michael@0: DWORD connFlags = 0; michael@0: WCHAR connName[RAS_MaxEntryName + 1]; michael@0: MOZ_SEH_TRY { michael@0: InternetGetConnectedStateExW(&connFlags, connName, michael@0: mozilla::ArrayLength(connName), 0); michael@0: } MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: INTERNET_PER_CONN_OPTIONW options[2]; michael@0: options[0].dwOption = INTERNET_PER_CONN_FLAGS_UI; michael@0: options[1].dwOption = aOption; michael@0: michael@0: INTERNET_PER_CONN_OPTION_LISTW list; michael@0: list.dwSize = sizeof(INTERNET_PER_CONN_OPTION_LISTW); michael@0: list.pszConnection = connFlags & INTERNET_CONNECTION_MODEM ? michael@0: connName : nullptr; michael@0: list.dwOptionCount = mozilla::ArrayLength(options); michael@0: list.dwOptionError = 0; michael@0: list.pOptions = options; michael@0: michael@0: unsigned long size = sizeof(INTERNET_PER_CONN_OPTION_LISTW); michael@0: if (!InternetQueryOptionW(nullptr, INTERNET_OPTION_PER_CONNECTION_OPTION, michael@0: &list, &size)) { michael@0: if (GetLastError() != ERROR_INVALID_PARAMETER) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: options[0].dwOption = INTERNET_PER_CONN_FLAGS; michael@0: size = sizeof(INTERNET_PER_CONN_OPTION_LISTW); michael@0: MOZ_SEH_TRY { michael@0: if (!InternetQueryOptionW(nullptr, michael@0: INTERNET_OPTION_PER_CONNECTION_OPTION, michael@0: &list, &size)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } michael@0: michael@0: aFlags = options[0].Value.dwValue; michael@0: aValue.Assign(options[1].Value.pszValue); michael@0: GlobalFree(options[1].Value.pszValue); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: nsWindowsSystemProxySettings::MatchOverride(const nsACString& aHost) michael@0: { michael@0: nsresult rv; michael@0: uint32_t flags = 0; michael@0: nsAutoString buf; michael@0: michael@0: rv = ReadInternetOption(INTERNET_PER_CONN_PROXY_BYPASS, flags, buf); michael@0: if (NS_FAILED(rv)) michael@0: return false; michael@0: michael@0: NS_ConvertUTF16toUTF8 cbuf(buf); michael@0: michael@0: nsAutoCString host(aHost); michael@0: int32_t start = 0; michael@0: int32_t end = cbuf.Length(); michael@0: michael@0: // Windows formats its proxy override list in the form: michael@0: // server;server;server where 'server' is a server name pattern or IP michael@0: // address, or "". "" must be translated to michael@0: // "localhost;127.0.0.1". michael@0: // In a server name pattern, a '*' character matches any substring and michael@0: // all other characters must match themselves; the whole pattern must match michael@0: // the whole hostname. michael@0: while (true) { michael@0: int32_t delimiter = cbuf.FindCharInSet(" ;", start); michael@0: if (delimiter == -1) michael@0: delimiter = end; michael@0: michael@0: if (delimiter != start) { michael@0: const nsAutoCString override(Substring(cbuf, start, michael@0: delimiter - start)); michael@0: if (override.EqualsLiteral("")) { michael@0: // This override matches local addresses. michael@0: if (host.EqualsLiteral("localhost") || michael@0: host.EqualsLiteral("127.0.0.1")) michael@0: return true; michael@0: } else if (PatternMatch(host, override)) { michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: if (delimiter == end) michael@0: break; michael@0: start = ++delimiter; michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: nsWindowsSystemProxySettings::PatternMatch(const nsACString& aHost, michael@0: const nsACString& aOverride) michael@0: { michael@0: nsAutoCString host(aHost); michael@0: nsAutoCString override(aOverride); michael@0: int32_t overrideLength = override.Length(); michael@0: int32_t tokenStart = 0; michael@0: int32_t offset = 0; michael@0: bool star = false; michael@0: michael@0: while (tokenStart < overrideLength) { michael@0: int32_t tokenEnd = override.FindChar('*', tokenStart); michael@0: if (tokenEnd == tokenStart) { michael@0: star = true; michael@0: tokenStart++; michael@0: // If the character following the '*' is a '.' character then skip michael@0: // it so that "*.foo.com" allows "foo.com". michael@0: if (override.FindChar('.', tokenStart) == tokenStart) michael@0: tokenStart++; michael@0: } else { michael@0: if (tokenEnd == -1) michael@0: tokenEnd = overrideLength; michael@0: nsAutoCString token(Substring(override, tokenStart, michael@0: tokenEnd - tokenStart)); michael@0: offset = host.Find(token, offset); michael@0: if (offset == -1 || (!star && offset)) michael@0: return false; michael@0: star = false; michael@0: tokenStart = tokenEnd; michael@0: offset += token.Length(); michael@0: } michael@0: } michael@0: michael@0: return (star || (offset == host.Length())); michael@0: } michael@0: michael@0: nsresult michael@0: nsWindowsSystemProxySettings::GetPACURI(nsACString& aResult) michael@0: { michael@0: nsresult rv; michael@0: uint32_t flags = 0; michael@0: nsAutoString buf; michael@0: michael@0: rv = ReadInternetOption(INTERNET_PER_CONN_AUTOCONFIG_URL, flags, buf); michael@0: if (!(flags & PROXY_TYPE_AUTO_PROXY_URL)) { michael@0: aResult.Truncate(); michael@0: return rv; michael@0: } michael@0: michael@0: if (NS_SUCCEEDED(rv)) michael@0: aResult = NS_ConvertUTF16toUTF8(buf); michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsWindowsSystemProxySettings::GetProxyForURI(const nsACString & aSpec, michael@0: const nsACString & aScheme, michael@0: const nsACString & aHost, michael@0: const int32_t aPort, michael@0: nsACString & aResult) michael@0: { michael@0: nsresult rv; michael@0: uint32_t flags = 0; michael@0: nsAutoString buf; michael@0: michael@0: rv = ReadInternetOption(INTERNET_PER_CONN_PROXY_SERVER, flags, buf); michael@0: if (NS_FAILED(rv) || !(flags & PROXY_TYPE_PROXY)) { michael@0: SetProxyResultDirect(aResult); michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (MatchOverride(aHost)) { michael@0: SetProxyResultDirect(aResult); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_ConvertUTF16toUTF8 cbuf(buf); michael@0: michael@0: nsAutoCString prefix; michael@0: ToLowerCase(aScheme, prefix); michael@0: michael@0: prefix.Append('='); michael@0: michael@0: nsAutoCString specificProxy; michael@0: nsAutoCString defaultProxy; michael@0: nsAutoCString socksProxy; michael@0: int32_t start = 0; michael@0: int32_t end = cbuf.Length(); michael@0: michael@0: while (true) { michael@0: int32_t delimiter = cbuf.FindCharInSet(" ;", start); michael@0: if (delimiter == -1) michael@0: delimiter = end; michael@0: michael@0: if (delimiter != start) { michael@0: const nsAutoCString proxy(Substring(cbuf, start, michael@0: delimiter - start)); michael@0: if (proxy.FindChar('=') == -1) { michael@0: // If a proxy name is listed by itself, it is used as the michael@0: // default proxy for any protocols that do not have a specific michael@0: // proxy specified. michael@0: // (http://msdn.microsoft.com/en-us/library/aa383996%28VS.85%29.aspx) michael@0: defaultProxy = proxy; michael@0: } else if (proxy.Find(prefix) == 0) { michael@0: // To list a proxy for a specific protocol, the string must michael@0: // follow the format "=://". michael@0: // (http://msdn.microsoft.com/en-us/library/aa383996%28VS.85%29.aspx) michael@0: specificProxy = Substring(proxy, prefix.Length()); michael@0: break; michael@0: } else if (proxy.Find("socks=") == 0) { michael@0: // SOCKS proxy. michael@0: socksProxy = Substring(proxy, 5); // "socks=" length. michael@0: } michael@0: } michael@0: michael@0: if (delimiter == end) michael@0: break; michael@0: start = ++delimiter; michael@0: } michael@0: michael@0: if (!specificProxy.IsEmpty()) michael@0: SetProxyResult("PROXY", specificProxy, aResult); // Protocol-specific proxy. michael@0: else if (!defaultProxy.IsEmpty()) michael@0: SetProxyResult("PROXY", defaultProxy, aResult); // Default proxy. michael@0: else if (!socksProxy.IsEmpty()) michael@0: SetProxyResult("SOCKS", socksProxy, aResult); // SOCKS proxy. michael@0: else michael@0: SetProxyResultDirect(aResult); // Direct connection. michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: #define NS_WINDOWSSYSTEMPROXYSERVICE_CID /* 4e22d3ea-aaa2-436e-ada4-9247de57d367 */\ michael@0: { 0x4e22d3ea, 0xaaa2, 0x436e, \ michael@0: {0xad, 0xa4, 0x92, 0x47, 0xde, 0x57, 0xd3, 0x67 } } michael@0: michael@0: NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsWindowsSystemProxySettings, Init) michael@0: NS_DEFINE_NAMED_CID(NS_WINDOWSSYSTEMPROXYSERVICE_CID); michael@0: michael@0: static const mozilla::Module::CIDEntry kSysProxyCIDs[] = { michael@0: { &kNS_WINDOWSSYSTEMPROXYSERVICE_CID, false, nullptr, nsWindowsSystemProxySettingsConstructor }, michael@0: { nullptr } michael@0: }; michael@0: michael@0: static const mozilla::Module::ContractIDEntry kSysProxyContracts[] = { michael@0: { NS_SYSTEMPROXYSETTINGS_CONTRACTID, &kNS_WINDOWSSYSTEMPROXYSERVICE_CID }, michael@0: { nullptr } michael@0: }; michael@0: michael@0: static const mozilla::Module kSysProxyModule = { michael@0: mozilla::Module::kVersion, michael@0: kSysProxyCIDs, michael@0: kSysProxyContracts michael@0: }; michael@0: michael@0: NSMODULE_DEFN(nsWindowsProxyModule) = &kSysProxyModule;