toolkit/system/windowsproxy/nsWindowsSystemProxySettings.cpp
author Mike Hommey <mh+mozilla@glandium.org>
Fri, 05 Apr 2019 02:30:56 +0000
changeset 468491 60669a841a87644ba9b2f4d9f17f225e2eca0980
parent 449078 66eb1f485c1a3ea81372758bc92292c9428b17cd
child 469201 c8ad4f9c261d8b5a3c5253b168fc3ab10b199dfc
permissions -rw-r--r--
Bug 1541792 - Replace linker magic with manual component registration. r=froydnj Before bug 938437, we had a rather large and error-prone nsStaticXULComponents.cpp used to register all modules. That was replaced with clever use of the linker, which allowed to avoid the mess that maintaining that file was. Fast forward to now, where after bug 1524687 and other work that preceded it, we have a much smaller number of remaining static xpcom components, registered via this linker hack, and don't expect to add any new ones. The list should eventually go down to zero. Within that context, it seems to be the right time to get rid of the magic, and with it the problems it causes on its own. Some of those components could probably be trivially be converted to static registration via .conf files, but I didn't want to deal with the possible need to increase the number of dummy modules in XPCOMInit.cpp. They can still be converted as a followup. Differential Revision: https://phabricator.services.mozilla.com/D26076

/* -*- 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 "nsIServiceManager.h"
#include "mozilla/ModuleUtils.h"
#include "nsPrintfCString.h"
#include "nsNetCID.h"
#include "nsISupportsPrimitives.h"
#include "nsIURI.h"
#include "nsThreadUtils.h"
#include "GeckoProfiler.h"
#include "prnetdb.h"
#include "ProxyUtils.h"

class nsWindowsSystemProxySettings final : public nsISystemProxySettings {
 public:
  NS_DECL_THREADSAFE_ISUPPORTS
  NS_DECL_NSISYSTEMPROXYSETTINGS

  nsWindowsSystemProxySettings(){};
  nsresult Init();

 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;
}

nsresult nsWindowsSystemProxySettings::Init() { 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);

  NS_NAMED_LITERAL_CSTRING(kSocksPrefix, "socks=");
  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.
        // (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());
        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;
}

/* 4e22d3ea-aaa2-436e-ada4-9247de57d367 */
#define NS_WINDOWSSYSTEMPROXYSERVICE_CID             \
  {                                                  \
    0x4e22d3ea, 0xaaa2, 0x436e, {                    \
      0xad, 0xa4, 0x92, 0x47, 0xde, 0x57, 0xd3, 0x67 \
    }                                                \
  }

NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsWindowsSystemProxySettings, Init)
NS_DEFINE_NAMED_CID(NS_WINDOWSSYSTEMPROXYSERVICE_CID);

static const mozilla::Module::CIDEntry kSysProxyCIDs[] = {
    {&kNS_WINDOWSSYSTEMPROXYSERVICE_CID, false, nullptr,
     nsWindowsSystemProxySettingsConstructor},
    {nullptr}};

static const mozilla::Module::ContractIDEntry kSysProxyContracts[] = {
    {NS_SYSTEMPROXYSETTINGS_CONTRACTID, &kNS_WINDOWSSYSTEMPROXYSERVICE_CID},
    {nullptr}};

extern const mozilla::Module kSysProxyModule = {
    mozilla::Module::kVersion, kSysProxyCIDs, kSysProxyContracts};