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

--HG--
extra : rebase_source : 4fb364432625418acf743b67a2caadc5a92caf7c
This commit is contained in:
Polly Shaw 2018-06-07 23:07:28 +01:00
parent bb9821b6c8
commit b18d1fbc4a
20 changed files with 1449 additions and 61 deletions

View File

@ -2229,6 +2229,7 @@ pref("network.proxy.autoconfig_url.include_path", false);
// 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);

View File

@ -38,6 +38,7 @@ XPIDL_SOURCES += [
'nsIDashboard.idl',
'nsIDashboardEventNotifier.idl',
'nsIDeprecationWarner.idl',
'nsIDHCPClient.idl',
'nsIDivertableChannel.idl',
'nsIDownloader.idl',
'nsIEncodedChannel.idl',

View File

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

View File

@ -5,17 +5,20 @@
* 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"
//-----------------------------------------------------------------------------
@ -26,6 +29,8 @@ 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
@ -75,6 +80,26 @@ GetExtraJSContextHeapSize()
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;
}
//-----------------------------------------------------------------------------
@ -92,12 +117,12 @@ public:
{
}
void SetPACString(const nsCString &pacString)
void SetPACString(const nsACString &pacString)
{
mPACString = pacString;
}
void SetPACURL(const nsCString &pacURL)
void SetPACURL(const nsACString &pacURL)
{
mPACURL = pacURL;
}
@ -191,14 +216,43 @@ public:
}
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
{
@ -211,6 +265,7 @@ public:
, mCancelStatus(NS_OK)
, mSetupPAC(false)
, mExtraHeapSize(0)
, mConfigureWPAD(false)
{ }
void CancelQueue (nsresult status)
@ -221,7 +276,7 @@ public:
void SetupPAC (const char *text,
uint32_t datalen,
nsCString &pacURI,
const nsACString &pacURI,
uint32_t extraHeapSize)
{
mSetupPAC = true;
@ -230,6 +285,11 @@ public:
mExtraHeapSize = extraHeapSize;
}
void ConfigureWPAD()
{
mConfigureWPAD = true;
}
NS_IMETHOD Run() override
{
MOZ_ASSERT(!NS_IsMainThread(), "wrong thread");
@ -254,6 +314,15 @@ public:
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;
}
@ -268,6 +337,7 @@ private:
uint32_t mExtraHeapSize;
nsCString mSetupPACData;
nsCString mSetupPACURI;
bool mConfigureWPAD;
};
//-----------------------------------------------------------------------------
@ -289,7 +359,7 @@ PendingPACQuery::PendingPACQuery(nsPACMan* pacMan,
}
void
PendingPACQuery::Complete(nsresult status, const nsCString &pacString)
PendingPACQuery::Complete(nsresult status, const nsACString &pacString)
{
if (!mCallback)
return;
@ -302,7 +372,7 @@ PendingPACQuery::Complete(nsresult status, const nsCString &pacString)
}
void
PendingPACQuery::UseAlternatePACFile(const nsCString &pacURL)
PendingPACQuery::UseAlternatePACFile(const nsACString &pacURL)
{
if (!mCallback)
return;
@ -337,6 +407,9 @@ nsPACMan::nsPACMan(nsIEventTarget *mainThreadEventTarget)
, 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){
@ -354,6 +427,7 @@ nsPACMan::~nsPACMan()
if (mPACThread) {
if (NS_IsMainThread()) {
mPACThread->Shutdown();
mPACThread = nullptr;
}
else {
RefPtr<ShutdownThread> runnable = new ShutdownThread(mPACThread);
@ -396,7 +470,7 @@ nsPACMan::AsyncGetProxyForURI(nsIURI *uri,
TimeStamp::Now() > mScheduledReload) {
LOG(("nsPACMan::AsyncGetProxyForURI reload as scheduled\n"));
LoadPACFromURI(EmptyCString());
LoadPACFromURI(mAutoDetect? EmptyCString(): mPACURISpec);
}
RefPtr<PendingPACQuery> query =
@ -429,16 +503,15 @@ nsPACMan::PostQuery(PendingPACQuery *query)
}
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.
//
@ -459,18 +532,67 @@ nsPACMan::LoadPACFromURI(const nsCString &spec)
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()
{
@ -483,6 +605,29 @@ nsPACMan::StartLoading()
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();
@ -800,6 +945,8 @@ nsresult
nsPACMan::Init(nsISystemProxySettings *systemProxySettings)
{
mSystemProxySettings = systemProxySettings;
mDHCPClient = do_GetService(NS_DHCPCLIENT_CONTRACTID);
nsresult rv =
NS_NewNamedThread("ProxyResolution", getter_AddRefs(mPACThread));

View File

@ -7,23 +7,24 @@
#ifndef nsPACMan_h__
#define nsPACMan_h__
#include "nsIStreamLoader.h"
#include "nsIInterfaceRequestor.h"
#include "nsIChannelEventSink.h"
#include "ProxyAutoConfig.h"
#include "nsThreadUtils.h"
#include "nsIURI.h"
#include "nsCOMPtr.h"
#include "nsString.h"
#include "mozilla/Atomics.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 "mozilla/TimeStamp.h"
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "nsIChannelEventSink.h"
#include "nsIInterfaceRequestor.h"
#include "nsIStreamLoader.h"
#include "nsThreadUtils.h"
#include "nsIURI.h"
#include "nsString.h"
#include "ProxyAutoConfig.h"
class nsISystemProxySettings;
class nsIDHCPClient;
class nsIThread;
namespace mozilla {
@ -52,8 +53,8 @@ public:
* 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,
@ -65,8 +66,8 @@ public:
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;
@ -127,11 +128,11 @@ public:
* 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.
@ -166,6 +167,10 @@ public:
return IsPACURI(tmp);
}
bool IsUsingWPAD() {
return mAutoDetect;
}
nsresult Init(nsISystemProxySettings *);
static nsPACMan *sInstance;
@ -173,6 +178,8 @@ public:
void ProcessPendingQ();
void CancelPendingQ(nsresult);
void SetWPADOverDHCPEnabled(bool aValue) { mWPADOverDHCPEnabled = aValue; }
private:
NS_DECL_NSISTREAMLOADEROBSERVER
NS_DECL_NSIINTERFACEREQUESTOR
@ -180,8 +187,10 @@ private:
friend class PendingPACQuery;
friend class PACLoadComplete;
friend class ConfigureWPADComplete;
friend class ExecutePACThreadAction;
friend class WaitForThreadShutdown;
friend class TestPACMan;
~nsPACMan();
@ -195,6 +204,11 @@ private:
*/
void StartLoading();
/**
* Continue loading the PAC file.
*/
void ContinueLoadingAfterPACUriKnown();
/**
* Reload the PAC file if there is reason to.
*/
@ -212,15 +226,22 @@ private:
*/
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 */
@ -239,6 +260,9 @@ private:
bool mInProgress;
bool mIncludePath;
bool mAutoDetect;
bool mWPADOverDHCPEnabled;
int32_t mProxyConfigType;
};
extern LazyLogModule gProxyLog;

View File

@ -62,7 +62,6 @@ namespace net {
#define PROXY_PREF_BRANCH "network.proxy"
#define PROXY_PREF(x) PROXY_PREF_BRANCH "." x
#define WPAD_URL "http://wpad/wpad.dat"
//----------------------------------------------------------------------------
@ -311,8 +310,8 @@ 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)
@ -821,6 +820,7 @@ nsProtocolProxyService::nsProtocolProxyService()
, mSOCKSProxyVersion(4)
, mSOCKSProxyRemoteDNS(false)
, mProxyOverTLS(true)
, mWPADOverDHCPEnabled(false)
, mPACMan(nullptr)
, mSessionStart(PR_Now())
, mFailedProxyTimeout(30 * 60) // 30 minute default
@ -1087,6 +1087,12 @@ nsProtocolProxyService::PrefsChanged(nsIPrefBranch *prefBranch,
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);
@ -1119,19 +1125,15 @@ nsProtocolProxyService::PrefsChanged(nsIPrefBranch *prefBranch,
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);
}
}
}
@ -1482,7 +1484,6 @@ nsProtocolProxyService::SetupPACThread(nsIEventTarget *mainThreadEventTarget)
else {
rv = mPACMan->Init(nullptr);
}
if (NS_FAILED(rv)) {
mPACMan->Shutdown();
mPACMan = nullptr;
@ -1508,11 +1509,15 @@ nsProtocolProxyService::ConfigureFromPAC(const nsCString &spec,
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);
}
@ -1565,8 +1570,6 @@ nsProtocolProxyService::ReloadPAC()
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);
@ -1575,7 +1578,7 @@ nsProtocolProxyService::ReloadPAC()
}
}
if (!pacSpec.IsEmpty())
if (!pacSpec.IsEmpty() || type == PROXYCONFIG_WPAD)
ConfigureFromPAC(pacSpec, true);
return NS_OK;
}
@ -1599,8 +1602,8 @@ class nsAsyncBridgeRequest final : public nsPACManCallback
}
void OnQueryComplete(nsresult status,
const nsCString &pacString,
const nsCString &newPACURL) override
const nsACString &pacString,
const nsACString &newPACURL) override
{
MutexAutoLock lock(mMutex);
mCompleted = true;
@ -2546,4 +2549,4 @@ nsProtocolProxyService::PruneProxyInfo(const nsProtocolInfo &info,
}
} // namespace net
} // namespace mozilla
} // namespace mozilla

