Bug 356831 - Proxy autodiscovery doesn't check DHCP (option 252) r=bagder,valentin
authorPolly Shaw <polly.shaw@gmail.com>
Thu, 07 Jun 2018 23:07:28 +0100
changeset 426779 3e47ebfc2f424bb13d55f15cef17c94a82e774a3
parent 426778 057ccf1cc3fc667350619a2bc4e6d656c9050432
child 426780 085439248886587f2251cb950de08bbbf0b70f6d
push id34285
push userbtara@mozilla.com
push dateMon, 16 Jul 2018 21:58:41 +0000
treeherdermozilla-central@f6df6a982ee9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbagder, valentin
bugs356831
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 356831 - Proxy autodiscovery doesn't check DHCP (option 252) r=bagder,valentin This patch addresses an issue with Firefox's proxy detection on networks which do not have their a proxy auto-configuration (PAC) file hosted at http://wpad/wpad.dat, and instead make use of DHCP option 252 for broadcasting the address of the PAC file. See https://findproxyforurl.com/wpad-introduction/ for an introduction to the protocol. Prior to this patch, proxy auto-detect missed out the DHCP query stage, and just looked for a PAC file at http://wpad/wpad.dat This patch only addresses the issue for Firefox on Windows, although it defines a DHCP client interface which could be implemented on other platforms. The high-level components of this patch are: * nsIDHCPClient.idl - this is an interface which has been defined for querying the DHCP server. * nsPACMan.cpp - where previously when the PAC URL was simply set to a constant of http://wpad/wpad.dat, it now dispatches an asynchronous command to the proxy thread. The class ExecutePACThreadAction has been augmented to include an instruction to 'ConfigureWPAD' (Configure Web-proxy auto-detect), and a new class, 'ConfigureWPADComplete' has been created to relay the result (the URL of the PAC file) back to the nsPACMan object. * nsProtocolProxyService.cpp Minor changes to reflect the fact that the PAC URL not being set does not always mean there is no PAC to be used; instead it could be in the process of being detected. * TestPACMan.cpp This is a new file, and tests only the DHCP auto-detect functionality. Some tests use multiple threads, as they test the non-blocking proxy detection. * DHCPUtils.cpp A class containing the main logic for querying DHCP. * WindowsNetworkFunctionsWrapper.cpp A very thin wrapper around the Windows API calls needed by DHCPUtils. This class was introduced so it could be mocked out in tests. * nsWindowsDHCPClient.cpp * An implementation of the interface defined in nsIDHCPClient.idl. Fairly thin: most logic is implemented in DHCPUtils. * TestDHCPUtils.cpp Tests for DHCPUtils and nsWindowsDHCPClient MozReview-Commit-ID: 4xFQz3tOLEx
modules/libpref/init/all.js
netwerk/base/ProxyAutoConfig.cpp
netwerk/base/moz.build
netwerk/base/nsIDHCPClient.idl
netwerk/base/nsPACMan.cpp
netwerk/base/nsPACMan.h
netwerk/base/nsProtocolProxyService.cpp
netwerk/base/nsProtocolProxyService.h
netwerk/build/nsNetCID.h
netwerk/test/gtest/TestPACMan.cpp
netwerk/test/gtest/moz.build
toolkit/moz.build
toolkit/system/windowsDHCPClient/DHCPUtils.cpp
toolkit/system/windowsDHCPClient/DHCPUtils.h
toolkit/system/windowsDHCPClient/WindowsNetworkFunctionsWrapper.cpp
toolkit/system/windowsDHCPClient/WindowsNetworkFunctionsWrapper.h
toolkit/system/windowsDHCPClient/moz.build
toolkit/system/windowsDHCPClient/nsWindowsDHCPClient.cpp
toolkit/system/windowsDHCPClient/nsWindowsDHCPClient.h
toolkit/system/windowsDHCPClient/tests/gtest/TestDHCPUtils.cpp
toolkit/system/windowsDHCPClient/tests/gtest/moz.build
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -2229,16 +2229,17 @@ pref("network.cookie.maxPerHost", 180);
 pref("network.proxy.autoconfig_url", "");
 // Strip off paths when sending URLs to PAC scripts
 pref("network.proxy.autoconfig_url.include_path", false);
 
 // If we cannot load the PAC file, then try again (doubling from interval_min
 // until we reach interval_max or the PAC file is successfully loaded).
 pref("network.proxy.autoconfig_retry_interval_min", 5);    // 5 seconds
 pref("network.proxy.autoconfig_retry_interval_max", 300);  // 5 minutes
+pref("network.proxy.enable_wpad_over_dhcp", true);
 
 // Use the HSTS preload list by default
 pref("network.stricttransportsecurity.preloadlist", true);
 
 // Use JS mDNS as a fallback
 pref("network.mdns.use_js_fallback", false);
 
 pref("converter.html2txt.structs",          true); // Output structured phrases (strong, em, code, sub, sup, b, i, u)
--- a/netwerk/base/ProxyAutoConfig.cpp
+++ b/netwerk/base/ProxyAutoConfig.cpp
@@ -1038,9 +1038,9 @@ ProxyAutoConfig::MyIPAddress(const JS::C
     return false;
   }
 
   aArgs.rval().setString(dottedDecimalString);
   return true;
 }
 
 } // namespace net
-} // namespace mozilla
+} // namespace mozilla
\ No newline at end of file
--- a/netwerk/base/moz.build
+++ b/netwerk/base/moz.build
@@ -33,16 +33,17 @@ XPIDL_SOURCES += [
     'nsIChannelWithDivertableParentListener.idl',
     'nsIChildChannel.idl',
     'nsIClassifiedChannel.idl',
     'nsIClassOfService.idl',
     'nsIContentSniffer.idl',
     'nsIDashboard.idl',
     'nsIDashboardEventNotifier.idl',
     'nsIDeprecationWarner.idl',
+    'nsIDHCPClient.idl',
     'nsIDivertableChannel.idl',
     'nsIDownloader.idl',
     'nsIEncodedChannel.idl',
     'nsIExternalProtocolHandler.idl',
     'nsIFileStreams.idl',
     'nsIFileURL.idl',
     'nsIForcePendingChannel.idl',
     'nsIFormPOSTActionChannel.idl',
new file mode 100644
--- /dev/null
+++ b/netwerk/base/nsIDHCPClient.idl
@@ -0,0 +1,19 @@
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsISupports.idl"
+
+/**
+ * This interface allows the proxy code to access the DHCP Options in a platform-specific way
+ */
+[scriptable, uuid(aee75dc0-be1a-46b9-9e0c-31a6899c175c)]
+interface nsIDHCPClient : nsISupports
+{
+
+    /**
+    * returns the DHCP Option designated by the option parameter
+    */
+    ACString getOption(in uint8_t option);
+};
--- a/netwerk/base/nsPACMan.cpp
+++ b/netwerk/base/nsPACMan.cpp
@@ -1,36 +1,41 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 "nsPACMan.h"
-#include "nsThreadUtils.h"
+
+#include "mozilla/Preferences.h"
+#include "nsContentUtils.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsIAuthPrompt.h"
-#include "nsIPromptFactory.h"
+#include "nsIDHCPClient.h"
 #include "nsIHttpChannel.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
-#include "nsNetUtil.h"
-#include "nsIAsyncVerifyRedirectCallback.h"
+#include "nsIPromptFactory.h"
+#include "nsIProtocolProxyService.h"
 #include "nsISystemProxySettings.h"
-#include "nsContentUtils.h"
-#include "mozilla/Preferences.h"
+#include "nsNetUtil.h"
+#include "nsThreadUtils.h"
 
 //-----------------------------------------------------------------------------
 
 namespace mozilla {
 namespace net {
 
 LazyLogModule gProxyLog("proxy");
 
 #undef LOG
 #define LOG(args) MOZ_LOG(gProxyLog, LogLevel::Debug, args)
+#define MOZ_WPAD_URL "http://wpad/wpad.dat"
+#define MOZ_DHCP_WPAD_OPTION 252
 
 // The PAC thread does evaluations of both PAC files and
 // nsISystemProxySettings because they can both block the calling thread and we
 // don't want that on the main thread
 
 // Check to see if the underlying request was not an error page in the case of
 // a HTTP request.  For other types of channels, just return true.
 static bool
@@ -70,16 +75,36 @@ GetExtraJSContextHeapSize()
 
       extraSize = value;
     }
   }
 
   return extraSize < 0 ? 0 : extraSize;
 }
 
+// Read network proxy type from preference
+// Used to verify that the preference is WPAD in nsPACMan::ConfigureWPAD
+nsresult
+GetNetworkProxyTypeFromPref(int32_t* type)
+{
+  *type = 0;
+  nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
+
+  if (!prefs) {
+    LOG(("Failed to get a preference service object"));
+    return NS_ERROR_FACTORY_NOT_REGISTERED;
+  }
+  nsresult rv = prefs->GetIntPref("network.proxy.type", type);
+  if (!NS_SUCCEEDED(rv)) {
+    LOG(("Failed to retrieve network.proxy.type from prefs"));
+    return rv;
+  }
+  LOG(("network.proxy.type pref retrieved: %d\n", *type));
+  return NS_OK;
+}
 
 //-----------------------------------------------------------------------------
 
 // The ExecuteCallback runnable is triggered by
 // nsPACManCallback::OnQueryComplete on the Main thread when its completion is
 // discovered on the pac thread
 
 class ExecuteCallback final : public Runnable
@@ -87,22 +112,22 @@ class ExecuteCallback final : public Run
 public:
   ExecuteCallback(nsPACManCallback* aCallback, nsresult status)
     : Runnable("net::ExecuteCallback")
     , mCallback(aCallback)
     , mStatus(status)
   {
   }
 
-  void SetPACString(const nsCString &pacString)
+  void SetPACString(const nsACString &pacString)
   {
     mPACString = pacString;
   }
 
-  void SetPACURL(const nsCString &pacURL)
+  void SetPACURL(const nsACString &pacURL)
   {
     mPACURL = pacURL;
   }
 
   NS_IMETHOD Run() override
   {
     mCallback->OnQueryComplete(mStatus, mPACString, mPACURL);
     mCallback = nullptr;
@@ -186,55 +211,90 @@ public:
   {
     MOZ_ASSERT(NS_IsMainThread(), "wrong thread");
     mPACMan->mLoader = nullptr;
     mPACMan->PostProcessPendingQ();
     return NS_OK;
   }
 
 private:
-    RefPtr<nsPACMan> mPACMan;
+  RefPtr<nsPACMan> mPACMan;
+};
+
+
+//-----------------------------------------------------------------------------
+
+// ConfigureWPADComplete allows the PAC thread to tell the main thread that
+// the URL for the PAC file has been found
+class ConfigureWPADComplete final : public Runnable
+{
+public:
+  ConfigureWPADComplete(nsPACMan *aPACMan, const nsACString &aPACURISpec)
+    : Runnable("net::ConfigureWPADComplete"),
+     mPACMan(aPACMan),
+     mPACURISpec(aPACURISpec)
+  {
+  }
+
+  NS_IMETHOD Run() override
+  {
+    MOZ_ASSERT(NS_IsMainThread(), "wrong thread");
+    mPACMan->AssignPACURISpec(mPACURISpec);
+    mPACMan->ContinueLoadingAfterPACUriKnown();
+    return NS_OK;
+  }
+
+private:
+  RefPtr<nsPACMan> mPACMan;
+  nsCString mPACURISpec;
 };
 
 //-----------------------------------------------------------------------------
 
 // ExecutePACThreadAction is used to proxy actions from the main
