DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Header

Mercurial (4f16fe418e32)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 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