diff options
Diffstat (limited to 'toolkit/system/windowsDHCPClient')
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..5229e0205a --- /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 %d and buffer " + "length of %d", + pAddresses.size(), outBufLen)); + + dwRetVal = aWindowsNetworkFunctionsWrapper->GetAdaptersAddressesWrapped( + family, flags, nullptr, pAddresses.data(), (PULONG)&outBufLen); + + if (dwRetVal == ERROR_BUFFER_OVERFLOW) { + iterations++; + } + } while (dwRetVal == ERROR_BUFFER_OVERFLOW && iterations < MOZ_MAX_TRIES); + + switch (dwRetVal) { + case NO_ERROR: { + // set default return value if we don't find a suitable network adapter + rv = NS_ERROR_NOT_AVAILABLE; + PIP_ADAPTER_ADDRESSES pCurrAddresses = pAddresses.data(); + while (pCurrAddresses) { + if (IsCurrentAndHasDHCP(pCurrAddresses)) { + rv = NS_OK; + aNetworkAdapterName.Assign(pCurrAddresses->AdapterName); + break; + } + pCurrAddresses = pCurrAddresses->Next; + } + } break; + case ERROR_NO_DATA: + rv = NS_ERROR_NOT_AVAILABLE; + break; + default: + MOZ_LOG(gDhcpUtilsLog, mozilla::LogLevel::Warning, + ("GetAdaptersAddresses returned %d", dwRetVal)); + rv = NS_ERROR_FAILURE; + break; + } + return rv; +} + +DWORD +IterateDHCPInformRequestsUntilBufferLargeEnough( + DHCPCAPI_PARAMS& aDhcpRequestedOptionParams, + wchar_t* aWideNetworkAdapterName, std::vector<char>& aBuffer, + WindowsNetworkFunctionsWrapper* aWindowsNetworkFunctionsWrapper) { + uint32_t iterations = 0; + uint32_t outBufLen = MOZ_WORKING_BUFFER_SIZE_DHCP_PARAMS; + + DHCPCAPI_PARAMS_ARRAY RequestParams = {1, // only one option to request + &aDhcpRequestedOptionParams}; + + // According to [2], + // the following is for 'Optional data to be requested, + // in addition to the data requested in the RecdParams array.' + // We are not requesting anything in addition, so this is empty. + DHCPCAPI_PARAMS_ARRAY SendParams = {0, nullptr}; + + DWORD winAPIResponse; + // Now we try calling the DHCPRequestParams method until the return value + // is not ERROR_MORE_DATA. According to [2]: + // + // + // > Note that the required size of Buffer may increase during the time that + // elapses > between the initial function call's return and a subsequent call; + // > therefore, the required size of Buffer (indicated in pSize) + // > provides an indication of the approximate size required of Buffer, + // > rather than guaranteeing that subsequent calls will return successfully + // > if Buffer is set to the size indicated in pSize. + // + // + // This is why we allow this DHCPRequestParams to be called several times, + // rather than just the two that would be neccessary if we could rely on the + // value returned in outBufLen being the true size needed. + do { + aBuffer.resize(outBufLen); + + winAPIResponse = aWindowsNetworkFunctionsWrapper->DhcpRequestParamsWrapped( + DHCPCAPI_REQUEST_SYNCHRONOUS, // Flags + nullptr, // Reserved + aWideNetworkAdapterName, // Adapter Name + nullptr, // not using class id + SendParams, // sent parameters + RequestParams, // requesting params + (PBYTE)aBuffer.data(), // buffer for the output of RequestParams + (PULONG)&outBufLen, // buffer size + nullptr // Request ID for persistent requests - not needed here + ); + + if (winAPIResponse == ERROR_MORE_DATA) { + iterations++; + } + } while (winAPIResponse == ERROR_MORE_DATA && iterations < MOZ_MAX_TRIES); + return winAPIResponse; +} + +nsresult RetrieveOption( + const nsACString& aAdapterName, uint8_t aOption, + std::vector<char>& aOptionValueBuf, uint32_t* aOptionSize, + WindowsNetworkFunctionsWrapper* aWindowsNetworkFunctionsWrapper) { + nsresult rv; + nsAutoString wideNetworkAdapterName = NS_ConvertUTF8toUTF16(aAdapterName); + + DHCPCAPI_PARAMS DhcpRequestedOptionParams = { + 0, // Flags - Reserved, must be set to zero [2] + aOption, // OptionId + false, // whether this is vendor specific - let's assume not + nullptr, // data filled in on return + 0 // nBytes used by return data + }; + + std::vector<char> tmpBuffer( + MOZ_WORKING_BUFFER_SIZE_DHCP_PARAMS); // a buffer for the DHCP response + // object + DWORD winAPIResponse = IterateDHCPInformRequestsUntilBufferLargeEnough( + DhcpRequestedOptionParams, wideNetworkAdapterName.get(), tmpBuffer, + aWindowsNetworkFunctionsWrapper); + + switch (winAPIResponse) { + case NO_ERROR: { + if (DhcpRequestedOptionParams.nBytesData == 0) { + *aOptionSize = 0; + rv = NS_ERROR_NOT_AVAILABLE; + break; + } + + if (*aOptionSize >= DhcpRequestedOptionParams.nBytesData) { + rv = NS_OK; + } else { + rv = NS_ERROR_LOSS_OF_SIGNIFICANT_DATA; + } + + uint32_t actualSizeReturned = + *aOptionSize > DhcpRequestedOptionParams.nBytesData + ? DhcpRequestedOptionParams.nBytesData + : *aOptionSize; + + memcpy(aOptionValueBuf.data(), DhcpRequestedOptionParams.Data, + actualSizeReturned); + *aOptionSize = DhcpRequestedOptionParams.nBytesData; + break; + } + case ERROR_INVALID_PARAMETER: + MOZ_LOG(gDhcpUtilsLog, mozilla::LogLevel::Warning, + ("RetrieveOption returned %d (ERROR_INVALID_PARAMETER) when " + "option %d requested", + winAPIResponse, aOption)); + rv = NS_ERROR_INVALID_ARG; + break; + default: + MOZ_LOG(gDhcpUtilsLog, mozilla::LogLevel::Warning, + ("RetrieveOption returned %d when option %d requested", + winAPIResponse, aOption)); + rv = NS_ERROR_FAILURE; + } + return rv; +} + +} // namespace windowsDHCPClient +} // namespace system +} // namespace toolkit +} // namespace mozilla 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..9ddb3bb994 --- /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", + 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; +} + +} // 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..d701a31104 --- /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(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); +} 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"] |