-// thread onto the PAC thread. There are 3 options: process the queue,
-// cancel the queue, and setup the javascript context with a new PAC file
+// thread onto the PAC thread. There are 4 options: process the queue,
+// cancel the queue, query DHCP for the PAC option
+// and setup the javascript context with a new PAC file
 
 class ExecutePACThreadAction final : public Runnable
 {
 public:
   // by default we just process the queue
   explicit ExecutePACThreadAction(nsPACMan* aPACMan)
     : Runnable("net::ExecutePACThreadAction")
     , mPACMan(aPACMan)
     , mCancel(false)
     , mCancelStatus(NS_OK)
     , mSetupPAC(false)
     , mExtraHeapSize(0)
+    , mConfigureWPAD(false)
   { }
 
   void CancelQueue (nsresult status)
   {
     mCancel = true;
     mCancelStatus = status;
   }
 
   void SetupPAC (const char *text,
                  uint32_t datalen,
-                 nsCString &pacURI,
+                 const nsACString &pacURI,
                  uint32_t extraHeapSize)
   {
     mSetupPAC = true;
     mSetupPACData.Assign(text, datalen);
     mSetupPACURI = pacURI;
     mExtraHeapSize = extraHeapSize;
   }
 
+  void ConfigureWPAD()
+  {
+    mConfigureWPAD = true;
+  }
+
   NS_IMETHOD Run() override
   {
     MOZ_ASSERT(!NS_IsMainThread(), "wrong thread");
     if (mCancel) {
       mPACMan->CancelPendingQ(mCancelStatus);
       mCancel = false;
       return NS_OK;
     }
@@ -249,30 +309,40 @@ public:
                          mExtraHeapSize,
                          target);
 
       RefPtr<PACLoadComplete> runnable = new PACLoadComplete(mPACMan);
       mPACMan->Dispatch(runnable.forget());
       return NS_OK;
     }
 
+    if (mConfigureWPAD) {
+      nsAutoCString spec;
+      mConfigureWPAD = false;
+      mPACMan->ConfigureWPAD(spec);
+      RefPtr<ConfigureWPADComplete> runnable = new ConfigureWPADComplete(mPACMan, spec);
+      mPACMan->Dispatch(runnable.forget());
+      return NS_OK;
+    }
+
     mPACMan->ProcessPendingQ();
     return NS_OK;
   }
 
 private:
   RefPtr<nsPACMan> mPACMan;
 
   bool      mCancel;
   nsresult  mCancelStatus;
 
   bool                 mSetupPAC;
   uint32_t             mExtraHeapSize;
   nsCString            mSetupPACData;
   nsCString            mSetupPACURI;
+  bool                 mConfigureWPAD;
 };
 
 //-----------------------------------------------------------------------------
 
 PendingPACQuery::PendingPACQuery(nsPACMan* pacMan,
                                  nsIURI* uri,
                                  nsPACManCallback* callback,
                                  bool mainThreadResponse)
