--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -2278,16 +2278,17 @@ pref("network.cookie.cookieBehavior",
pref("network.cookie.thirdparty.sessionOnly", false);
pref("network.cookie.thirdparty.nonsecureSessionOnly", false);
pref("network.cookie.leave-secure-alone", true);
pref("network.cookie.ipc.sync", false);
pref("network.cookie.lifetimePolicy", 0); // 0-accept, 1-dontUse 2-acceptForSession, 3-acceptForNDays
pref("network.cookie.prefsMigrated", false);
pref("network.cookie.lifetime.days", 90); // Ignored unless network.cookie.lifetimePolicy is 3.
+pref("network.proxy.enable_wpad", true);
// The PAC file to load. Ignored unless network.proxy.type is 2.
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
--- 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,40 @@
/* -*- 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 "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
@@ -87,22 +91,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 +190,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 +288,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);
+ NS_DispatchToMainThread(runnable);
+ 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;
+ nsCString mSetupPACData;
+ nsCString mSetupPACURI;
+ bool mConfigureWPAD;
};
//-----------------------------------------------------------------------------
PendingPACQuery::PendingPACQuery(nsPACMan* pacMan,
nsIURI* uri,
nsPACManCallback* callback,
bool mainThreadResponse)
@@ -283,30 +332,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());
@@ -331,16 +380,18 @@ 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)
+ , mWPADEnabled(false)
{
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);
@@ -390,17 +441,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(EmptyCString(), true);
}
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());
@@ -423,26 +474,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, bool aIsScheduledReload)
{
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, aIsScheduledReload:%s\n", aSpec.BeginReading(), aIsScheduledReload? "true" : "false"));
// 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 =
@@ -453,40 +503,108 @@ nsPACMan::LoadPACFromURI(const nsCString
if (NS_FAILED(rv))
return rv;
mLoadPending = true;
}
CancelExistingLoad();
mLoader = loader;
- if (!spec.IsEmpty()) {
- mPACURISpec = spec;
+ if (!aIsScheduledReload) {
mPACURIRedirectSpec.Truncate();
mNormalPACURISpec.Truncate(); // set at load time
mLoadFailureCount = 0; // reset
+ mAutoDetect = aSpec.IsEmpty() && mWPADEnabled;
+ 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");
+ aSpec.Truncate();
+ 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) {
+ MOZ_ASSERT(mWPADEnabled);
+ 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);
@@ -793,16 +911,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
--- 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); /* Runnable */
@@ -126,17 +127,17 @@ public:
* 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
* 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, bool aIsScheduledReload = false);
/**
* 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 SetWPADEnabled(bool aValue) { mWPADEnabled = 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,18 @@ private:
nsCOMPtr<nsIStreamLoader> mLoader;
bool mLoadPending;
Atomic<bool, Relaxed> mShutdown;
TimeStamp mScheduledReload;
uint32_t mLoadFailureCount;
bool mInProgress;
bool mIncludePath;
+ bool mAutoDetect;
+ bool mWPADEnabled;
};
extern LazyLogModule gProxyLog;
} // namespace net
} // namespace mozilla
#endif // nsPACMan_h__
--- a/netwerk/base/nsProtocolProxyService.cpp
+++ b/netwerk/base/nsProtocolProxyService.cpp
@@ -56,17 +56,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;
@@ -194,18 +193,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;
@@ -495,16 +494,17 @@ nsProtocolProxyService::nsProtocolProxyS
, mProxyConfig(PROXYCONFIG_DIRECT)
, mHTTPProxyPort(-1)
, mFTPProxyPort(-1)
, mHTTPSProxyPort(-1)
, mSOCKSProxyPort(-1)
, mSOCKSProxyVersion(4)
, mSOCKSProxyRemoteDNS(false)
, mProxyOverTLS(true)
+ , mWPADEnabled(false)
, mPACMan(nullptr)
, mSessionStart(PR_Now())
, mFailedProxyTimeout(30 * 60) // 30 minute default
, mIsShutdown(false)
{
}
nsProtocolProxyService::~nsProtocolProxyService()
@@ -765,16 +765,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"))) {
+ proxy_GetBoolPref(prefBranch, PROXY_PREF("enable_wpad"),
+ mWPADEnabled);
+ reloadPAC = true;
+ }
+
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);
@@ -797,29 +803,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())
- ConfigureFromPAC(tempString, false);
+ if (!tempString.IsEmpty() || mProxyConfig == PROXYCONFIG_WPAD) {
+ ConfigureFromPAC(tempString, true);
+ }
}
}
bool
nsProtocolProxyService::CanUseProxy(nsIURI *aURI, int32_t defaultPort)
{
if (mHostFiltersArray.Length() == 0 && !mFilterLocalHosts)
return true;
@@ -1160,19 +1162,17 @@ 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
nsProtocolProxyService::ResetPACThread()
{
@@ -1186,21 +1186,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->SetWPADEnabled(mWPADEnabled);
return mPACMan->LoadPACFromURI(spec);
}
void
nsProtocolProxyService::ProcessPACString(const nsCString &pacString,
uint32_t aResolveFlags,
nsIProxyInfo **result)
{
@@ -1243,27 +1247,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 -
@@ -1277,18 +1279,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();
}
--- a/netwerk/base/nsProtocolProxyService.h
+++ b/netwerk/base/nsProtocolProxyService.h
@@ -389,16 +389,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 mWPADEnabled;
RefPtr<nsPACMan> mPACMan; // non-null if we are using PAC
nsCOMPtr<nsISystemProxySettings> mSystemProxySettings;
PRTime mSessionStart;
nsFailedProxyTable mFailedProxies;
int32_t mFailedProxyTimeout;
--- a/netwerk/build/nsNetCID.h
+++ b/netwerk/build/nsNetCID.h
@@ -318,16 +318,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,270 @@
+#include "gtest/gtest.h"
+#include "nsServiceManagerUtils.h"
+#include "../../../xpcom/threads/nsThreadManager.h"
+#include "nsIDHCPClient.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"
+nsCString WPADOptionResult;
+
+namespace mozilla {
+namespace net {
+
+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:
+ // by default we just process the queue
+ 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 exists 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()
+ {
+ 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->SetWPADEnabled(true);
+ mPACMan->Init(nullptr);
+ }
+
+ virtual void
+ TearDown()
+ {
+ mPACMan->Shutdown();
+ }
+
+ 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:
+
+ 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, WhenPACUriIsSetDirectlyItIsUsedRatherThanWPAD) {
+
+ SetOptionResult(TEST_WPAD_DHCP_OPTION);
+
+ nsCString spec;
+ spec.AssignLiteral(TEST_ASSIGNED_PAC_URL);
+
+ mPACMan->LoadPACFromURI(spec);
+
+ ProcessAllEventsTenTimes();
+
+ AssertPACSpecEqualTo(TEST_ASSIGNED_PAC_URL);
+
+}
+
+TEST_F(TestPACMan, WhenAScheduledReloadOfAssignedPACHappensTheAssignedPACSpecStaysRatherThanReadingFromDHCP) {
+
+ SetOptionResult(TEST_WPAD_DHCP_OPTION);
+ nsCString spec;
+ spec.AssignLiteral(TEST_ASSIGNED_PAC_URL);
+ mPACMan->LoadPACFromURI(spec);
+
+ ProcessAllEventsTenTimes();
+
+ mPACMan->LoadPACFromURI(EmptyCString(), true);
+
+ ProcessAllEventsTenTimes();
+
+ AssertPACSpecEqualTo(TEST_ASSIGNED_PAC_URL);
+
+}
+
+} // namespace net
+} // namespace mozilla
--- a/netwerk/test/gtest/moz.build
+++ b/netwerk/test/gtest/moz.build
@@ -4,16 +4,17 @@
# 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/.
UNIFIED_SOURCES += [
'TestBufferedInputStream.cpp',
'TestHeaders.cpp',
'TestHttpAuthUtils.cpp',
'TestMozURL.cpp',
+ 'TestPACMan.cpp',
'TestPartiallySeekableInputStream.cpp',
'TestProtocolProxyService.cpp',
'TestReadStreamToString.cpp',
'TestServerTimingHeader.cpp',
'TestStandardURL.cpp',
'TestURIMutator.cpp',
]
@@ -24,8 +25,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'
+]
--- a/toolkit/moz.build
+++ b/toolkit/moz.build
@@ -35,17 +35,18 @@ if CONFIG['MOZ_MAINTENANCE_SERVICE']:
DIRS += ['xre']
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', '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,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
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
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
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
new file mode 100644
--- /dev/null
+++ b/toolkit/system/windowsDHCPClient/moz.build
@@ -0,0 +1,15 @@
+# -*- 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/.
+
+TEST_DIRS += ['tests/gtest']
+
+SOURCES += [
+ 'DHCPUtils.cpp',
+ 'nsWindowsDHCPClient.cpp',
+ 'WindowsNetworkFunctionsWrapper.cpp'
+]
+
+FINAL_LIBRARY = 'xul'
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
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
new file mode 100644
--- /dev/null
+++ b/toolkit/system/windowsDHCPClient/tests/gtest/TestDHCPUtils.cpp
@@ -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);
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/system/windowsDHCPClient/tests/gtest/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/.
+
+UNIFIED_SOURCES += [
+ 'TestDHCPUtils.cpp',
+]
+
+LOCAL_INCLUDES += [
+ '/toolkit/system/windowsDHCPClient',
+]
+
+FINAL_LIBRARY = 'xul-gtest'
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wshadow']