Line data Source code
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 : #if 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 : 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 : #if 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 char* aHost, uint16_t* aResult, uint16_t aAddressFamily)
200 : {
201 : MOZ_ASSERT(aHost);
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 = -1;
224 : if (aAddressFamily == PR_AF_UNSPEC || aAddressFamily == PR_AF_INET) {
225 : _GetMinTTLForRequestType_Windows(dnsapi, aHost, DNS_TYPE_A, &ttl);
226 : }
227 : if (aAddressFamily == PR_AF_UNSPEC || aAddressFamily == PR_AF_INET6) {
228 : _GetMinTTLForRequestType_Windows(dnsapi, aHost, 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 == -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 1 : _GetAddrInfo_Portable(const char* aCanonHost, uint16_t aAddressFamily,
253 : uint16_t aFlags, const char* aNetworkInterface,
254 : AddrInfo** aAddrInfo)
255 : {
256 1 : MOZ_ASSERT(aCanonHost);
257 1 : MOZ_ASSERT(aAddrInfo);
258 :
259 : // We accept the same aFlags that nsHostResolver::ResolveHost accepts, but we
260 : // need to translate the aFlags into a form that PR_GetAddrInfoByName
261 : // accepts.
262 1 : int prFlags = PR_AI_ADDRCONFIG;
263 1 : if (!(aFlags & nsHostResolver::RES_CANON_NAME)) {
264 1 : prFlags |= PR_AI_NOCANONNAME;
265 : }
266 :
267 : // We need to remove IPv4 records manually because PR_GetAddrInfoByName
268 : // doesn't support PR_AF_INET6.
269 1 : bool disableIPv4 = aAddressFamily == PR_AF_INET6;
270 1 : if (disableIPv4) {
271 0 : aAddressFamily = PR_AF_UNSPEC;
272 : }
273 :
274 1 : PRAddrInfo* prai = PR_GetAddrInfoByName(aCanonHost, aAddressFamily, prFlags);
275 :
276 1 : if (!prai) {
277 0 : return NS_ERROR_UNKNOWN_HOST;
278 : }
279 :
280 1 : const char* canonName = nullptr;
281 1 : if (aFlags & nsHostResolver::RES_CANON_NAME) {
282 0 : canonName = PR_GetCanonNameFromAddrInfo(prai);
283 : }
284 :
285 1 : bool filterNameCollision = !(aFlags & nsHostResolver::RES_ALLOW_NAME_COLLISION);
286 : nsAutoPtr<AddrInfo> ai(new AddrInfo(aCanonHost, prai, disableIPv4,
287 2 : filterNameCollision, canonName));
288 1 : PR_FreeAddrInfo(prai);
289 1 : if (ai->mAddresses.isEmpty()) {
290 0 : return NS_ERROR_UNKNOWN_HOST;
291 : }
292 :
293 1 : *aAddrInfo = ai.forget();
294 :
295 1 : return NS_OK;
296 : }
297 :
298 : //////////////////////////////////////
299 : // COMMON/PLATFORM INDEPENDENT CODE //
300 : //////////////////////////////////////
301 : nsresult
302 2 : GetAddrInfoInit() {
303 2 : LOG("Initializing GetAddrInfo.\n");
304 :
305 : #if DNSQUERY_AVAILABLE
306 : return _GetAddrInfoInit_Windows();
307 : #else
308 2 : return NS_OK;
309 : #endif
310 : }
311 :
312 : nsresult
313 1 : GetAddrInfoShutdown() {
314 1 : LOG("Shutting down GetAddrInfo.\n");
315 :
316 : #if DNSQUERY_AVAILABLE
317 : return _GetAddrInfoShutdown_Windows();
318 : #else
319 1 : return NS_OK;
320 : #endif
321 : }
322 :
323 : nsresult
324 1 : GetAddrInfo(const char* aHost, uint16_t aAddressFamily, uint16_t aFlags,
325 : const char* aNetworkInterface, AddrInfo** aAddrInfo, bool aGetTtl)
326 : {
327 1 : if (NS_WARN_IF(!aHost) || NS_WARN_IF(!aAddrInfo)) {
328 0 : return NS_ERROR_NULL_POINTER;
329 : }
330 :
331 : #if DNSQUERY_AVAILABLE
332 : // The GetTTLData needs the canonical name to function properly
333 : if (aGetTtl) {
334 : aFlags |= nsHostResolver::RES_CANON_NAME;
335 : }
336 : #endif
337 :
338 1 : *aAddrInfo = nullptr;
339 1 : nsresult rv = _GetAddrInfo_Portable(aHost, aAddressFamily, aFlags,
340 1 : aNetworkInterface, aAddrInfo);
341 :
342 : #if DNSQUERY_AVAILABLE
343 : if (aGetTtl && NS_SUCCEEDED(rv)) {
344 : // Figure out the canonical name, or if that fails, just use the host name
345 : // we have.
346 : const char *name = nullptr;
347 : if (*aAddrInfo != nullptr && (*aAddrInfo)->mCanonicalName) {
348 : name = (*aAddrInfo)->mCanonicalName;
349 : } else {
350 : name = aHost;
351 : }
352 :
353 : LOG("Getting TTL for %s (cname = %s).", aHost, name);
354 : uint16_t ttl = 0;
355 : nsresult ttlRv = _GetTTLData_Windows(name, &ttl, aAddressFamily);
356 : if (NS_SUCCEEDED(ttlRv)) {
357 : (*aAddrInfo)->ttl = ttl;
358 : LOG("Got TTL %u for %s (name = %s).", ttl, aHost, name);
359 : } else {
360 : LOG_WARNING("Could not get TTL for %s (cname = %s).", aHost, name);
361 : }
362 : }
363 : #endif
364 :
365 1 : return rv;
366 : }
367 :
368 : } // namespace net
369 : } // namespace mozilla
|