Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/netwerk/dns/GetAddrInfo.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "GetAddrInfo.h"
8
#include "mozilla/net/DNS.h"
9
#include "prnetdb.h"
10
#include "nsHostResolver.h"
11
#include "nsError.h"
12
#include "mozilla/Mutex.h"
13
#include "nsAutoPtr.h"
14
#include "mozilla/StaticPtr.h"
15
#include "MainThreadUtils.h"
16
#include "mozilla/DebugOnly.h"
17
#include "mozilla/net/DNS.h"
18
#include <algorithm>
19
#include "prerror.h"
20
21
#include "mozilla/Logging.h"
22
23
#ifdef DNSQUERY_AVAILABLE
24
// There is a bug in windns.h where the type of parameter ppQueryResultsSet for
25
// DnsQuery_A is dependent on UNICODE being set. It should *always* be
26
// PDNS_RECORDA, but if UNICODE is set it is PDNS_RECORDW. To get around this
27
// we make sure that UNICODE is unset.
28
#undef UNICODE
29
#include <ws2tcpip.h>
30
#undef GetAddrInfo
31
#include <windns.h>
32
#endif
33
34
namespace mozilla {
35
namespace net {
36
37
static LazyLogModule gGetAddrInfoLog("GetAddrInfo");
38
#define LOG(msg, ...) \
39
3
  MOZ_LOG(gGetAddrInfoLog, LogLevel::Debug, ("[DNS]: " msg, ##__VA_ARGS__))
40
#define LOG_WARNING(msg, ...) \
41
  MOZ_LOG(gGetAddrInfoLog, LogLevel::Warning, ("[DNS]: " msg, ##__VA_ARGS__))
42
43
#ifdef DNSQUERY_AVAILABLE
44
////////////////////////////
45
// WINDOWS IMPLEMENTATION //
46
////////////////////////////
47
48
// Ensure consistency of PR_* and AF_* constants to allow for legacy usage of
49
// PR_* constants with this API.
50
static_assert(PR_AF_INET == AF_INET && PR_AF_INET6 == AF_INET6
51
    && PR_AF_UNSPEC == AF_UNSPEC, "PR_AF_* must match AF_*");
52
53
// We intentionally leak this mutex. This is because we can run into a
54
// situation where the worker threads are still running until the process
55
// is actually fully shut down, and at any time one of those worker
56
// threads can access gDnsapiInfoLock.
57
static OffTheBooksMutex* gDnsapiInfoLock = nullptr;
58
59
struct DnsapiInfo
60
{
61
public:
62
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DnsapiInfo);
63
64
  HMODULE mLibrary;
65
  decltype(&DnsQuery_A) mDnsQueryFunc;
66
  decltype(&DnsFree) mDnsFreeFunc;
67
68
private:
69
  // This will either be called during shutdown of the GetAddrInfo module, or
70
  // when a worker thread is done doing a lookup (ie: within
71
  // _GetAddrInfo_Windows). Note that the lock must be held when this is
72
  // called.
73
  ~DnsapiInfo()
74
  {
75
    if (gDnsapiInfoLock) {
76
      gDnsapiInfoLock->AssertCurrentThreadOwns();
77
    } else {
78
      MOZ_ASSERT_UNREACHABLE("No mutex available during GetAddrInfo "
79
                             "shutdown.");
80
      return;
81
    }
82
83
    LOG("Freeing Dnsapi.dll");
84
    MOZ_ASSERT(mLibrary);
85
    DebugOnly<BOOL> rv = FreeLibrary(mLibrary);
86
    NS_WARNING_ASSERTION(rv, "Failed to free Dnsapi.dll.");
87
  }
88
};
89
90
static StaticRefPtr<DnsapiInfo> gDnsapiInfo;
91
92
static MOZ_ALWAYS_INLINE nsresult
93
_GetAddrInfoInit_Windows()
94
{
95
  // This is necessary to ensure strict thread safety because if two threads
96
  // run this function at the same time they can potentially create two
97
  // mutexes.
98
  MOZ_ASSERT(NS_IsMainThread(),
99
             "Do not initialize GetAddrInfo off main thread!");
100
101
  if (!gDnsapiInfoLock) {
102
    gDnsapiInfoLock = new OffTheBooksMutex("GetAddrInfo.cpp::gDnsapiInfoLock");
103
  }
104
  OffTheBooksMutexAutoLock lock(*gDnsapiInfoLock);
105
106
  if (gDnsapiInfo) {
107
    MOZ_ASSERT_UNREACHABLE("GetAddrInfo is being initialized multiple times!");
108
    return NS_ERROR_ALREADY_INITIALIZED;
109
  }
110
111
  HMODULE library = LoadLibraryA("Dnsapi.dll");
112
  if (NS_WARN_IF(!library)) {
113
    return NS_ERROR_FAILURE;
114
  }
115
116
  FARPROC dnsQueryFunc = GetProcAddress(library, "DnsQuery_A");
117
  FARPROC dnsFreeFunc = GetProcAddress(library, "DnsFree");
118
  if (NS_WARN_IF(!dnsQueryFunc) || NS_WARN_IF(!dnsFreeFunc)) {
119
    DebugOnly<BOOL> rv = FreeLibrary(library);
120
    NS_WARNING_ASSERTION(rv, "Failed to free Dnsapi.dll.");
121
    return NS_ERROR_FAILURE;
122
  }
123
124
  DnsapiInfo* info = new DnsapiInfo;
125
  info->mLibrary = library;
126
  info->mDnsQueryFunc = (decltype(info->mDnsQueryFunc)) dnsQueryFunc;
127
  info->mDnsFreeFunc = (decltype(info->mDnsFreeFunc)) dnsFreeFunc;
128
  gDnsapiInfo = info;
129
130
  return NS_OK;
131
}
132
133
static MOZ_ALWAYS_INLINE nsresult
134
_GetAddrInfoShutdown_Windows()
135
{
136
  OffTheBooksMutexAutoLock lock(*gDnsapiInfoLock);
137
138
  if (NS_WARN_IF(!gDnsapiInfo) || NS_WARN_IF(!gDnsapiInfoLock)) {
139
    MOZ_ASSERT_UNREACHABLE("GetAddrInfo not initialized!");
140
    return NS_ERROR_NOT_INITIALIZED;
141
  }
142
143
  gDnsapiInfo = nullptr;
144
145
  return NS_OK;
146
}
147
148
// If successful, returns in aResult a TTL value that is smaller or
149
// equal with the one already there. Gets the TTL value by calling
150
// to dnsapi->mDnsQueryFunc and iterating through the returned
151
// records to find the one with the smallest TTL value.
152
static MOZ_ALWAYS_INLINE nsresult
153
_GetMinTTLForRequestType_Windows(DnsapiInfo * dnsapi, const char* aHost,
154
                                 uint16_t aRequestType, unsigned int* aResult)
155
{
156
  MOZ_ASSERT(dnsapi);
157
  MOZ_ASSERT(aHost);
158
  MOZ_ASSERT(aResult);
159
160
  PDNS_RECORDA dnsData = nullptr;
161
  DNS_STATUS status = dnsapi->mDnsQueryFunc(
162
    aHost,
163
    aRequestType,
164
    (DNS_QUERY_STANDARD | DNS_QUERY_NO_NETBT | DNS_QUERY_NO_HOSTS_FILE
165
      | DNS_QUERY_NO_MULTICAST | DNS_QUERY_ACCEPT_TRUNCATED_RESPONSE
166
      | DNS_QUERY_DONT_RESET_TTL_VALUES),
167
    nullptr,
168
    &dnsData,
169
    nullptr);
170
  if (status == DNS_INFO_NO_RECORDS || status == DNS_ERROR_RCODE_NAME_ERROR
171
      || !dnsData) {
172
    LOG("No DNS records found for %s. status=%X. aRequestType = %X\n",
173
        aHost, status, aRequestType);
174
    return NS_ERROR_FAILURE;
175
  } else if (status != NOERROR) {
176
    LOG_WARNING("DnsQuery_A failed with status %X.\n", status);
177
    return NS_ERROR_UNEXPECTED;
178
  }
179
180
  for (PDNS_RECORDA curRecord = dnsData; curRecord; curRecord = curRecord->pNext) {
181
    // Only records in the answer section are important
182
    if (curRecord->Flags.S.Section != DnsSectionAnswer) {
183
      continue;
184
    }
185
186
    if (curRecord->wType == aRequestType) {
187
      *aResult = std::min<unsigned int>(*aResult, curRecord->dwTtl);
188
    } else {
189
      LOG("Received unexpected record type %u in response for %s.\n",
190
          curRecord->wType, aHost);
191
    }
192
  }
193
194
  dnsapi->mDnsFreeFunc(dnsData, DNS_FREE_TYPE::DnsFreeRecordList);
195
  return NS_OK;
196
}
197
198
static MOZ_ALWAYS_INLINE nsresult
199
_GetTTLData_Windows(const nsACString& aHost, uint32_t* aResult, uint16_t aAddressFamily)
200
{
201
  MOZ_ASSERT(!aHost.IsEmpty());
202
  MOZ_ASSERT(aResult);
203
  if (aAddressFamily != PR_AF_UNSPEC &&
204
      aAddressFamily != PR_AF_INET &&
205
      aAddressFamily != PR_AF_INET6) {
206
    return NS_ERROR_UNEXPECTED;
207
  }
208
209
  RefPtr<DnsapiInfo> dnsapi = nullptr;
210
  {
211
    OffTheBooksMutexAutoLock lock(*gDnsapiInfoLock);
212
    dnsapi = gDnsapiInfo;
213
  }
214
215
  if (!dnsapi) {
216
    LOG_WARNING("GetAddrInfo has been shutdown or has not been initialized.");
217
    return NS_ERROR_NOT_INITIALIZED;
218
  }
219
220
  // In order to avoid using ANY records which are not always implemented as a
221
  // "Gimme what you have" request in hostname resolvers, we should send A
222
  // and/or AAAA requests, based on the address family requested.
223
  unsigned int ttl = (unsigned int)-1;
224
  if (aAddressFamily == PR_AF_UNSPEC || aAddressFamily == PR_AF_INET) {
225
    _GetMinTTLForRequestType_Windows(dnsapi, aHost.BeginReading(), DNS_TYPE_A, &ttl);
226
  }
227
  if (aAddressFamily == PR_AF_UNSPEC || aAddressFamily == PR_AF_INET6) {
228
    _GetMinTTLForRequestType_Windows(dnsapi, aHost.BeginReading(), DNS_TYPE_AAAA, &ttl);
229
  }
230
231
  {
232
    // dnsapi's destructor is not thread-safe, so we release explicitly here
233
    OffTheBooksMutexAutoLock lock(*gDnsapiInfoLock);
234
    dnsapi = nullptr;
235
  }
236
237
  if (ttl == (unsigned int)-1) {
238
    LOG("No useable TTL found.");
239
    return NS_ERROR_FAILURE;
240
  }
241
242
  *aResult = ttl;
243
  return NS_OK;
244
}
245
#endif
246
247
////////////////////////////////////
248
// PORTABLE RUNTIME IMPLEMENTATION//
249
////////////////////////////////////
250
251
static MOZ_ALWAYS_INLINE nsresult
252
_GetAddrInfo_Portable(const nsACString& aCanonHost, uint16_t aAddressFamily,
253
                      uint16_t aFlags, AddrInfo** aAddrInfo)
254
0
{
255
0
  MOZ_ASSERT(!aCanonHost.IsEmpty());
256
0
  MOZ_ASSERT(aAddrInfo);
257
0
258
0
  // We accept the same aFlags that nsHostResolver::ResolveHost accepts, but we
259
0
  // need to translate the aFlags into a form that PR_GetAddrInfoByName
260
0
  // accepts.
261
0
  int prFlags = PR_AI_ADDRCONFIG;
262
0
  if (!(aFlags & nsHostResolver::RES_CANON_NAME)) {
263
0
    prFlags |= PR_AI_NOCANONNAME;
264
0
  }
265
0
266
0
  // We need to remove IPv4 records manually because PR_GetAddrInfoByName
267
0
  // doesn't support PR_AF_INET6.
268
0
  bool disableIPv4 = aAddressFamily == PR_AF_INET6;
269
0
  if (disableIPv4) {
270
0
    aAddressFamily = PR_AF_UNSPEC;
271
0
  }
272
0
273
0
  PRAddrInfo* prai = PR_GetAddrInfoByName(aCanonHost.BeginReading(), aAddressFamily, prFlags);
274
0
275
0
  if (!prai) {
276
0
    return NS_ERROR_UNKNOWN_HOST;
277
0
  }
278
0
279
0
  nsAutoCString canonName;
280
0
  if (aFlags & nsHostResolver::RES_CANON_NAME) {
281
0
    canonName.Assign(PR_GetCanonNameFromAddrInfo(prai));
282
0
  }
283
0
284
0
  bool filterNameCollision = !(aFlags & nsHostResolver::RES_ALLOW_NAME_COLLISION);
285
0
  nsAutoPtr<AddrInfo> ai(new AddrInfo(aCanonHost, prai, disableIPv4,
286
0
                                      filterNameCollision, canonName));
287
0
  PR_FreeAddrInfo(prai);
288
0
  if (ai->mAddresses.isEmpty()) {
289
0
    return NS_ERROR_UNKNOWN_HOST;
290
0
  }
291
0
292
0
  *aAddrInfo = ai.forget();
293
0
294
0
  return NS_OK;
295
0
}
296
297
//////////////////////////////////////
298
// COMMON/PLATFORM INDEPENDENT CODE //
299
//////////////////////////////////////
300
nsresult
301
3
GetAddrInfoInit() {
302
3
  LOG("Initializing GetAddrInfo.\n");
303
3
304
#ifdef DNSQUERY_AVAILABLE
305
  return _GetAddrInfoInit_Windows();
306
#else
307
  return NS_OK;
308
3
#endif
309
3
}
310
311
nsresult
312
0
GetAddrInfoShutdown() {
313
0
  LOG("Shutting down GetAddrInfo.\n");
314
0
315
#ifdef DNSQUERY_AVAILABLE
316
  return _GetAddrInfoShutdown_Windows();
317
#else
318
  return NS_OK;
319
0
#endif
320
0
}
321
322
nsresult
323
GetAddrInfo(const nsACString& aHost, uint16_t aAddressFamily, uint16_t aFlags,
324
            AddrInfo** aAddrInfo, bool aGetTtl)
325
0
{
326
0
  if (NS_WARN_IF(aHost.IsEmpty()) || NS_WARN_IF(!aAddrInfo)) {
327
0
    return NS_ERROR_NULL_POINTER;
328
0
  }
329
0
330
#ifdef DNSQUERY_AVAILABLE
331
  // The GetTTLData needs the canonical name to function properly
332
  if (aGetTtl) {
333
    aFlags |= nsHostResolver::RES_CANON_NAME;
334
  }
335
#endif
336
337
0
  nsAutoCString host(aHost);
338
0
  if (gNativeIsLocalhost) {
339
0
    // pretend we use the given host but use IPv4 localhost instead!
340
0
    host = NS_LITERAL_CSTRING("localhost");
341
0
    aAddressFamily = PR_AF_INET;
342
0
  }
343
0
344
0
  *aAddrInfo = nullptr;
345
0
  nsresult rv = _GetAddrInfo_Portable(host, aAddressFamily, aFlags,
346
0
                                      aAddrInfo);
347
0
348
#ifdef DNSQUERY_AVAILABLE
349
  if (aGetTtl && NS_SUCCEEDED(rv)) {
350
    // Figure out the canonical name, or if that fails, just use the host name
351
    // we have.
352
    nsAutoCString name;
353
    if (*aAddrInfo != nullptr && !(*aAddrInfo)->mCanonicalName.IsEmpty()) {
354
      name = (*aAddrInfo)->mCanonicalName;
355
    } else {
356
      name = host;
357
    }
358
359
    LOG("Getting TTL for %s (cname = %s).", host.get(), name.get());
360
    uint32_t ttl = 0;
361
    nsresult ttlRv = _GetTTLData_Windows(name, &ttl, aAddressFamily);
362
    if (NS_SUCCEEDED(ttlRv)) {
363
      (*aAddrInfo)->ttl = ttl;
364
      LOG("Got TTL %u for %s (name = %s).", ttl, host.get(), name.get());
365
    } else {
366
      LOG_WARNING("Could not get TTL for %s (cname = %s).", host.get(), name.get());
367
    }
368
  }
369
#endif
370
371
0
  return rv;
372
0
}
373
374
} // namespace net
375
} // namespace mozilla