summaryrefslogtreecommitdiffstats
path: root/toolkit/system/windowsDHCPClient
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/system/windowsDHCPClient')
-rw-r--r--toolkit/system/windowsDHCPClient/DHCPUtils.cpp247
-rw-r--r--toolkit/system/windowsDHCPClient/DHCPUtils.h31
-rw-r--r--toolkit/system/windowsDHCPClient/WindowsNetworkFunctionsWrapper.cpp41
-rw-r--r--toolkit/system/windowsDHCPClient/WindowsNetworkFunctionsWrapper.h48
-rw-r--r--toolkit/system/windowsDHCPClient/components.conf14
-rw-r--r--toolkit/system/windowsDHCPClient/moz.build22
-rw-r--r--toolkit/system/windowsDHCPClient/nsWindowsDHCPClient.cpp77
-rw-r--r--toolkit/system/windowsDHCPClient/nsWindowsDHCPClient.h34
-rw-r--r--toolkit/system/windowsDHCPClient/tests/gtest/TestDHCPUtils.cpp306
-rw-r--r--toolkit/system/windowsDHCPClient/tests/gtest/moz.build21
10 files changed, 841 insertions, 0 deletions
diff --git a/toolkit/system/windowsDHCPClient/DHCPUtils.cpp b/toolkit/system/windowsDHCPClient/DHCPUtils.cpp
new file mode 100644
index 0000000000..956c2adbf0
--- /dev/null
+++ b/toolkit/system/windowsDHCPClient/DHCPUtils.cpp
@@ -0,0 +1,247 @@
+/* -*- 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)
+// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365915(v=vs.85).aspx
+// 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 dwRetVal = 0;
+ nsresult rv = NS_ERROR_FAILURE;
+
+ // Set the flags to pass to GetAdaptersAddresses
+ uint32_t flags = GAA_FLAG_INCLUDE_PREFIX;
+
+ // default to unspecified address family (both)
+ uint32_t family = AF_UNSPEC;
+
+ // Allocate a 15 KB buffer to start with.
+ uint32_t outBufLen = MOZ_WORKING_BUFFER_SIZE_NETWORK_ADAPTERS;
+ uint32_t iterations = 0;
+
+ aNetworkAdapterName.Truncate();
+
+ // Now we try calling the GetAdaptersAddresses method until the return value
+ // is not ERROR_BUFFER_OVERFLOW. According to [1]
+ //
+ //
+ // > When the return value is ERROR_BUFFER_OVERFLOW, the SizePointer parameter
+ // returned > points to the required size of the buffer to hold the adapter
+ // information. > Note that it is possible for the buffer size required for
+ // the IP_ADAPTER_ADDRESSES > structures pointed to by the AdapterAddresses
+ // parameter to change between > subsequent calls to the GetAdaptersAddresses
+ // function if an adapter address > is added or removed. However, this method
+ // of using the GetAdaptersAddresses > function is strongly discouraged. This
+ // method requires calling the > GetAdaptersAddresses function multiple times.
+ // >
+ // > The recommended method of calling the GetAdaptersAddresses function is
+ // > to pre-allocate a 15KB working buffer pointed to by the AdapterAddresses
+ // parameter. > On typical computers, this dramatically reduces the chances
+ // that the > GetAdaptersAddresses function returns ERROR_BUFFER_OVERFLOW,
+ // which would require > calling GetAdaptersAddresses function multiple times.
+ //
+ //
+ // The possibility of the buffer size changing between calls to
+ // GetAdaptersAddresses is why we allow the following code to be called
+ // several times, rather than just the two that would be neccessary if we
+ // could rely on the value returned in outBufLen being the true size needed.
+
+ std::vector<IP_ADAPTER_ADDRESSES> pAddresses;
+ do {
+ // resize outBufLen up to the next multiple of sizeof(IP_ADAPTER_ADDRESSES)
+ outBufLen = ((outBufLen + sizeof(IP_ADAPTER_ADDRESSES) - 1) /
+ sizeof(IP_ADAPTER_ADDRESSES)) *
+ sizeof(IP_ADAPTER_ADDRESSES);
+ pAddresses.resize(outBufLen / sizeof(IP_ADAPTER_ADDRESSES));
+ LOG(
+ ("Trying GetAdaptersAddresses with pAddresses sized to %zu and buffer "
+ "length of %d",
+ pAddresses.size(), outBufLen));
+
+ dwRetVal = aWindowsNetworkFunctionsWrapper->GetAdaptersAddressesWrapped(
+ family, flags, nullptr, pAddresses.data(), (PULONG)&outBufLen);
+
+ if (dwRetVal == ERROR_BUFFER_OVERFLOW) {
+ iterations++;
+ }
+ } while (dwRetVal == ERROR_BUFFER_OVERFLOW && iterations < MOZ_MAX_TRIES);
+
+ switch (dwRetVal) {
+ case NO_ERROR: {
+ // set default return value if we don't find a suitable network adapter
+ rv = NS_ERROR_NOT_AVAILABLE;
+ PIP_ADAPTER_ADDRESSES pCurrAddresses = pAddresses.data();
+ while (pCurrAddresses) {
+ if (IsCurrentAndHasDHCP(pCurrAddresses)) {
+ rv = NS_OK;
+ aNetworkAdapterName.Assign(pCurrAddresses->AdapterName);
+ break;
+ }
+ pCurrAddresses = pCurrAddresses->Next;
+ }
+ } break;
+ case ERROR_NO_DATA:
+ rv = NS_ERROR_NOT_AVAILABLE;
+ break;
+ default:
+ MOZ_LOG(gDhcpUtilsLog, mozilla::LogLevel::Warning,
+ ("GetAdaptersAddresses returned %d", dwRetVal));
+ rv = NS_ERROR_FAILURE;
+ break;
+ }
+ return rv;
+}
+
+DWORD
+IterateDHCPInformRequestsUntilBufferLargeEnough(
+ DHCPCAPI_PARAMS& aDhcpRequestedOptionParams,
+ wchar_t* aWideNetworkAdapterName, std::vector<char>& aBuffer,
+ WindowsNetworkFunctionsWrapper* aWindowsNetworkFunctionsWrapper) {
+ uint32_t iterations = 0;
+ uint32_t outBufLen = MOZ_WORKING_BUFFER_SIZE_DHCP_PARAMS;
+
+ DHCPCAPI_PARAMS_ARRAY RequestParams = {1, // only one option to request
+ &aDhcpRequestedOptionParams};
+
+ // According to [2],
+ // the following is for 'Optional data to be requested,
+ // in addition to the data requested in the RecdParams array.'
+ // We are not requesting anything in addition, so this is empty.
+ DHCPCAPI_PARAMS_ARRAY SendParams = {0, nullptr};
+
+ DWORD winAPIResponse;
+ // Now we try calling the DHCPRequestParams method until the return value
+ // is not ERROR_MORE_DATA. According to [2]:
+ //
+ //
+ // > Note that the required size of Buffer may increase during the time that
+ // elapses > between the initial function call's return and a subsequent call;
+ // > therefore, the required size of Buffer (indicated in pSize)
+ // > provides an indication of the approximate size required of Buffer,
+ // > rather than guaranteeing that subsequent calls will return successfully
+ // > if Buffer is set to the size indicated in pSize.
+ //
+ //
+ // This is why we allow this DHCPRequestParams to be called several times,
+ // rather than just the two that would be neccessary if we could rely on the
+ // value returned in outBufLen being the true size needed.
+ do {
+ aBuffer.resize(outBufLen);
+
+ winAPIResponse = aWindowsNetworkFunctionsWrapper->DhcpRequestParamsWrapped(
+ DHCPCAPI_REQUEST_SYNCHRONOUS, // Flags
+ nullptr, // Reserved
+ aWideNetworkAdapterName, // Adapter Name
+ nullptr, // not using class id
+ SendParams, // sent parameters
+ RequestParams, // requesting params
+ (PBYTE)aBuffer.data(), // buffer for the output of RequestParams
+ (PULONG)&outBufLen, // buffer size
+ nullptr // Request ID for persistent requests - not needed here
+ );
+
+ if (winAPIResponse == ERROR_MORE_DATA) {
+ iterations++;
+ }
+ } while (winAPIResponse == ERROR_MORE_DATA && iterations < MOZ_MAX_TRIES);
+ return winAPIResponse;
+}
+
+nsresult RetrieveOption(
+ const nsACString& aAdapterName, uint8_t aOption,
+ std::vector<char>& aOptionValueBuf, uint32_t* aOptionSize,
+ WindowsNetworkFunctionsWrapper* aWindowsNetworkFunctionsWrapper) {
+ nsresult rv;
+ nsAutoString wideNetworkAdapterName = NS_ConvertUTF8toUTF16(aAdapterName);
+
+ DHCPCAPI_PARAMS DhcpRequestedOptionParams = {
+ 0, // Flags - Reserved, must be set to zero [2]
+ aOption, // OptionId
+ false, // whether this is vendor specific - let's assume not
+ nullptr, // data filled in on return
+ 0 // nBytes used by return data
+ };
+
+ std::vector<char> tmpBuffer(
+ MOZ_WORKING_BUFFER_SIZE_DHCP_PARAMS); // a buffer for the DHCP response
+ // object
+ DWORD winAPIResponse = IterateDHCPInformRequestsUntilBufferLargeEnough(
+ DhcpRequestedOptionParams, wideNetworkAdapterName.get(), tmpBuffer,
+ aWindowsNetworkFunctionsWrapper);
+
+ switch (winAPIResponse) {
+ case NO_ERROR: {
+ if (DhcpRequestedOptionParams.nBytesData == 0) {
+ *aOptionSize = 0;
+ rv = NS_ERROR_NOT_AVAILABLE;
+ break;
+ }
+
+ if (*aOptionSize >= DhcpRequestedOptionParams.nBytesData) {
+ rv = NS_OK;
+ } else {
+ rv = NS_ERROR_LOSS_OF_SIGNIFICANT_DATA;
+ }
+
+ uint32_t actualSizeReturned =
+ *aOptionSize > DhcpRequestedOptionParams.nBytesData
+ ? DhcpRequestedOptionParams.nBytesData
+ : *aOptionSize;
+
+ memcpy(aOptionValueBuf.data(), DhcpRequestedOptionParams.Data,
+ actualSizeReturned);
+ *aOptionSize = DhcpRequestedOptionParams.nBytesData;
+ break;
+ }
+ case ERROR_INVALID_PARAMETER:
+ MOZ_LOG(gDhcpUtilsLog, mozilla::LogLevel::Warning,
+ ("RetrieveOption returned %lu (ERROR_INVALID_PARAMETER) when "
+ "option %d requested",
+ winAPIResponse, aOption));
+ rv = NS_ERROR_INVALID_ARG;
+ break;
+ default:
+ MOZ_LOG(gDhcpUtilsLog, mozilla::LogLevel::Warning,
+ ("RetrieveOption returned %lu when option %d requested",
+ winAPIResponse, aOption));
+ rv = NS_ERROR_FAILURE;
+ }
+ return rv;
+}
+
+} // namespace windowsDHCPClient
+} // namespace system
+} // namespace toolkit
+} // namespace mozilla
diff --git a/toolkit/system/windowsDHCPClient/DHCPUtils.h b/toolkit/system/windowsDHCPClient/DHCPUtils.h
new file mode 100644
index 0000000000..20f2996acb
--- /dev/null
+++ b/toolkit/system/windowsDHCPClient/DHCPUtils.h
@@ -0,0 +1,31 @@
+/* -*- 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
diff --git a/toolkit/system/windowsDHCPClient/WindowsNetworkFunctionsWrapper.cpp b/toolkit/system/windowsDHCPClient/WindowsNetworkFunctionsWrapper.cpp
new file mode 100644
index 0000000000..0dac4bcbb9
--- /dev/null
+++ b/toolkit/system/windowsDHCPClient/WindowsNetworkFunctionsWrapper.cpp
@@ -0,0 +1,41 @@
+/* -*- 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"
+
+#ifndef __MINGW32__
+# pragma comment(lib, "IPHLPAPI.lib")
+# pragma comment(lib, "dhcpcsvc.lib")
+#endif
+
+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
diff --git a/toolkit/system/windowsDHCPClient/WindowsNetworkFunctionsWrapper.h b/toolkit/system/windowsDHCPClient/WindowsNetworkFunctionsWrapper.h
new file mode 100644
index 0000000000..2da6a9ff33
--- /dev/null
+++ b/toolkit/system/windowsDHCPClient/WindowsNetworkFunctionsWrapper.h
@@ -0,0 +1,48 @@
+/* -*- 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:
+ virtual ~WindowsNetworkFunctionsWrapper(){};
+};
+
+} // namespace windowsDHCPClient
+} // namespace system
+} // namespace toolkit
+} // namespace mozilla
+#endif // mozilla_toolkit_system_windowsDHCPClient_windowsNetworkFunctionsWrapper_h
diff --git a/toolkit/system/windowsDHCPClient/components.conf b/toolkit/system/windowsDHCPClient/components.conf
new file mode 100644
index 0000000000..f2085bce3e
--- /dev/null
+++ b/toolkit/system/windowsDHCPClient/components.conf
@@ -0,0 +1,14 @@
+# -*- 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/.
+
+Classes = [
+ {
+ 'cid': '{febf1d69-4d7d-4891-9524-045ad18b5592}',
+ 'contract_ids': ['@mozilla.org/dhcp-client;1'],
+ 'type': 'mozilla::toolkit::system::windowsDHCPClient::nsWindowsDHCPClient',
+ 'headers': ['/toolkit/system/windowsDHCPClient/nsWindowsDHCPClient.h']
+ },
+]
diff --git a/toolkit/system/windowsDHCPClient/moz.build b/toolkit/system/windowsDHCPClient/moz.build
new file mode 100644
index 0000000000..308ac4dd4d
--- /dev/null
+++ b/toolkit/system/windowsDHCPClient/moz.build
@@ -0,0 +1,22 @@
+# -*- 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",
+]
+
+XPCOM_MANIFESTS += [
+ "components.conf",
+]
+
+FINAL_LIBRARY = "xul"
diff --git a/toolkit/system/windowsDHCPClient/nsWindowsDHCPClient.cpp b/toolkit/system/windowsDHCPClient/nsWindowsDHCPClient.cpp
new file mode 100644
index 0000000000..dae96cc309
--- /dev/null
+++ b/toolkit/system/windowsDHCPClient/nsWindowsDHCPClient.cpp
@@ -0,0 +1,77 @@
+/* -*- 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/Components.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)
+
+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",
+ uint32_t(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, uint32_t(rv)));
+ return rv;
+ }
+ aRetVal.Assign(optionValue.data(), sizeOptionValue);
+ return NS_OK;
+}
+
+} // namespace windowsDHCPClient
+} // namespace system
+} // namespace toolkit
+} // namespace mozilla
diff --git a/toolkit/system/windowsDHCPClient/nsWindowsDHCPClient.h b/toolkit/system/windowsDHCPClient/nsWindowsDHCPClient.h
new file mode 100644
index 0000000000..d1d3eac0f0
--- /dev/null
+++ b/toolkit/system/windowsDHCPClient/nsWindowsDHCPClient.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/. */
+
+#include "nsIDHCPClient.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){};
+
+ private:
+ ~nsWindowsDHCPClient(){};
+ WindowsNetworkFunctionsWrapper* mNetworkFunctions;
+};
+
+} // namespace windowsDHCPClient
+} // namespace system
+} // namespace toolkit
+} // namespace mozilla
diff --git a/toolkit/system/windowsDHCPClient/tests/gtest/TestDHCPUtils.cpp b/toolkit/system/windowsDHCPClient/tests/gtest/TestDHCPUtils.cpp
new file mode 100644
index 0000000000..483e8663a8
--- /dev/null
+++ b/toolkit/system/windowsDHCPClient/tests/gtest/TestDHCPUtils.cpp
@@ -0,0 +1,306 @@
+/* -*- 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 "nsCOMPtr.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, const char* value) {
+ mOptions[option] = value;
+ }
+
+ nsString GetLastRequestedNetworkAdapterName() {
+ return mLastRequestedNetworkAdapterName;
+ }
+
+ private:
+ IP_ADAPTER_ADDRESSES* mAddressesToReturn = nullptr;
+ const 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, const char* value) {
+ mMockWindowsFunctions.get()->SetDHCPOption(option, value);
+ }
+
+ void Given_Network_Adapter_Called(IP_ADAPTER_ADDRESSES& adapterAddresses,
+ const char* adapterName) {
+ adapterAddresses.AdapterName = const_cast<char*>(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) {
+ const 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(0U, 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);
+}
diff --git a/toolkit/system/windowsDHCPClient/tests/gtest/moz.build b/toolkit/system/windowsDHCPClient/tests/gtest/moz.build
new file mode 100644
index 0000000000..b87007a353
--- /dev/null
+++ b/toolkit/system/windowsDHCPClient/tests/gtest/moz.build
@@ -0,0 +1,21 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, you can obtain one at http://mozilla.org/MPL/2.0/.
+
+with Files("**"):
+ BUG_COMPONENT = ("Core", "Networking: HTTP")
+
+UNIFIED_SOURCES += [
+ "TestDHCPUtils.cpp",
+]
+
+LOCAL_INCLUDES += [
+ "/toolkit/system/windowsDHCPClient",
+]
+
+FINAL_LIBRARY = "xul-gtest"
+
+if CONFIG["GNU_CXX"]:
+ CXXFLAGS += ["-Wshadow"]