@@ -284,30 +354,30 @@ PendingPACQuery::PendingPACQuery(nsPACMa
 {
   uri->GetAsciiSpec(mSpec);
   uri->GetAsciiHost(mHost);
   uri->GetScheme(mScheme);
   uri->GetPort(&mPort);
 }
 
 void
-PendingPACQuery::Complete(nsresult status, const nsCString &pacString)
+PendingPACQuery::Complete(nsresult status, const nsACString &pacString)
 {
   if (!mCallback)
     return;
   RefPtr<ExecuteCallback> runnable = new ExecuteCallback(mCallback, status);
   runnable->SetPACString(pacString);
   if (mOnMainThreadOnly)
     mPACMan->Dispatch(runnable.forget());
   else
     runnable->Run();
 }
 
 void
-PendingPACQuery::UseAlternatePACFile(const nsCString &pacURL)
+PendingPACQuery::UseAlternatePACFile(const nsACString &pacURL)
 {
   if (!mCallback)
     return;
 
   RefPtr<ExecuteCallback> runnable = new ExecuteCallback(mCallback, NS_OK);
   runnable->SetPACURL(pacURL);
   if (mOnMainThreadOnly)
     mPACMan->Dispatch(runnable.forget());
@@ -332,16 +402,19 @@ static const char *kPACIncludePath =
   "network.proxy.autoconfig_url.include_path";
 
 nsPACMan::nsPACMan(nsIEventTarget *mainThreadEventTarget)
   : NeckoTargetHolder(mainThreadEventTarget)
   , mLoadPending(false)
   , mShutdown(false)
   , mLoadFailureCount(0)
   , mInProgress(false)
+  , mAutoDetect(false)
+  , mWPADOverDHCPEnabled(false)
+  , mProxyConfigType(0)
 {
   MOZ_ASSERT(NS_IsMainThread(), "pacman must be created on main thread");
   if (!sThreadLocalSetup){
     sThreadLocalSetup = true;
     PR_NewThreadPrivateIndex(&sThreadLocalIndex, nullptr);
   }
   mPAC.SetThreadLocalIndex(sThreadLocalIndex);
   mIncludePath = Preferences::GetBool(kPACIncludePath, false);
@@ -349,16 +422,17 @@ nsPACMan::nsPACMan(nsIEventTarget *mainT
 
 nsPACMan::~nsPACMan()
 {
   MOZ_ASSERT(mShutdown, "Shutdown must be called before dtor.");
 
   if (mPACThread) {
     if (NS_IsMainThread()) {
       mPACThread->Shutdown();
+      mPACThread = nullptr;
     }
     else {
       RefPtr<ShutdownThread> runnable = new ShutdownThread(mPACThread);
       Dispatch(runnable.forget());
     }
   }
 
   NS_ASSERTION(mLoader == nullptr, "pac man not shutdown properly");
@@ -391,17 +465,17 @@ nsPACMan::AsyncGetProxyForURI(nsIURI *ur
   if (mShutdown)
     return NS_ERROR_NOT_AVAILABLE;
 
   // Maybe Reload PAC
   if (!mPACURISpec.IsEmpty() && !mScheduledReload.IsNull() &&
       TimeStamp::Now() > mScheduledReload) {
     LOG(("nsPACMan::AsyncGetProxyForURI reload as scheduled\n"));
 
-    LoadPACFromURI(EmptyCString());
+    LoadPACFromURI(mAutoDetect? EmptyCString(): mPACURISpec);
   }
 
   RefPtr<PendingPACQuery> query =
     new PendingPACQuery(this, uri, callback, mainThreadResponse);
 
   if (IsPACURI(uri)) {
     // deal with this directly instead of queueing it
     query->Complete(NS_OK, EmptyCString());
@@ -424,26 +498,25 @@ nsPACMan::PostQuery(PendingPACQuery *que
   // add a reference to the query while it is in the pending list
   RefPtr<PendingPACQuery> addref(query);
   mPendingQ.insertBack(addref.forget().take());
   ProcessPendingQ();
   return NS_OK;
 }
 
 nsresult
-nsPACMan::LoadPACFromURI(const nsCString &spec)
+nsPACMan::LoadPACFromURI(const nsACString &aSpec)
 {
   NS_ENSURE_STATE(!mShutdown);
-  NS_ENSURE_ARG(!spec.IsEmpty() || !mPACURISpec.IsEmpty());
 
   nsCOMPtr<nsIStreamLoader> loader =
       do_CreateInstance(NS_STREAMLOADER_CONTRACTID);
   NS_ENSURE_STATE(loader);
 
-  LOG(("nsPACMan::LoadPACFromURI %s\n", spec.get()));
+  LOG(("nsPACMan::LoadPACFromURI aSpec: %s\n", aSpec.BeginReading()));
   // Since we might get called from nsProtocolProxyService::Init, we need to
   // post an event back to the main thread before we try to use the IO service.
   //
   // But, we need to flag ourselves as loading, so that we queue up any PAC
   // queries the enter between now and when we actually load the PAC file.
 
   if (!mLoadPending) {
     nsCOMPtr<nsIRunnable> runnable =
@@ -454,40 +527,112 @@ nsPACMan::LoadPACFromURI(const nsCString
     if (NS_FAILED(rv))
       return rv;
     mLoadPending = true;
   }
 
   CancelExistingLoad();
 
   mLoader = loader;
-  if (!spec.IsEmpty()) {
-    mPACURISpec = spec;
-    mPACURIRedirectSpec.Truncate();
-    mNormalPACURISpec.Truncate(); // set at load time
-    mLoadFailureCount = 0;  // reset
-  }
+  mPACURIRedirectSpec.Truncate();
+  mNormalPACURISpec.Truncate(); // set at load time
+  mLoadFailureCount = 0;  // reset
+  mAutoDetect = aSpec.IsEmpty();
+  mPACURISpec.Assign(aSpec);
 
   // reset to Null
   mScheduledReload = TimeStamp();
   return NS_OK;
 }
 
+nsresult
+nsPACMan::GetPACFromDHCP(nsACString &aSpec)
+{
+  MOZ_ASSERT(!NS_IsMainThread(), "wrong thread");
+  if (!mDHCPClient) {
+    LOG(("nsPACMan::GetPACFromDHCP DHCP option %d query failed because there is no DHCP client available\n", MOZ_DHCP_WPAD_OPTION));
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+  nsresult rv;
+  rv = mDHCPClient->GetOption(MOZ_DHCP_WPAD_OPTION, aSpec);
+  if (NS_FAILED(rv)) {
+    LOG(("nsPACMan::GetPACFromDHCP DHCP option %d query failed with result %d\n", MOZ_DHCP_WPAD_OPTION, (uint32_t)rv));
+  } else {
+    LOG(("nsPACMan::GetPACFromDHCP DHCP option %d query succeeded, finding PAC URL %s\n", MOZ_DHCP_WPAD_OPTION, aSpec.BeginReading()));
+  }
+  return rv;
+}
+
+nsresult
+nsPACMan::ConfigureWPAD(nsACString &aSpec)
+{
+  MOZ_ASSERT(!NS_IsMainThread(), "wrong thread");
+
+  MOZ_RELEASE_ASSERT(mProxyConfigType == nsIProtocolProxyService::PROXYCONFIG_WPAD,
+            "WPAD is being executed when not selected by user");
+
+  aSpec.Truncate();
+  if (mWPADOverDHCPEnabled) {
+    GetPACFromDHCP(aSpec);
+  }
+
+  if (aSpec.IsEmpty()) {
+    // We diverge from the WPAD spec here in that we don't walk the
+    // hosts's FQDN, stripping components until we hit a TLD.  Doing so
+    // is dangerous in the face of an incomplete list of TLDs, and TLDs
+    // get added over time.  We could consider doing only a single
+    // substitution of the first component, if that proves to help
+    // compatibility.
+    aSpec.AssignLiteral(MOZ_WPAD_URL);
+  }
+  return NS_OK;
+}
+
+void
+nsPACMan::AssignPACURISpec(const nsACString &aSpec)
+{
+  MOZ_ASSERT(NS_IsMainThread(), "wrong thread");
+  mPACURISpec.Assign(aSpec);
+}
+
 void
 nsPACMan::StartLoading()
 {
   MOZ_ASSERT(NS_IsMainThread(), "wrong thread");
   mLoadPending = false;
 
   // CancelExistingLoad was called...
   if (!mLoader) {
     PostCancelPendingQ(NS_ERROR_ABORT);
     return;
   }
 
+  if (mAutoDetect) {
+    GetNetworkProxyTypeFromPref(&mProxyConfigType);
+    RefPtr<ExecutePACThreadAction> wpadConfigurer =
+      new ExecutePACThreadAction(this);
+    wpadConfigurer->ConfigureWPAD();
+    if (mPACThread) {
+      mPACThread->Dispatch(wpadConfigurer, nsIEventTarget::DISPATCH_NORMAL);
+    }
+  } else {
+    ContinueLoadingAfterPACUriKnown();
+  }
+}
+
+void
+nsPACMan::ContinueLoadingAfterPACUriKnown()
+{
+  MOZ_ASSERT(NS_IsMainThread(), "wrong thread");
+
+  // CancelExistingLoad was called...
+  if (!mLoader) {
+    PostCancelPendingQ(NS_ERROR_ABORT);
+    return;
+  }
   if (NS_SUCCEEDED(mLoader->Init(this, nullptr))) {
     // Always hit the origin server when loading PAC.
     nsCOMPtr<nsIIOService> ios = do_GetIOService();
     if (ios) {
       nsCOMPtr<nsIChannel> channel;
       nsCOMPtr<nsIURI> pacURI;
       NS_NewURI(getter_AddRefs(pacURI), mPACURISpec);
 
@@ -795,17 +940,18 @@ nsPACMan::AsyncOnChannelRedirect(nsIChan
   callback->OnRedirectVerifyCallback(NS_OK);
   return NS_OK;
 }
 
 nsresult
 nsPACMan::Init(nsISystemProxySettings *systemProxySettings)
 {
   mSystemProxySettings = systemProxySettings;
+  mDHCPClient = do_GetService(NS_DHCPCLIENT_CONTRACTID);
 
   nsresult rv =
     NS_NewNamedThread("ProxyResolution", getter_AddRefs(mPACThread));
 
   return rv;
 }
 
 } // namespace net
-} // namespace mozilla
+} // namespace mozilla
\ No newline at end of file
--- a/netwerk/base/nsPACMan.h
+++ b/netwerk/base/nsPACMan.h
@@ -2,33 +2,34 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 nsPACMan_h__
 #define nsPACMan_h__
 
-#include "nsIStreamLoader.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/LinkedList.h"
+#include "mozilla/Logging.h"
+#include "mozilla/net/NeckoTargetHolder.h"
+#include "mozilla/TimeStamp.h"
+#include "nsAutoPtr.h"
+#include "nsCOMPtr.h"
+#include "nsIChannelEventSink.h"
 #include "nsIInterfaceRequestor.h"
-#include "nsIChannelEventSink.h"
-#include "ProxyAutoConfig.h"
+#include "nsIStreamLoader.h"
 #include "nsThreadUtils.h"
 #include "nsIURI.h"
-#include "nsCOMPtr.h"
 #include "nsString.h"
-#include "mozilla/Attributes.h"
-#include "mozilla/LinkedList.h"
-#include "nsAutoPtr.h"
-#include "mozilla/TimeStamp.h"
-#include "mozilla/Logging.h"
-#include "mozilla/Atomics.h"
-#include "mozilla/net/NeckoTargetHolder.h"
+#include "ProxyAutoConfig.h"
 
 class nsISystemProxySettings;
+class nsIDHCPClient;
 class nsIThread;
 
 namespace mozilla {
 namespace net {
 
 class nsPACMan;
 class WaitForThreadShutdown;
 
@@ -47,31 +48,31 @@ public:
    *        This parameter holds the value of the PAC string.  It is empty when
    *        status is a failure code.
    * @param newPACURL
    *        This parameter holds the URL of a new PAC file that should be loaded
    *        before the query is evaluated again. At least one of pacString and
    *        newPACURL should be 0 length.
    */
   virtual void OnQueryComplete(nsresult status,
-                               const nsCString &pacString,
-                               const nsCString &newPACURL) = 0;
+                               const nsACString &pacString,
+                               const nsACString &newPACURL) = 0;
 };
 
 class PendingPACQuery final : public Runnable,
                               public LinkedListElement<PendingPACQuery>
 {
 public:
   PendingPACQuery(nsPACMan *pacMan, nsIURI *uri,
                   nsPACManCallback *callback,
                   bool mainThreadResponse);
 
   // can be called from either thread
-  void Complete(nsresult status, const nsCString &pacString);
-  void UseAlternatePACFile(const nsCString &pacURL);
+  void Complete(nsresult status, const nsACString &pacString);
+  void UseAlternatePACFile(const nsACString &pacURL);
 
   nsCString                  mSpec;
   nsCString                  mScheme;
   nsCString                  mHost;
   int32_t                    mPort;
 
   NS_IMETHOD Run(void) override;     /* Runnable */
 
@@ -122,21 +123,21 @@ public:
                                nsPACManCallback *callback,
                                bool mustCallbackOnMainThread);
 
   /**
    * This method may be called to reload the PAC file.  While we are loading
    * the PAC file, any asynchronous PAC queries will be queued up to be
    * processed once the PAC file finishes loading.
    *
-   * @param pacSpec
+   * @param aSpec
    *        The non normalized uri spec of this URI used for comparison with
    *        system proxy settings to determine if the PAC uri has changed.
    */
-  nsresult LoadPACFromURI(const nsCString &pacSpec);
+  nsresult LoadPACFromURI(const nsACString &aSpec);
 
   /**
    * Returns true if we are currently loading the PAC file.
    */
   bool IsLoading() { return mLoader != nullptr; }
 
   /**
    * Returns true if the given URI matches the URI of our PAC file or the
@@ -161,71 +162,91 @@ public:
     nsresult rv = uri->GetSpec(tmp);
     if (NS_FAILED(rv)) {
       return false;
     }
 
     return IsPACURI(tmp);
   }
 
+  bool IsUsingWPAD() {
+    return mAutoDetect;
+  }
+
   nsresult Init(nsISystemProxySettings *);
   static nsPACMan *sInstance;
 
   // PAC thread operations only
   void ProcessPendingQ();
   void CancelPendingQ(nsresult);
 
+  void SetWPADOverDHCPEnabled(bool aValue) { mWPADOverDHCPEnabled = aValue; }
+
 private:
   NS_DECL_NSISTREAMLOADEROBSERVER
   NS_DECL_NSIINTERFACEREQUESTOR
   NS_DECL_NSICHANNELEVENTSINK
 
   friend class PendingPACQuery;
   friend class PACLoadComplete;
+  friend class ConfigureWPADComplete;
   friend class ExecutePACThreadAction;
   friend class WaitForThreadShutdown;
+  friend class TestPACMan;
 
   ~nsPACMan();
 
   /**
    * Cancel any existing load if any.
    */
   void CancelExistingLoad();
 
   /**
    * Start loading the PAC file.
    */
   void StartLoading();
 
   /**
+   * Continue loading the PAC file.
+   */
+  void ContinueLoadingAfterPACUriKnown();
+
+  /**
    * Reload the PAC file if there is reason to.
    */
   void MaybeReloadPAC();
 
   /**
    * Called when we fail to load the PAC file.
    */
   void OnLoadFailure();
 
   /**
    * PostQuery() only runs on the PAC thread and it is used to
    * place a pendingPACQuery into the queue and potentially
    * execute the queue if it was otherwise empty
    */
   nsresult PostQuery(PendingPACQuery *query);
 
+  // Having found the PAC URI on the PAC thread, copy it to a string which
+  // can be altered on the main thread.
+  void AssignPACURISpec(const nsACString &aSpec);
+
   // PAC thread operations only
   void PostProcessPendingQ();
   void PostCancelPendingQ(nsresult);
   bool ProcessPending();
+  nsresult GetPACFromDHCP(nsACString &aSpec);
+  nsresult ConfigureWPAD(nsACString &aSpec);
 
 private:
   ProxyAutoConfig mPAC;
   nsCOMPtr<nsIThread>           mPACThread;
   nsCOMPtr<nsISystemProxySettings> mSystemProxySettings;
+  nsCOMPtr<nsIDHCPClient> mDHCPClient;
 
   LinkedList<PendingPACQuery> mPendingQ; /* pac thread only */
 
   // These specs are not nsIURI so that they can be used off the main thread.
   // The non-normalized versions are directly from the configuration, the
   // normalized version has been extracted from an nsIURI
   nsCString                    mPACURISpec;
   nsCString                    mPACURIRedirectSpec;
@@ -234,16 +255,19 @@ private:
   nsCOMPtr<nsIStreamLoader>    mLoader;
   bool                         mLoadPending;
   Atomic<bool, Relaxed>        mShutdown;
   TimeStamp                    mScheduledReload;
   uint32_t                     mLoadFailureCount;
 
   bool                         mInProgress;
   bool                         mIncludePath;
+  bool                         mAutoDetect;
+  bool                         mWPADOverDHCPEnabled;
+  int32_t                      mProxyConfigType;
 };
 
 extern LazyLogModule gProxyLog;
 
 } // namespace net
 } // namespace mozilla
 
-#endif  // nsPACMan_h__
+#endif  // nsPACMan_h__
\ No newline at end of file
--- a/netwerk/base/nsProtocolProxyService.cpp
+++ b/netwerk/base/nsProtocolProxyService.cpp
@@ -57,17 +57,16 @@ namespace net {
 #undef LOG
 #define LOG(args) MOZ_LOG(gProxyLog, LogLevel::Debug, args)
 
 //----------------------------------------------------------------------------
 
 #define PROXY_PREF_BRANCH  "network.proxy"
 #define PROXY_PREF(x)      PROXY_PREF_BRANCH "." x
 
-#define WPAD_URL "http://wpad/wpad.dat"
 
 //----------------------------------------------------------------------------
 
 // This structure is intended to be allocated on the stack
 struct nsProtocolInfo {
     nsAutoCString scheme;
     uint32_t flags;
     int32_t defaultPort;
@@ -306,18 +305,18 @@ public:
         return rv;
     }
 
 private:
 
     // Called asynchronously, so we do not need to post another PLEvent
     // before calling DoCallback.
     void OnQueryComplete(nsresult status,
-                         const nsCString &pacString,
-                         const nsCString &newPACURL) override
+                         const nsACString &pacString,
+                         const nsACString &newPACURL) override
     {
         // If we've already called DoCallback then, nothing more to do.
         if (!mCallback)
             return;
 
         // Provided we haven't been canceled...
         if (mStatus == NS_OK) {
             mStatus = status;
@@ -816,16 +815,17 @@ nsProtocolProxyService::nsProtocolProxyS
     , mProxyConfig(PROXYCONFIG_DIRECT)
     , mHTTPProxyPort(-1)
     , mFTPProxyPort(-1)
     , mHTTPSProxyPort(-1)
     , mSOCKSProxyPort(-1)
     , mSOCKSProxyVersion(4)
     , mSOCKSProxyRemoteDNS(false)
     , mProxyOverTLS(true)
+    , mWPADOverDHCPEnabled(false)
     , mPACMan(nullptr)
     , mSessionStart(PR_Now())
     , mFailedProxyTimeout(30 * 60) // 30 minute default
     , mIsShutdown(false)
 {
 }
 
 nsProtocolProxyService::~nsProtocolProxyService()
@@ -1082,16 +1082,22 @@ nsProtocolProxyService::PrefsChanged(nsI
         proxy_GetBoolPref(prefBranch, PROXY_PREF("socks_remote_dns"),
                           mSOCKSProxyRemoteDNS);
 
     if (!pref || !strcmp(pref, PROXY_PREF("proxy_over_tls"))) {
         proxy_GetBoolPref(prefBranch, PROXY_PREF("proxy_over_tls"),
                           mProxyOverTLS);
     }
 
+    if (!pref || !strcmp(pref, PROXY_PREF("enable_wpad_over_dhcp"))) {
+        proxy_GetBoolPref(prefBranch, PROXY_PREF("enable_wpad_over_dhcp"),
+                          mWPADOverDHCPEnabled);
+        reloadPAC = reloadPAC || mProxyConfig == PROXYCONFIG_WPAD;
+    }
+
     if (!pref || !strcmp(pref, PROXY_PREF("failover_timeout")))
         proxy_GetIntPref(prefBranch, PROXY_PREF("failover_timeout"),
                          mFailedProxyTimeout);
 
     if (!pref || !strcmp(pref, PROXY_PREF("no_proxies_on"))) {
         rv = prefBranch->GetCharPref(PROXY_PREF("no_proxies_on"), tempString);
         if (NS_SUCCEEDED(rv))
             LoadHostFilters(tempString);
@@ -1114,29 +1120,25 @@ nsProtocolProxyService::PrefsChanged(nsI
         tempString.Truncate();
         if (mProxyConfig == PROXYCONFIG_PAC) {
             prefBranch->GetCharPref(PROXY_PREF("autoconfig_url"), tempString);
             if (mPACMan && !mPACMan->IsPACURI(tempString)) {
                 LOG(("PAC Thread URI Changed - Reset Pac Thread"));
                 ResetPACThread();
             }
         } else if (mProxyConfig == PROXYCONFIG_WPAD) {
-            // We diverge from the WPAD spec here in that we don't walk the
-            // hosts's FQDN, stripping components until we hit a TLD.  Doing so
-            // is dangerous in the face of an incomplete list of TLDs, and TLDs
-            // get added over time.  We could consider doing only a single
-            // substitution of the first component, if that proves to help
-            // compatibility.
-            tempString.AssignLiteral(WPAD_URL);
+            LOG(("Auto-detecting proxy - Reset Pac Thread"));
+            ResetPACThread();
         } else if (mSystemProxySettings) {
             // Get System Proxy settings if available
             AsyncConfigureFromPAC(false, false);
         }
-        if (!tempString.IsEmpty())
+        if (!tempString.IsEmpty() || mProxyConfig == PROXYCONFIG_WPAD) {
             ConfigureFromPAC(tempString, false);
+        }
     }
 }
 
 bool
 nsProtocolProxyService::CanUseProxy(nsIURI *aURI, int32_t defaultPort)
 {
     if (mHostFiltersArray.Length() == 0 && !mFilterLocalHosts)
         return true;
@@ -1477,17 +1479,16 @@ nsProtocolProxyService::SetupPACThread(n
     if (mSystemProxySettings &&
         NS_SUCCEEDED(mSystemProxySettings->GetMainThreadOnly(&mainThreadOnly)) &&
         !mainThreadOnly) {
         rv = mPACMan->Init(mSystemProxySettings);
     }
     else {
         rv = mPACMan->Init(nullptr);
     }
-
     if (NS_FAILED(rv)) {
         mPACMan->Shutdown();
         mPACMan = nullptr;
     }
     return rv;
 }
 
 nsresult
@@ -1503,21 +1504,25 @@ nsProtocolProxyService::ResetPACThread()
 
 nsresult
 nsProtocolProxyService::ConfigureFromPAC(const nsCString &spec,
                                          bool forceReload)
 {
     nsresult rv = SetupPACThread();
     NS_ENSURE_SUCCESS(rv, rv);
 
-    if (mPACMan->IsPACURI(spec) && !forceReload)
+    bool autodetect = spec.IsEmpty();
+    if (!forceReload && ((!autodetect && mPACMan->IsPACURI(spec)) ||
+                         (autodetect && mPACMan->IsUsingWPAD()))) {
         return NS_OK;
+    }
 
     mFailedProxies.Clear();
 
+    mPACMan->SetWPADOverDHCPEnabled(mWPADOverDHCPEnabled);
     return mPACMan->LoadPACFromURI(spec);
 }
 
 void
 nsProtocolProxyService::ProcessPACString(const nsCString &pacString,
                                          uint32_t aResolveFlags,
                                          nsIProxyInfo **result)
 {
@@ -1560,27 +1565,25 @@ nsProtocolProxyService::ReloadPAC()
     int32_t type;
     nsresult rv = prefs->GetIntPref(PROXY_PREF("type"), &type);
     if (NS_FAILED(rv))
         return NS_OK;
 
     nsAutoCString pacSpec;
     if (type == PROXYCONFIG_PAC)
         prefs->GetCharPref(PROXY_PREF("autoconfig_url"), pacSpec);
-    else if (type == PROXYCONFIG_WPAD)
-        pacSpec.AssignLiteral(WPAD_URL);
     else if (type == PROXYCONFIG_SYSTEM) {
         if (mSystemProxySettings) {
             AsyncConfigureFromPAC(true, true);
         } else {
             ResetPACThread();
         }
     }
 
-    if (!pacSpec.IsEmpty())
+    if (!pacSpec.IsEmpty() || type == PROXYCONFIG_WPAD)
         ConfigureFromPAC(pacSpec, true);
     return NS_OK;
 }
 
 // When sync interface is removed this can go away too
 // The nsPACManCallback portion of this implementation should be run
 // off the main thread, because it uses a condvar for signaling and
 // the main thread is blocking on that condvar -
@@ -1594,18 +1597,18 @@ class nsAsyncBridgeRequest final  : publ
         : mMutex("nsDeprecatedCallback")
         , mCondVar(mMutex, "nsDeprecatedCallback")
         , mStatus(NS_OK)
         , mCompleted(false)
     {
     }
 
     void OnQueryComplete(nsresult status,
-                         const nsCString &pacString,
-                         const nsCString &newPACURL) override
+                          const nsACString &pacString,
+                          const nsACString &newPACURL) override
     {
         MutexAutoLock lock(mMutex);
         mCompleted = true;
         mStatus = status;
         mPACString = pacString;
         mPACURL = newPACURL;
         mCondVar.Notify();
     }
@@ -2541,9 +2544,9 @@ nsProtocolProxyService::PruneProxyInfo(c
         NS_RELEASE(head);
 
     *list = head;  // Transfer ownership
 
     LOG(("nsProtocolProxyService::PruneProxyInfo LEAVE list=%p", *list));
 }
 
 } // namespace net
-} // namespace mozilla
+} // namespace mozilla
\ No newline at end of file
--- a/netwerk/base/nsProtocolProxyService.h
+++ b/netwerk/base/nsProtocolProxyService.h
@@ -395,16 +395,17 @@ protected:
 
     // mSOCKSProxyTarget could be a host, a domain socket path,
     // or a named-pipe name.
     nsCString                    mSOCKSProxyTarget;
     int32_t                      mSOCKSProxyPort;
     int32_t                      mSOCKSProxyVersion;
     bool                         mSOCKSProxyRemoteDNS;
     bool                         mProxyOverTLS;
+    bool                         mWPADOverDHCPEnabled;
 
     RefPtr<nsPACMan>           mPACMan;  // non-null if we are using PAC
     nsCOMPtr<nsISystemProxySettings> mSystemProxySettings;
 
     PRTime                       mSessionStart;
     nsFailedProxyTable           mFailedProxies;
     int32_t                      mFailedProxyTimeout;
 
@@ -418,9 +419,9 @@ private:
     nsCOMPtr<nsIThread>           mProxySettingThread;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsProtocolProxyService, NS_PROTOCOL_PROXY_SERVICE_IMPL_CID)
 
 } // namespace net
 } // namespace mozilla
 
-#endif // !nsProtocolProxyService_h__
+#endif // !nsProtocolProxyService_h__
\ No newline at end of file
--- a/netwerk/build/nsNetCID.h
+++ b/netwerk/build/nsNetCID.h
@@ -330,16 +330,20 @@
 // component implementing nsIIncrementalDownload.
 #define NS_INCREMENTALDOWNLOAD_CONTRACTID \
     "@mozilla.org/network/incremental-download;1"
 
 // component implementing nsISystemProxySettings.
 #define NS_SYSTEMPROXYSETTINGS_CONTRACTID \
     "@mozilla.org/system-proxy-settings;1"
 
+// component implementing nsIDHCPClient.
+#define NS_DHCPCLIENT_CONTRACTID \
+    "@mozilla.org/dhcp-client;1"
+
 // service implementing nsIStreamTransportService
 #define NS_STREAMTRANSPORTSERVICE_CONTRACTID \
     "@mozilla.org/network/stream-transport-service;1"
 #define NS_STREAMTRANSPORTSERVICE_CID \
 { /* 0885d4f8-f7b8-4cda-902e-94ba38bc256e */         \
     0x0885d4f8,                                      \
     0xf7b8,                                          \
     0x4cda,                                          \
new file mode 100644
--- /dev/null
+++ b/netwerk/test/gtest/TestPACMan.cpp
@@ -0,0 +1,282 @@
+#include "gtest/gtest.h"
+#include "nsServiceManagerUtils.h"
+#include "../../../xpcom/threads/nsThreadManager.h"
+#include "nsIDHCPClient.h"
+#include "nsIPrefBranch.h"
+#include "nsComponentManager.h"
+#include "mozilla/ModuleUtils.h"
+#include "../../base/nsPACMan.h"
+
+
+#define TEST_WPAD_DHCP_OPTION "http://pac/pac.dat"
+#define TEST_ASSIGNED_PAC_URL "http://assignedpac/pac.dat"
+#define WPAD_PREF 4
+#define NETWORK_PROXY_TYPE_PREF_NAME "network.proxy.type"
+#define GETTING_NETWORK_PROXY_TYPE_FAILED -1
+
+nsCString WPADOptionResult;
+
+namespace mozilla {
+namespace net {
+
+nsresult
+SetNetworkProxyType(int32_t pref)
+{
+  nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
+
+  if (!prefs) {
+    return NS_ERROR_FACTORY_NOT_REGISTERED;
+  }
+  return prefs->SetIntPref(NETWORK_PROXY_TYPE_PREF_NAME, pref);
+}
+
+nsresult
+GetNetworkProxyType(int32_t* pref)
+{
+  nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
+
+  if (!prefs) {
+    return NS_ERROR_FACTORY_NOT_REGISTERED;
+  }
+  return prefs->GetIntPref(NETWORK_PROXY_TYPE_PREF_NAME, pref);
+}
+
+class nsTestDHCPClient final : public nsIDHCPClient
+{
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSIDHCPCLIENT
+
+  nsTestDHCPClient() {};
+  nsresult Init(){
+    return NS_OK;
+  };
+
+private:
+   ~nsTestDHCPClient() {};
+};
+
+NS_IMETHODIMP
+nsTestDHCPClient::GetOption(uint8_t option, nsACString & _retval)
+{
+  _retval.Assign(WPADOptionResult);
+  return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(nsTestDHCPClient, nsIDHCPClient)
+
+#define NS_TESTDHCPCLIENTSERVICE_CID  /* {FEBF1D69-4D7D-4891-9524-045AD18B5592} */\
+    { 0xFEBF1D69, 0x4D7D, 0x4891, \
+         {0x95, 0x24, 0x04, 0x5a, 0xd1, 0x8b, 0x55, 0x92 } }
+
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsTestDHCPClient, Init)
+NS_DEFINE_NAMED_CID(NS_TESTDHCPCLIENTSERVICE_CID);
+
+static const mozilla::Module::CIDEntry kSysDHCPClientCIDs[] = {
+  { &kNS_TESTDHCPCLIENTSERVICE_CID, false, nullptr, nsTestDHCPClientConstructor },
+  { nullptr }
+};
+
+static const mozilla::Module::ContractIDEntry kSysDHCPClientContracts[] = {
+  { NS_DHCPCLIENT_CONTRACTID, &kNS_TESTDHCPCLIENTSERVICE_CID },
+  { nullptr }
+};
+
+static const mozilla::Module kSysDHCPClientModule = {
+  mozilla::Module::kVersion,
+  kSysDHCPClientCIDs,
+  kSysDHCPClientContracts
+};
+
+NSMODULE_DEFN(nsDHCPClientModule) = &kSysDHCPClientModule;
+
+void
+SetOptionResult(const char* result)
+{
+  WPADOptionResult.Assign(result);
+}
+
+class ProcessPendingEventsAction final : public Runnable
+{
+public:
+  ProcessPendingEventsAction() : Runnable("net::ProcessPendingEventsAction") { }
+
+  NS_IMETHOD
+  Run() override
+  {
+    if (NS_HasPendingEvents(nullptr)) {
+      NS_WARNING("Found pending requests on PAC thread");
+      nsresult rv;
+      rv = NS_ProcessPendingEvents(nullptr);
+      EXPECT_EQ(NS_OK, rv);
+    }
+    NS_WARNING("No pending requests on PAC thread");
+    return NS_OK;
+  }
+};
+
+class TestPACMan : public ::testing::Test {
+  protected:
+
+    RefPtr<nsPACMan> mPACMan;
+
+    void
+    ProcessAllEvents()
+    {
+      ProcessPendingEventsOnPACThread();
+      nsresult rv;
+      while (NS_HasPendingEvents(nullptr)) {
+        NS_WARNING("Pending events on main thread");
+        rv = NS_ProcessPendingEvents(nullptr);
+        ASSERT_EQ(NS_OK, rv);
+        ProcessPendingEventsOnPACThread();
+      }
+      NS_WARNING("End of pending events on main thread");
+    }
+
+
+    // This method is used to ensure that all pending events on the main thread
+    // and the Proxy thread are processsed.
+    // It iterates over ProcessAllEvents because simply calling ProcessAllEvents once
+    // did not reliably process the events on both threads on all platforms.
+    void
+    ProcessAllEventsTenTimes(){
+      for (int i = 0; i < 10; i++) {
+        ProcessAllEvents();
+      }
+    }
+
+    virtual void
+    SetUp()
+    {
+      ASSERT_EQ(NS_OK, GetNetworkProxyType(&originalNetworkProxyTypePref));
+      nsFactoryEntry* factoryEntry = nsComponentManagerImpl::gComponentManager
+          ->GetFactoryEntry(kNS_TESTDHCPCLIENTSERVICE_CID);
+      if (factoryEntry) {
+        nsresult rv = nsComponentManagerImpl::gComponentManager->UnregisterFactory(kNS_TESTDHCPCLIENTSERVICE_CID, factoryEntry->mFactory);
+        ASSERT_EQ(NS_OK, rv);
+      }
+      nsComponentManagerImpl::gComponentManager->RegisterModule(&kSysDHCPClientModule, nullptr);
+
+      mPACMan = new nsPACMan(nullptr);
+      mPACMan->SetWPADOverDHCPEnabled(true);
+      mPACMan->Init(nullptr);
+      ASSERT_EQ(NS_OK, SetNetworkProxyType(WPAD_PREF));
+
+    }
+
+    virtual void
+    TearDown()
+    {
+
+      mPACMan->Shutdown();
+      if (originalNetworkProxyTypePref != GETTING_NETWORK_PROXY_TYPE_FAILED) {
+        ASSERT_EQ(NS_OK, SetNetworkProxyType(originalNetworkProxyTypePref));
+      }
+    }
+
+    nsCOMPtr<nsIDHCPClient>
+    GetPACManDHCPCient()
+    {
+      return mPACMan->mDHCPClient;
+    }
+
+    void
+    SetPACManDHCPCient(nsCOMPtr<nsIDHCPClient> aValue)
+    {
+       mPACMan->mDHCPClient = aValue;
+    }
+
+    void
+    AssertPACSpecEqualTo(const char* aExpected)
+    {
+      ASSERT_STREQ(aExpected, mPACMan->mPACURISpec.Data());
+    }
+
+  private:
+
+    int32_t originalNetworkProxyTypePref = GETTING_NETWORK_PROXY_TYPE_FAILED;
+
+    void ProcessPendingEventsOnPACThread(){
+      RefPtr<ProcessPendingEventsAction> action =
+          new ProcessPendingEventsAction();
+
+      mPACMan->mPACThread->Dispatch(action, nsIEventTarget::DISPATCH_SYNC);
+    }
+};
+
+TEST_F(TestPACMan, TestCreateDHCPClientAndGetOption) {
+  SetOptionResult(TEST_WPAD_DHCP_OPTION);
+  nsCString spec;
+
+  GetPACManDHCPCient()->GetOption(252, spec);
+
+  ASSERT_STREQ(TEST_WPAD_DHCP_OPTION, spec.Data());
+}
+
+TEST_F(TestPACMan, TestCreateDHCPClientAndGetEmptyOption) {
+  SetOptionResult("");
+  nsCString spec;
+  spec.AssignLiteral(TEST_ASSIGNED_PAC_URL);
+
+  GetPACManDHCPCient()->GetOption(252, spec);
+
+  ASSERT_TRUE(spec.IsEmpty());
+}
+
+TEST_F(TestPACMan, WhenTheDHCPClientExistsAndDHCPIsNonEmptyDHCPOptionIsUsedAsPACUri) {
+  SetOptionResult(TEST_WPAD_DHCP_OPTION);
+
+  mPACMan->LoadPACFromURI(EmptyCString());
+  ProcessAllEventsTenTimes();
+
+  ASSERT_STREQ(TEST_WPAD_DHCP_OPTION, WPADOptionResult.Data());
+  AssertPACSpecEqualTo(TEST_WPAD_DHCP_OPTION);
+}
+
+TEST_F(TestPACMan, WhenTheDHCPResponseIsEmptyWPADDefaultsToStandardURL) {
+  SetOptionResult(EmptyCString().Data());
+
+  mPACMan->LoadPACFromURI(EmptyCString());
+  ASSERT_TRUE(NS_HasPendingEvents(nullptr));
+  ProcessAllEventsTenTimes();
+
+  ASSERT_STREQ("", WPADOptionResult.Data());
+  AssertPACSpecEqualTo("http://wpad/wpad.dat");
+}
+
+TEST_F(TestPACMan, WhenThereIsNoDHCPClientWPADDefaultsToStandardURL) {
+  SetOptionResult(TEST_WPAD_DHCP_OPTION);
+  SetPACManDHCPCient(nullptr);
+
+  mPACMan->LoadPACFromURI(EmptyCString());
+  ProcessAllEventsTenTimes();
+
+  ASSERT_STREQ(TEST_WPAD_DHCP_OPTION, WPADOptionResult.Data());
+  AssertPACSpecEqualTo("http://wpad/wpad.dat");
+}
+
+TEST_F(TestPACMan, WhenWPADOverDHCPIsPreffedOffWPADDefaultsToStandardURL) {
+  SetOptionResult(TEST_WPAD_DHCP_OPTION);
+  mPACMan->SetWPADOverDHCPEnabled(false);
+
+  mPACMan->LoadPACFromURI(EmptyCString());
+  ProcessAllEventsTenTimes();
+
+  ASSERT_STREQ(TEST_WPAD_DHCP_OPTION, WPADOptionResult.Data());
+  AssertPACSpecEqualTo("http://wpad/wpad.dat");
+}
+
+TEST_F(TestPACMan, WhenPACUriIsSetDirectlyItIsUsedRatherThanWPAD) {
+  SetOptionResult(TEST_WPAD_DHCP_OPTION);
+  nsCString spec;
+  spec.AssignLiteral(TEST_ASSIGNED_PAC_URL);
+
+  mPACMan->LoadPACFromURI(spec);
+  ProcessAllEventsTenTimes();
+
+  AssertPACSpecEqualTo(TEST_ASSIGNED_PAC_URL);
+}
+
+} // namespace net
+} // namespace mozilla
\ No newline at end of file
--- a/netwerk/test/gtest/moz.build
+++ b/netwerk/test/gtest/moz.build
@@ -5,16 +5,17 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 UNIFIED_SOURCES += [
     'TestBufferedInputStream.cpp',
     'TestHeaders.cpp',
     'TestHttpAuthUtils.cpp',
     'TestMIMEInputStream.cpp',
     'TestMozURL.cpp',
+    'TestPACMan.cpp',
     'TestPartiallySeekableInputStream.cpp',
     'TestProtocolProxyService.cpp',
     'TestReadStreamToString.cpp',
     'TestServerTimingHeader.cpp',
     'TestStandardURL.cpp',
     'TestURIMutator.cpp',
 ]
 
@@ -25,8 +26,13 @@ TEST_DIRS += [
 LOCAL_INCLUDES += [
     '/netwerk/base',
     '/xpcom/tests/gtest',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul-gtest'
+
+LOCAL_INCLUDES += [
+    '!/xpcom',
+    '/xpcom/components'
+]
\ No newline at end of file
--- a/toolkit/moz.build
+++ b/toolkit/moz.build
@@ -35,17 +35,18 @@ if CONFIG['MOZ_MAINTENANCE_SERVICE']:
 
 DIRS += ['xre']
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gtk3':
     DIRS += ['system/unixproxy']
 elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
     DIRS += ['system/osxproxy']
 elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
-    DIRS += ['system/windowsproxy']
+    DIRS += ['system/windowsproxy',
+            'system/windowsDHCPClient']
 elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
     DIRS += ['system/androidproxy']
 
 TEST_HARNESS_FILES.testing.mochitest.browser.toolkit.crashreporter.test.browser += [
     'crashreporter/test/browser/crashreport.sjs',
 ]
 
 with Files('moz.*'):
new file mode 100644
--- /dev/null
+++ b/toolkit/system/windowsDHCPClient/DHCPUtils.cpp
@@ -0,0 +1,258 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "DHCPUtils.h"
+#include <vector>
+#include "mozilla\Logging.h"
+#include "nsString.h"
+
+
+#define MOZ_WORKING_BUFFER_SIZE_NETWORK_ADAPTERS 15000
+#define MOZ_WORKING_BUFFER_SIZE_DHCP_PARAMS 1000
+#define MOZ_MAX_TRIES 3
+namespace mozilla {
+namespace toolkit {
+namespace system {
+namespace windowsDHCPClient {
+
+//
+// The comments on this page reference the following Microsoft documentation pages (both retrieved 2017-06-27)
+// [1] https://msdn.microsoft.com/en-us/library/windows/desktop/aa365915(v=vs.85).aspx
+// [2] https://msdn.microsoft.com/en-us/library/windows/desktop/aa363298(v=vs.85).aspx
+mozilla::LazyLogModule gDhcpUtilsLog("dhcpUtils");
+
+#undef LOG
+#define LOG(args) MOZ_LOG(gDhcpUtilsLog, LogLevel::Debug, args)
+
+bool
+IsCurrentAndHasDHCP(PIP_ADAPTER_ADDRESSES aAddresses)
+{
+  return aAddresses->OperStatus == 1 &&
+      (aAddresses->Dhcpv4Server.iSockaddrLength ||
+      aAddresses->Dhcpv6Server.iSockaddrLength);
+}
+
+nsresult
+GetActiveDHCPNetworkAdapterName(nsACString& aNetworkAdapterName,
+    WindowsNetworkFunctionsWrapper* aWindowsNetworkFunctionsWrapper)
+{
+  /* Declare and initialize variables */
+
+  uint32_t dwSize = 0;
+  uint32_t dwRetVal = 0;
+  nsresult rv = NS_ERROR_FAILURE;
+
+  // Set the flags to pass to GetAdaptersAddresses
+  uint32_t flags = GAA_FLAG_INCLUDE_PREFIX;
+
+  // default to unspecified address family (both)
+  uint32_t family = AF_UNSPEC;
+
+  // Allocate a 15 KB buffer to start with.
+  uint32_t outBufLen = MOZ_WORKING_BUFFER_SIZE_NETWORK_ADAPTERS;
+  uint32_t iterations = 0;
+
+  aNetworkAdapterName.Truncate();
+
+  // Now we try calling the GetAdaptersAddresses method until the return value
+  // is not ERROR_BUFFER_OVERFLOW. According to [1]
+  //
+  //
+  // > When the return value is ERROR_BUFFER_OVERFLOW, the SizePointer parameter returned
+  // > points to the required size of the buffer to hold the adapter information.
+  // > Note that it is possible for the buffer size required for the IP_ADAPTER_ADDRESSES
+  // > structures pointed to by the AdapterAddresses parameter to change between
+  // > subsequent calls to the GetAdaptersAddresses function if an adapter address
+  // > is added or removed. However, this method of using the GetAdaptersAddresses
+  // > function is strongly discouraged. This method requires calling the
+  // > GetAdaptersAddresses function multiple times.
+  // >
+  // > The recommended method of calling the GetAdaptersAddresses function is
+  // > to pre-allocate a 15KB working buffer pointed to by the AdapterAddresses parameter.
+  // > On typical computers, this dramatically reduces the chances that the
+  // > GetAdaptersAddresses function returns ERROR_BUFFER_OVERFLOW, which would require
+  // > calling GetAdaptersAddresses function multiple times.
+  //
+  //
+  // The possibility of the buffer size changing between calls to
+  // GetAdaptersAddresses is why we allow the following code to be called several times,
+  // rather than just the two that would be neccessary if we could rely on the
+  // value returned in outBufLen being the true size needed.
+
+  std::vector<IP_ADAPTER_ADDRESSES> pAddresses;
+  do {
+    // resize outBufLen up to the next multiple of sizeof(IP_ADAPTER_ADDRESSES)
+    outBufLen = ((outBufLen + sizeof(IP_ADAPTER_ADDRESSES) - 1) / sizeof(IP_ADAPTER_ADDRESSES)) * sizeof(IP_ADAPTER_ADDRESSES);
+    pAddresses.resize(outBufLen/sizeof(IP_ADAPTER_ADDRESSES));
+    LOG(("Trying GetAdaptersAddresses with pAddresses sized to %d and buffer length of %d", pAddresses.size(), outBufLen));
+
+    dwRetVal =
+      aWindowsNetworkFunctionsWrapper->GetAdaptersAddressesWrapped(
+        family, flags, nullptr, pAddresses.data(), (PULONG)&outBufLen);
+
+    if (dwRetVal == ERROR_BUFFER_OVERFLOW) {
+      iterations++;
+    }
+  } while (dwRetVal == ERROR_BUFFER_OVERFLOW && iterations < MOZ_MAX_TRIES);
+
+  switch(dwRetVal) {
+    case NO_ERROR:
+      {
+        // set default return value if we don't find a suitable network adapter
+        rv = NS_ERROR_NOT_AVAILABLE;
+        PIP_ADAPTER_ADDRESSES pCurrAddresses = pAddresses.data();
+        while (pCurrAddresses) {
+          if (IsCurrentAndHasDHCP(pCurrAddresses)) {
+            rv = NS_OK;
+            aNetworkAdapterName.Assign(pCurrAddresses->AdapterName);
+            break;
+          }
+          pCurrAddresses = pCurrAddresses->Next;
+        }
+      }
+      break;
+    case ERROR_NO_DATA:
+      rv = NS_ERROR_NOT_AVAILABLE;
+      break;
+    default:
+      MOZ_LOG(gDhcpUtilsLog, mozilla::LogLevel::Warning,
+              ("GetAdaptersAddresses returned %d", dwRetVal));
+      rv = NS_ERROR_FAILURE;
+      break;
+  }
+  return rv;
+}
+
+DWORD
+IterateDHCPInformRequestsUntilBufferLargeEnough(
+    DHCPCAPI_PARAMS&     aDhcpRequestedOptionParams,
+     wchar_t*            aWideNetworkAdapterName,
+     std::vector<char>&  aBuffer,
+     WindowsNetworkFunctionsWrapper* aWindowsNetworkFunctionsWrapper)
+{
+  uint32_t iterations = 0;
+  uint32_t outBufLen = MOZ_WORKING_BUFFER_SIZE_DHCP_PARAMS;
+
+  DHCPCAPI_PARAMS_ARRAY RequestParams = {
+    1,  // only one option to request
+    &aDhcpRequestedOptionParams
+  };
+
+  // According to [2],
+  // the following is for 'Optional data to be requested,
+  // in addition to the data requested in the RecdParams array.'
+  // We are not requesting anything in addition, so this is empty.
+  DHCPCAPI_PARAMS_ARRAY SendParams = {
+    0,
+    nullptr
+  };
+
+  DWORD winAPIResponse;
+  // Now we try calling the DHCPRequestParams method until the return value
+  // is not ERROR_MORE_DATA. According to [2]:
+  //
+  //
+  // > Note that the required size of Buffer may increase during the time that elapses
+  // > between the initial function call's return and a subsequent call;
+  // > therefore, the required size of Buffer (indicated in pSize)
+  // > provides an indication of the approximate size required of Buffer,
+  // > rather than guaranteeing that subsequent calls will return successfully
+  // > if Buffer is set to the size indicated in pSize.
+  //
+  //
+  // This is why we allow this DHCPRequestParams to be called several times,
+  // rather than just the two that would be neccessary if we could rely on the
+  // value returned in outBufLen being the true size needed.
+  do {
+    aBuffer.resize(outBufLen);
+
+    winAPIResponse = aWindowsNetworkFunctionsWrapper->DhcpRequestParamsWrapped(
+      DHCPCAPI_REQUEST_SYNCHRONOUS, // Flags
+      nullptr,                         // Reserved
+      aWideNetworkAdapterName,               // Adapter Name
+      nullptr,                         // not using class id
+      SendParams,                         // sent parameters
+      RequestParams,                // requesting params
+      (PBYTE)aBuffer.data(),            // buffer for the output of RequestParams
+      (PULONG)&outBufLen,                      // buffer size
+      nullptr                          // Request ID for persistent requests - not needed here
+    );
+
+    if (winAPIResponse == ERROR_MORE_DATA) {
+      iterations++;
+    }
+  } while (winAPIResponse == ERROR_MORE_DATA && iterations < MOZ_MAX_TRIES);
+  return winAPIResponse;
+}
+
+nsresult
+RetrieveOption(
+  const nsACString&   aAdapterName,
+  uint8_t            aOption,
+  std::vector<char>& aOptionValueBuf,
+  uint32_t*          aOptionSize,
+  WindowsNetworkFunctionsWrapper* aWindowsNetworkFunctionsWrapper)
+{
+
+  nsresult rv;
+  nsAutoString wideNetworkAdapterName = NS_ConvertUTF8toUTF16(aAdapterName);
+
+  DHCPCAPI_PARAMS DhcpRequestedOptionParams = {
+    0,                //  Flags - Reserved, must be set to zero [2]
+    aOption, // OptionId
+    false,            // whether this is vendor specific - let's assume not
+    nullptr,             // data filled in on return
+    0                // nBytes used by return data
+  };
+
+  std::vector<char> tmpBuffer(MOZ_WORKING_BUFFER_SIZE_DHCP_PARAMS);  // a buffer for the DHCP response object
+  DWORD winAPIResponse = IterateDHCPInformRequestsUntilBufferLargeEnough(DhcpRequestedOptionParams,
+     wideNetworkAdapterName.get(),
+     tmpBuffer,
+     aWindowsNetworkFunctionsWrapper);
+
+  switch (winAPIResponse){
+    case NO_ERROR:
+      {
+        if (DhcpRequestedOptionParams.nBytesData == 0) {
+          *aOptionSize = 0;
+          rv = NS_ERROR_NOT_AVAILABLE;
+          break;
+        }
+
+        if (*aOptionSize >= DhcpRequestedOptionParams.nBytesData) {
+          rv = NS_OK;
+        } else {
+          rv = NS_ERROR_LOSS_OF_SIGNIFICANT_DATA;
+        }
+
+        uint32_t actualSizeReturned =
+              *aOptionSize > DhcpRequestedOptionParams.nBytesData?
+              DhcpRequestedOptionParams.nBytesData: *aOptionSize;
+
+        memcpy(aOptionValueBuf.data(),
+              DhcpRequestedOptionParams.Data, actualSizeReturned);
+        *aOptionSize = DhcpRequestedOptionParams.nBytesData;
+        break;
+      }
+    case ERROR_INVALID_PARAMETER:
+      MOZ_LOG(gDhcpUtilsLog, mozilla::LogLevel::Warning,
+          ("RetrieveOption returned %d (ERROR_INVALID_PARAMETER) when option %d requested",
+           winAPIResponse, aOption));
+      rv = NS_ERROR_INVALID_ARG;
+      break;
+    default:
+      MOZ_LOG(gDhcpUtilsLog, mozilla::LogLevel::Warning,
+          ("RetrieveOption returned %d when option %d requested", winAPIResponse, aOption));
+      rv = NS_ERROR_FAILURE;
+  }
+  return rv;
+}
+
+} // namespace windowsDHCPClient
+} // namespace system
+} // namespace toolkit
+} // namespace mozilla
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/toolkit/system/windowsDHCPClient/DHCPUtils.h
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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_toolkit_system_windowsDHCPClient_DHCPUtils_h
+#define mozilla_toolkit_system_windowsDHCPClient_DHCPUtils_h
+
+#include "WindowsNetworkFunctionsWrapper.h"
+#include <vector>
+
+namespace mozilla {
+namespace toolkit {
+namespace system {
+namespace windowsDHCPClient {
+
+nsresult GetActiveDHCPNetworkAdapterName(
+  nsACString& aNetworkAdapterName,
+  WindowsNetworkFunctionsWrapper* aWindowsNetworkFunctionsWrapper);
+
+nsresult RetrieveOption(
+  const nsACString&  aAdapterName,
+  uint8_t           aOption,
+  std::vector<char>& aOptionValueBuf,
+  uint32_t*         aOptionSize,
+  WindowsNetworkFunctionsWrapper* aWindowsNetworkFunctionsWrapper
+);
+
+} // namespace windowsDHCPClient
+} // namespace system
+} // namespace toolkit
+} // namespace mozilla
+#endif // mozilla_toolkit_system_windowsDHCPClient_DHCPUtils_h
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/toolkit/system/windowsDHCPClient/WindowsNetworkFunctionsWrapper.cpp
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "WindowsNetworkFunctionsWrapper.h"
+
+
+#pragma comment(lib, "IPHLPAPI.lib")
+#pragma comment(lib, "dhcpcsvc.lib" )
+
+namespace mozilla {
+namespace toolkit {
+namespace system {
+namespace windowsDHCPClient {
+
+NS_IMPL_ISUPPORTS(WindowsNetworkFunctionsWrapper, nsISupports)
+
+ULONG WindowsNetworkFunctionsWrapper::GetAdaptersAddressesWrapped(
+      _In_    ULONG                 aFamily,
+      _In_    ULONG                 aFlags,
+      _In_    PVOID                 aReserved,
+      _Inout_ PIP_ADAPTER_ADDRESSES aAdapterAddresses,
+      _Inout_ PULONG                aSizePointer)
+{
+  return GetAdaptersAddresses(aFamily, aFlags, aReserved, aAdapterAddresses, aSizePointer);
+}
+
+DWORD WindowsNetworkFunctionsWrapper::DhcpRequestParamsWrapped(
+    _In_    DWORD                 aFlags,
+    _In_    LPVOID                aReserved,
+    _In_    LPWSTR                aAdapterName,
+    _In_    LPDHCPCAPI_CLASSID    aClassId,
+    _In_    DHCPCAPI_PARAMS_ARRAY aSendParams,
+    _Inout_ DHCPCAPI_PARAMS_ARRAY aRecdParams,
+    _In_    LPBYTE                aBuffer,
+    _Inout_ LPDWORD               apSize,
+    _In_    LPWSTR                aRequestIdStr)
+{
+  return DhcpRequestParams(aFlags, aReserved, aAdapterName, aClassId, aSendParams, aRecdParams, aBuffer, apSize, aRequestIdStr);
+}
+} // namespace windowsDHCPClient
+} // namespace system
+} // namespace toolkit
+} // namespace mozilla
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/toolkit/system/windowsDHCPClient/WindowsNetworkFunctionsWrapper.h
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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_toolkit_system_windowsDHCPClient_windowsNetworkFunctionsWrapper_h
+#define mozilla_toolkit_system_windowsDHCPClient_windowsNetworkFunctionsWrapper_h
+
+#include <Winsock2.h> // there is a compilation error if Winsock.h is not
+                      // declared before dhcpcsdk.h
+#include <dhcpcsdk.h>
+#include <iphlpapi.h>
+
+#include "nsISupports.h"
+
+// Thin wrapper around low-level network functions needed for DHCP querying for web proxy
+namespace mozilla {
+namespace toolkit {
+namespace system {
+namespace windowsDHCPClient {
+
+class WindowsNetworkFunctionsWrapper : nsISupports
+{
+
+  public:
+
+    NS_DECL_THREADSAFE_ISUPPORTS
+    WindowsNetworkFunctionsWrapper(){};
+
+    virtual ULONG GetAdaptersAddressesWrapped(
+      _In_    ULONG                 aFamily,
+      _In_    ULONG                 aFlags,
+      _In_    PVOID                 aReserved,
+      _Inout_ PIP_ADAPTER_ADDRESSES aAdapterAddresses,
+      _Inout_ PULONG                aSizePointer
+    );
+
+    virtual DWORD DhcpRequestParamsWrapped(
+    _In_    DWORD                 aFlags,
+    _In_    LPVOID                aReserved,
+    _In_    LPWSTR                aAdapterName,
+    _In_    LPDHCPCAPI_CLASSID    aClassId,
+    _In_    DHCPCAPI_PARAMS_ARRAY aSendParams,
+    _Inout_ DHCPCAPI_PARAMS_ARRAY aRecdParams,
+    _In_    LPBYTE                aBuffer,
+    _Inout_ LPDWORD               apSize,
+    _In_    LPWSTR                aRequestIdStr
+  );
+
+  protected:
+    ~WindowsNetworkFunctionsWrapper(){};
+
+};
+
+} // namespace windowsDHCPClient
+} // namespace system
+} // namespace toolkit
+} // namespace mozilla
+#endif //mozilla_toolkit_system_windowsDHCPClient_windowsNetworkFunctionsWrapper_h
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/toolkit/system/windowsDHCPClient/moz.build
@@ -0,0 +1,18 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+with Files('**'):
+    BUG_COMPONENT = ('Core', 'Networking: HTTP')
+
+TEST_DIRS += ['tests/gtest']
+
+SOURCES += [
+    'DHCPUtils.cpp',
+    'nsWindowsDHCPClient.cpp',
+    'WindowsNetworkFunctionsWrapper.cpp'
+]
+
+FINAL_LIBRARY = 'xul'
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/toolkit/system/windowsDHCPClient/nsWindowsDHCPClient.cpp
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "nsWindowsDHCPClient.h"
+
+#include <vector>
+
+#include "DHCPUtils.h"
+#include "nsNetCID.h"
+#include "nsString.h"
+#include "mozilla/Logging.h"
+#include "mozilla/ModuleUtils.h"
+
+namespace mozilla {
+namespace toolkit {
+namespace system {
+namespace windowsDHCPClient {
+
+LazyLogModule gDhcpLog("windowsDHCPClient");
+
+#undef LOG
+#define LOG(args) MOZ_LOG(gDhcpLog, LogLevel::Debug, args)
+
+#define MOZ_MAX_DHCP_OPTION_LENGTH 255 // this is the maximum option length in DHCP V4 and 6
+
+NS_IMPL_ISUPPORTS(nsWindowsDHCPClient, nsIDHCPClient)
+
+nsresult
+nsWindowsDHCPClient::Init()
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowsDHCPClient::GetOption(uint8_t aOption, nsACString& aRetVal)
+{
+  nsCString networkAdapterName;
+  nsresult rv;
+  rv = GetActiveDHCPNetworkAdapterName(networkAdapterName, mNetworkFunctions);
+  if (rv != NS_OK) {
+    LOG(("Failed to get network adapter name in nsWindowsDHCPClient::GetOption due to error %d", rv));
+    return rv;
+  }
+
+  uint32_t sizeOptionValue = MOZ_MAX_DHCP_OPTION_LENGTH;
+  std::vector<char> optionValue;
+
+  bool retryingAfterLossOfSignificantData = false;
+  do {
+    optionValue.resize(sizeOptionValue);
+    rv = RetrieveOption(networkAdapterName,
+            aOption,
+            optionValue,
+            &sizeOptionValue,
+            mNetworkFunctions);
+    if (rv == NS_ERROR_LOSS_OF_SIGNIFICANT_DATA) {
+      LOG(("In nsWindowsDHCPClient::GetOption, DHCP Option %d required %d bytes", aOption, sizeOptionValue));
+      if (retryingAfterLossOfSignificantData) {
+        break;
+      }
+      retryingAfterLossOfSignificantData = true;
+    }
+  } while (rv == NS_ERROR_LOSS_OF_SIGNIFICANT_DATA);
+  if (rv != NS_OK) {
+    LOG(("Failed to get DHCP Option %d nsWindowsDHCPClient::GetOption due to error %d", aOption, rv));
+    return rv;
+  }
+  aRetVal.Assign(optionValue.data(), sizeOptionValue);
+  return NS_OK;
+}
+
+#define NS_WINDOWSDHCPCLIENTSERVICE_CID  /* {FEBF1D69-4D7D-4891-9524-045AD18B5592} */\
+    { 0xFEBF1D69, 0x4D7D, 0x4891, \
+         {0x95, 0x24, 0x04, 0x5a, 0xd1, 0x8b, 0x55, 0x92 } }
+
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsWindowsDHCPClient, Init)
+NS_DEFINE_NAMED_CID(NS_WINDOWSDHCPCLIENTSERVICE_CID);
+
+static const mozilla::Module::CIDEntry kSysDHCPClientCIDs[] = {
+  { &kNS_WINDOWSDHCPCLIENTSERVICE_CID, false, nullptr, nsWindowsDHCPClientConstructor },
+  { nullptr }
+};
+
+static const mozilla::Module::ContractIDEntry kSysDHCPClientContracts[] = {
+  { NS_DHCPCLIENT_CONTRACTID, &kNS_WINDOWSDHCPCLIENTSERVICE_CID },
+  { nullptr }
+};
+
+static const mozilla::Module kSysDHCPClientModule = {
+  mozilla::Module::kVersion,
+  kSysDHCPClientCIDs,
+  kSysDHCPClientContracts
+};
+
+NSMODULE_DEFN(nsDHCPClientModule) = &kSysDHCPClientModule;
+
+} // namespace windowsDHCPClient
+} // namespace system
+} // namespace toolkit
+} // namespace mozilla
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/toolkit/system/windowsDHCPClient/nsWindowsDHCPClient.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "nsIDHCPClient.h"
+#include "nsIServiceManager.h"
+#include "nsNetCID.h"
+#include "WindowsNetworkFunctionsWrapper.h"
+
+namespace mozilla {
+namespace toolkit {
+namespace system {
+namespace windowsDHCPClient {
+
+class nsWindowsDHCPClient final : public nsIDHCPClient
+{
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSIDHCPCLIENT
+
+  explicit nsWindowsDHCPClient(WindowsNetworkFunctionsWrapper *aNetworkFunctions = new WindowsNetworkFunctionsWrapper())
+    : mNetworkFunctions(aNetworkFunctions) {};
+  nsresult Init();
+
+private:
+
+   ~nsWindowsDHCPClient() {};
+   WindowsNetworkFunctionsWrapper* mNetworkFunctions;
+
+};
+
+
+} // namespace windowsDHCPClient
+} // namespace system
+} // namespace toolkit
+} // namespace mozilla
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/toolkit/system/windowsDHCPClient/tests/gtest/TestDHCPUtils.cpp
@@ -0,0 +1,316 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "DHCPUtils.h"
+#include "gtest/gtest.h"
+#include "nsString.h"
+#include "nsWindowsDHCPClient.h"
+
+using namespace mozilla::toolkit::system::windowsDHCPClient;
+
+class WindowsNetworkFunctionsMock : public WindowsNetworkFunctionsWrapper {
+
+  public:
+    WindowsNetworkFunctionsMock():mAddressesToReturn(nullptr) {
+      memset(mOptions, 0, sizeof(char*) * 256);
+    }
+
+    ULONG GetAdaptersAddressesWrapped(
+      _In_    ULONG                 Family,
+      _In_    ULONG                 Flags,
+      _In_    PVOID                 Reserved,
+      _Inout_ PIP_ADAPTER_ADDRESSES AdapterAddresses,
+      _Inout_ PULONG                SizePointer
+    ){
+      if (*SizePointer < sizeof(*mAddressesToReturn)){
+        *SizePointer = sizeof(*mAddressesToReturn);
+        return ERROR_BUFFER_OVERFLOW;
+      }
+
+      *SizePointer = sizeof(*mAddressesToReturn);
+      memcpy(AdapterAddresses, mAddressesToReturn,
+                    *SizePointer);
+      return 0;
+    }
+
+    DWORD DhcpRequestParamsWrapped(
+      _In_    DWORD                 Flags,
+      _In_    LPVOID                Reserved,
+      _In_    LPWSTR                AdapterName,
+      _In_    LPDHCPCAPI_CLASSID    ClassId,
+      _In_    DHCPCAPI_PARAMS_ARRAY SendParams,
+      _Inout_ DHCPCAPI_PARAMS_ARRAY RecdParams,
+      _In_    LPBYTE                Buffer,
+      _Inout_ LPDWORD               pSize,
+      _In_    LPWSTR                RequestIdStr
+    )
+    {
+      mLastRequestedNetworkAdapterName.Assign(AdapterName);
+
+      if (mOptions[RecdParams.Params[0].OptionId] == nullptr) {
+        RecdParams.Params[0].nBytesData = 0;
+      }
+      else {
+        RecdParams.Params[0].Data = Buffer;
+        size_t lengthOfValue = strlen(mOptions[RecdParams.Params[0].OptionId]);
+        if (*pSize > lengthOfValue) {
+          memcpy(Buffer, mOptions[RecdParams.Params[0].OptionId], lengthOfValue);
+          RecdParams.Params[0].nBytesData = lengthOfValue;
+        } else {
+          *pSize = lengthOfValue;
+          return ERROR_MORE_DATA;
+        }
+      }
+      return 0;
+    }
+
+    void
+    AddAdapterAddresses(IP_ADAPTER_ADDRESSES& aAddressesToAdd)
+    {
+      if (mAddressesToReturn == nullptr) {
+        mAddressesToReturn = &aAddressesToAdd;
+        return;
+      }
+      IP_ADAPTER_ADDRESSES* tail = mAddressesToReturn;
+
+      while (tail->Next != nullptr) {
+        tail = tail->Next;
+      }
+      tail->Next = &aAddressesToAdd;
+    }
+
+    void
+    SetDHCPOption(uint8_t option, char* value)
+    {
+      mOptions[option] = value;
+    }
+
+    nsString
+    GetLastRequestedNetworkAdapterName()
+    {
+      return mLastRequestedNetworkAdapterName;
+    }
+
+  private:
+    IP_ADAPTER_ADDRESSES* mAddressesToReturn = nullptr;
+    char* mOptions[256];
+    nsString mLastRequestedNetworkAdapterName;
+};
+
+class TestDHCPUtils : public ::testing::Test {
+  protected:
+    RefPtr<WindowsNetworkFunctionsMock> mMockWindowsFunctions;
+    nsCString mDefaultAdapterName;
+
+    virtual void
+    SetUp()
+    {
+      mMockWindowsFunctions = new WindowsNetworkFunctionsMock();
+      mDefaultAdapterName.AssignLiteral("my favourite network adapter");
+    }
+
+    void
+    Given_DHCP_Option_Is(uint8_t option, char* value)
+    {
+      mMockWindowsFunctions.get()->SetDHCPOption(option, value);
+    }
+
+    void
+    Given_Network_Adapter_Called(
+      IP_ADAPTER_ADDRESSES& adapterAddresses,
+      char* adapterName)
+    {
+      adapterAddresses.AdapterName = adapterName;
+      adapterAddresses.Next = nullptr;
+      adapterAddresses.Dhcpv4Server.iSockaddrLength = 0;
+      adapterAddresses.Dhcpv6Server.iSockaddrLength = 0;
+      AddAdapterAddresses(adapterAddresses);
+    }
+
+    void
+    Given_Network_Adapter_Supports_DHCP_V4(IP_ADAPTER_ADDRESSES& adapterAddresses)
+    {
+      adapterAddresses.Dhcpv4Server.iSockaddrLength = 4;
+    }
+
+    void
+    Given_Network_Adapter_Supports_DHCP_V6(IP_ADAPTER_ADDRESSES& adapterAddresses)
+    {
+      adapterAddresses.Dhcpv6Server.iSockaddrLength = 12;
+    }
+
+    void
+    Given_Network_Adapter_Has_Operational_Status(
+        IP_ADAPTER_ADDRESSES& adapterAddresses,
+        IF_OPER_STATUS operStatus)
+    {
+      adapterAddresses.OperStatus = operStatus;
+    }
+
+  private:
+    void
+    AddAdapterAddresses(IP_ADAPTER_ADDRESSES& aAddressToAdd)
+    {
+      mMockWindowsFunctions.get()->AddAdapterAddresses(aAddressToAdd);
+    }
+
+};
+
+// following class currently just distinguishes tests of nsWindowsDHCPClient from
+// tests of DHCPUtils.
+class TestNsWindowsDHCPClient : public TestDHCPUtils { };
+
+
+TEST_F(TestDHCPUtils, TestGetAdaptersAddresses)
+{
+  IP_ADAPTER_ADDRESSES adapterAddresses = {};
+  Given_Network_Adapter_Called(adapterAddresses, "my favourite network adapter");
+  Given_Network_Adapter_Supports_DHCP_V4(adapterAddresses);
+  Given_Network_Adapter_Has_Operational_Status(adapterAddresses, IfOperStatusUp);
+
+  nsCString networkAdapterName;
+
+  ASSERT_EQ(NS_OK, GetActiveDHCPNetworkAdapterName(networkAdapterName, mMockWindowsFunctions));
+
+  ASSERT_STREQ(networkAdapterName.Data(), "my favourite network adapter");
+}
+
+TEST_F(TestDHCPUtils, TestGetAdaptersAddressesNoAvailableNetworks)
+{
+  IP_ADAPTER_ADDRESSES adapterAddresses = {};
+  Given_Network_Adapter_Called(adapterAddresses, "my favourite network adapter");
+  Given_Network_Adapter_Supports_DHCP_V4(adapterAddresses);
+  Given_Network_Adapter_Has_Operational_Status(adapterAddresses, IfOperStatusDown);
+
+  nsCString networkAdapterName;
+  ASSERT_EQ(NS_ERROR_NOT_AVAILABLE, GetActiveDHCPNetworkAdapterName(networkAdapterName, mMockWindowsFunctions));
+
+  ASSERT_STREQ(networkAdapterName.Data(), "");
+}
+
+TEST_F(TestDHCPUtils, TestGetAdaptersAddressesNoNetworksWithDHCP)
+{
+  IP_ADAPTER_ADDRESSES adapterAddresses = {};
+  Given_Network_Adapter_Called(adapterAddresses, "my favourite network adapter");
+  Given_Network_Adapter_Has_Operational_Status(adapterAddresses, IfOperStatusUp);
+
+  nsCString networkAdapterName;
+  ASSERT_EQ(NS_ERROR_NOT_AVAILABLE, GetActiveDHCPNetworkAdapterName(networkAdapterName, mMockWindowsFunctions));
+
+  ASSERT_STREQ(networkAdapterName.Data(), "");
+}
+
+TEST_F(TestDHCPUtils, TestGetAdaptersAddressesSecondNetworkIsAvailable)
+{
+  IP_ADAPTER_ADDRESSES adapterAddresses = {};
+  Given_Network_Adapter_Called(adapterAddresses, "my favourite network adapter");
+  Given_Network_Adapter_Supports_DHCP_V4(adapterAddresses);
+  Given_Network_Adapter_Has_Operational_Status(adapterAddresses, IfOperStatusDown);
+
+
+  IP_ADAPTER_ADDRESSES secondAdapterAddresses = {};
+  Given_Network_Adapter_Called(secondAdapterAddresses, "my second favourite network adapter");
+  Given_Network_Adapter_Supports_DHCP_V6(secondAdapterAddresses);
+  Given_Network_Adapter_Has_Operational_Status(secondAdapterAddresses, IfOperStatusUp);
+
+  nsCString networkAdapterName;
+  ASSERT_EQ(NS_OK, GetActiveDHCPNetworkAdapterName(networkAdapterName, mMockWindowsFunctions));
+
+  ASSERT_STREQ(networkAdapterName.Data(), "my second favourite network adapter");
+}
+
+TEST_F(TestDHCPUtils, TestGetOption)
+{
+
+  char* pacURL = "http://pac.com";
+  Given_DHCP_Option_Is(1, "My network option");
+  Given_DHCP_Option_Is(252, pacURL);
+
+  std::vector<char> optionValue(255, *"originalValue originalValue");
+  memcpy(optionValue.data(), "originalValue originalValue", strlen("originalValue originalValue") + 1);
+
+  uint32_t size = 255;
+
+  nsresult retVal = RetrieveOption(mDefaultAdapterName, 252, optionValue, &size, mMockWindowsFunctions);
+
+  ASSERT_EQ(strlen(pacURL), size);
+  ASSERT_STREQ("http://pac.comoriginalValue", optionValue.data());
+  ASSERT_EQ(NS_OK, retVal);
+}
+
+TEST_F(TestDHCPUtils, TestGetAbsentOption)
+{
+  std::vector<char> optionValue(255);
+  uint32_t size = 256;
+  memcpy(optionValue.data(), "originalValue", strlen("originalValue") + 1);
+
+  nsresult retVal = RetrieveOption(mDefaultAdapterName, 252, optionValue, &size, mMockWindowsFunctions);
+
+  ASSERT_EQ(0, size);
+  ASSERT_EQ(NS_ERROR_NOT_AVAILABLE, retVal);
+}
+
+TEST_F(TestDHCPUtils, TestGetTooLongOption)
+{
+  Given_DHCP_Option_Is(252, "http://pac.com");
+
+  std::vector<char> optionValue(255);
+  memcpy(optionValue.data(), "originalValue", strlen("originalValue") + 1);
+  uint32_t size = 4;
+  nsresult retVal = RetrieveOption(mDefaultAdapterName, 252, optionValue, &size, mMockWindowsFunctions);
+
+  ASSERT_STREQ("httpinalValue", optionValue.data());
+  ASSERT_EQ(NS_ERROR_LOSS_OF_SIGNIFICANT_DATA, retVal);
+  ASSERT_EQ(strlen("http://pac.com"), size);
+}
+
+TEST_F(TestNsWindowsDHCPClient, TestGettingOptionThroughNSWindowsDHCPClient)
+{
+  IP_ADAPTER_ADDRESSES adapterAddresses = {};
+  Given_Network_Adapter_Called(adapterAddresses, "my favourite network adapter");
+  Given_Network_Adapter_Supports_DHCP_V4(adapterAddresses);
+  Given_Network_Adapter_Has_Operational_Status(adapterAddresses, IfOperStatusUp);
+  Given_DHCP_Option_Is(252, "http://pac.com");
+
+  nsCString optionValue;
+  nsCOMPtr<nsIDHCPClient> dhcpClient = new nsWindowsDHCPClient(mMockWindowsFunctions);
+  nsresult retVal = dhcpClient->GetOption(252, optionValue);
+
+  ASSERT_STREQ("http://pac.com", optionValue.Data());
+  ASSERT_STREQ(L"my favourite network adapter", mMockWindowsFunctions->GetLastRequestedNetworkAdapterName().Data());
+  ASSERT_EQ(NS_OK, retVal);
+}
+
+TEST_F(TestNsWindowsDHCPClient, TestGettingOptionThroughNSWindowsDHCPClientWhenNoAvailableNetworkAdapter)
+{
+  IP_ADAPTER_ADDRESSES adapterAddresses = {};
+  Given_Network_Adapter_Called(adapterAddresses, "my favourite network adapter");
+  Given_Network_Adapter_Has_Operational_Status(adapterAddresses, IfOperStatusDown);
+  Given_DHCP_Option_Is(252, "http://pac.com");
+
+  nsCString optionValue;
+  nsCOMPtr<nsIDHCPClient> dhcpClient = new nsWindowsDHCPClient(mMockWindowsFunctions);
+  nsresult retVal = dhcpClient->GetOption(252, optionValue);
+
+  ASSERT_STREQ("", optionValue.Data());
+  ASSERT_EQ(NS_ERROR_NOT_AVAILABLE, retVal);
+}
+
+TEST_F(TestNsWindowsDHCPClient, TestGettingAbsentOptionThroughNSWindowsDHCPClient)
+{
+  IP_ADAPTER_ADDRESSES adapterAddresses = {};
+  Given_Network_Adapter_Called(adapterAddresses, "my favourite network adapter");
+  Given_Network_Adapter_Supports_DHCP_V6(adapterAddresses);
+  Given_Network_Adapter_Has_Operational_Status(adapterAddresses, IfOperStatusUp);
+
+  nsCString optionValue;
+  nsCOMPtr<nsIDHCPClient> dhcpClient = new nsWindowsDHCPClient(mMockWindowsFunctions);
+  nsresult retVal = dhcpClient->GetOption(252, optionValue);
+
+  ASSERT_STREQ("", optionValue.Data());
+  ASSERT_EQ(NS_ERROR_NOT_AVAILABLE, retVal);
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/toolkit/system/windowsDHCPClient/tests/gtest/moz.build
@@ -0,0 +1,21 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+with Files('**'):
+    BUG_COMPONENT = ('Core', 'Networking: HTTP')
+
+UNIFIED_SOURCES += [
+    'TestDHCPUtils.cpp',
+]
+
+LOCAL_INCLUDES += [
+    '/toolkit/system/windowsDHCPClient',
+]
+
+FINAL_LIBRARY = 'xul-gtest'
+
+if CONFIG['GNU_CXX']:
+    CXXFLAGS += ['-Wshadow']
\ No newline at end of file