summaryrefslogtreecommitdiffstats
path: root/toolkit/system/windowsDHCPClient/DHCPUtils.cpp
blob: 956c2adbf06e7f77576337ac5a28823107792512 (plain)
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 %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