/* -*- 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 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(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 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 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 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 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 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 dhcpClient = new nsWindowsDHCPClient(mMockWindowsFunctions); nsresult retVal = dhcpClient->GetOption(252, optionValue); ASSERT_STREQ("", optionValue.Data()); ASSERT_EQ(NS_ERROR_NOT_AVAILABLE, retVal); }