mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 10:18:41 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			247 lines
		
	
	
	
		
			9.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			247 lines
		
	
	
	
		
			9.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* -*- 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
 |