View File

@ -400,6 +400,7 @@ protected:
int32_t mSOCKSProxyVersion;
bool mSOCKSProxyRemoteDNS;
bool mProxyOverTLS;
bool mWPADOverDHCPEnabled;
RefPtr<nsPACMan> mPACMan; // non-null if we are using PAC
nsCOMPtr<nsISystemProxySettings> mSystemProxySettings;

View File

@ -335,6 +335,10 @@
#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"

View File

@ -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

View File

@ -10,6 +10,7 @@ UNIFIED_SOURCES += [
'TestHttpAuthUtils.cpp',
'TestMIMEInputStream.cpp',
'TestMozURL.cpp',
'TestPACMan.cpp',
'TestPartiallySeekableInputStream.cpp',
'TestProtocolProxyService.cpp',
'TestReadStreamToString.cpp',
@ -30,3 +31,8 @@ LOCAL_INCLUDES += [
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul-gtest'
LOCAL_INCLUDES += [
'!/xpcom',
'/xpcom/components'
]

View File

@ -40,7 +40,8 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gtk3':
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']

View File

@ -0,0 +1,256 @@
/* -*- 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 {
pAddresses.resize(outBufLen/sizeof(IP_ADAPTER_ADDRESSES));
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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,323 @@
/* -*- 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);
}

View File

@ -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']