|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
|
2 /* vim: set sw=4 ts=8 et 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 "nsDNSService2.h" |
|
8 #include "nsIDNSRecord.h" |
|
9 #include "nsIDNSListener.h" |
|
10 #include "nsICancelable.h" |
|
11 #include "nsIPrefService.h" |
|
12 #include "nsIPrefBranch.h" |
|
13 #include "nsIServiceManager.h" |
|
14 #include "nsIXPConnect.h" |
|
15 #include "nsProxyRelease.h" |
|
16 #include "nsReadableUtils.h" |
|
17 #include "nsString.h" |
|
18 #include "nsAutoPtr.h" |
|
19 #include "nsNetCID.h" |
|
20 #include "nsError.h" |
|
21 #include "nsDNSPrefetch.h" |
|
22 #include "nsThreadUtils.h" |
|
23 #include "nsIProtocolProxyService.h" |
|
24 #include "prsystem.h" |
|
25 #include "prnetdb.h" |
|
26 #include "prmon.h" |
|
27 #include "prio.h" |
|
28 #include "plstr.h" |
|
29 #include "nsIOService.h" |
|
30 #include "nsCharSeparatedTokenizer.h" |
|
31 #include "nsNetAddr.h" |
|
32 #include "nsProxyRelease.h" |
|
33 |
|
34 #include "mozilla/Attributes.h" |
|
35 #include "mozilla/VisualEventTracer.h" |
|
36 #include "mozilla/net/NeckoCommon.h" |
|
37 #include "mozilla/net/ChildDNSService.h" |
|
38 #include "mozilla/net/DNSListenerProxy.h" |
|
39 |
|
40 using namespace mozilla; |
|
41 using namespace mozilla::net; |
|
42 |
|
43 static const char kPrefDnsCacheEntries[] = "network.dnsCacheEntries"; |
|
44 static const char kPrefDnsCacheExpiration[] = "network.dnsCacheExpiration"; |
|
45 static const char kPrefDnsCacheGrace[] = "network.dnsCacheExpirationGracePeriod"; |
|
46 static const char kPrefIPv4OnlyDomains[] = "network.dns.ipv4OnlyDomains"; |
|
47 static const char kPrefDisableIPv6[] = "network.dns.disableIPv6"; |
|
48 static const char kPrefDisablePrefetch[] = "network.dns.disablePrefetch"; |
|
49 static const char kPrefDnsLocalDomains[] = "network.dns.localDomains"; |
|
50 static const char kPrefDnsNotifyResolution[] = "network.dns.notifyResolution"; |
|
51 |
|
52 //----------------------------------------------------------------------------- |
|
53 |
|
54 class nsDNSRecord : public nsIDNSRecord |
|
55 { |
|
56 public: |
|
57 NS_DECL_THREADSAFE_ISUPPORTS |
|
58 NS_DECL_NSIDNSRECORD |
|
59 |
|
60 nsDNSRecord(nsHostRecord *hostRecord) |
|
61 : mHostRecord(hostRecord) |
|
62 , mIter(nullptr) |
|
63 , mIterGenCnt(-1) |
|
64 , mDone(false) {} |
|
65 |
|
66 private: |
|
67 virtual ~nsDNSRecord() {} |
|
68 |
|
69 nsRefPtr<nsHostRecord> mHostRecord; |
|
70 NetAddrElement *mIter; |
|
71 int mIterGenCnt; // the generation count of |
|
72 // mHostRecord->addr_info when we |
|
73 // start iterating |
|
74 bool mDone; |
|
75 }; |
|
76 |
|
77 NS_IMPL_ISUPPORTS(nsDNSRecord, nsIDNSRecord) |
|
78 |
|
79 NS_IMETHODIMP |
|
80 nsDNSRecord::GetCanonicalName(nsACString &result) |
|
81 { |
|
82 // this method should only be called if we have a CNAME |
|
83 NS_ENSURE_TRUE(mHostRecord->flags & nsHostResolver::RES_CANON_NAME, |
|
84 NS_ERROR_NOT_AVAILABLE); |
|
85 |
|
86 // if the record is for an IP address literal, then the canonical |
|
87 // host name is the IP address literal. |
|
88 const char *cname; |
|
89 { |
|
90 MutexAutoLock lock(mHostRecord->addr_info_lock); |
|
91 if (mHostRecord->addr_info) |
|
92 cname = mHostRecord->addr_info->mCanonicalName ? |
|
93 mHostRecord->addr_info->mCanonicalName : |
|
94 mHostRecord->addr_info->mHostName; |
|
95 else |
|
96 cname = mHostRecord->host; |
|
97 result.Assign(cname); |
|
98 } |
|
99 return NS_OK; |
|
100 } |
|
101 |
|
102 NS_IMETHODIMP |
|
103 nsDNSRecord::GetNextAddr(uint16_t port, NetAddr *addr) |
|
104 { |
|
105 if (mDone) { |
|
106 return NS_ERROR_NOT_AVAILABLE; |
|
107 } |
|
108 |
|
109 mHostRecord->addr_info_lock.Lock(); |
|
110 if (mHostRecord->addr_info) { |
|
111 if (mIterGenCnt != mHostRecord->addr_info_gencnt) { |
|
112 // mHostRecord->addr_info has changed, restart the iteration. |
|
113 mIter = nullptr; |
|
114 mIterGenCnt = mHostRecord->addr_info_gencnt; |
|
115 } |
|
116 |
|
117 bool startedFresh = !mIter; |
|
118 |
|
119 do { |
|
120 if (!mIter) { |
|
121 mIter = mHostRecord->addr_info->mAddresses.getFirst(); |
|
122 } else { |
|
123 mIter = mIter->getNext(); |
|
124 } |
|
125 } |
|
126 while (mIter && mHostRecord->Blacklisted(&mIter->mAddress)); |
|
127 |
|
128 if (!mIter && startedFresh) { |
|
129 // If everything was blacklisted we want to reset the blacklist (and |
|
130 // likely relearn it) and return the first address. That is better |
|
131 // than nothing. |
|
132 mHostRecord->ResetBlacklist(); |
|
133 mIter = mHostRecord->addr_info->mAddresses.getFirst(); |
|
134 } |
|
135 |
|
136 if (mIter) { |
|
137 memcpy(addr, &mIter->mAddress, sizeof(NetAddr)); |
|
138 } |
|
139 |
|
140 mHostRecord->addr_info_lock.Unlock(); |
|
141 |
|
142 if (!mIter) { |
|
143 mDone = true; |
|
144 return NS_ERROR_NOT_AVAILABLE; |
|
145 } |
|
146 } |
|
147 else { |
|
148 mHostRecord->addr_info_lock.Unlock(); |
|
149 |
|
150 if (!mHostRecord->addr) { |
|
151 // Both mHostRecord->addr_info and mHostRecord->addr are null. |
|
152 // This can happen if mHostRecord->addr_info expired and the |
|
153 // attempt to reresolve it failed. |
|
154 return NS_ERROR_NOT_AVAILABLE; |
|
155 } |
|
156 memcpy(addr, mHostRecord->addr, sizeof(NetAddr)); |
|
157 mDone = true; |
|
158 } |
|
159 |
|
160 // set given port |
|
161 port = htons(port); |
|
162 if (addr->raw.family == AF_INET) { |
|
163 addr->inet.port = port; |
|
164 } |
|
165 else if (addr->raw.family == AF_INET6) { |
|
166 addr->inet6.port = port; |
|
167 } |
|
168 |
|
169 return NS_OK; |
|
170 } |
|
171 |
|
172 NS_IMETHODIMP |
|
173 nsDNSRecord::GetScriptableNextAddr(uint16_t port, nsINetAddr * *result) |
|
174 { |
|
175 NetAddr addr; |
|
176 nsresult rv = GetNextAddr(port, &addr); |
|
177 if (NS_FAILED(rv)) return rv; |
|
178 |
|
179 NS_ADDREF(*result = new nsNetAddr(&addr)); |
|
180 |
|
181 return NS_OK; |
|
182 } |
|
183 |
|
184 NS_IMETHODIMP |
|
185 nsDNSRecord::GetNextAddrAsString(nsACString &result) |
|
186 { |
|
187 NetAddr addr; |
|
188 nsresult rv = GetNextAddr(0, &addr); |
|
189 if (NS_FAILED(rv)) return rv; |
|
190 |
|
191 char buf[kIPv6CStrBufSize]; |
|
192 if (NetAddrToString(&addr, buf, sizeof(buf))) { |
|
193 result.Assign(buf); |
|
194 return NS_OK; |
|
195 } |
|
196 NS_ERROR("NetAddrToString failed unexpectedly"); |
|
197 return NS_ERROR_FAILURE; // conversion failed for some reason |
|
198 } |
|
199 |
|
200 NS_IMETHODIMP |
|
201 nsDNSRecord::HasMore(bool *result) |
|
202 { |
|
203 if (mDone) { |
|
204 *result = false; |
|
205 return NS_OK; |
|
206 } |
|
207 |
|
208 NetAddrElement *iterCopy = mIter; |
|
209 |
|
210 NetAddr addr; |
|
211 *result = NS_SUCCEEDED(GetNextAddr(0, &addr)); |
|
212 |
|
213 mIter = iterCopy; |
|
214 mDone = false; |
|
215 |
|
216 return NS_OK; |
|
217 } |
|
218 |
|
219 NS_IMETHODIMP |
|
220 nsDNSRecord::Rewind() |
|
221 { |
|
222 mIter = nullptr; |
|
223 mIterGenCnt = -1; |
|
224 mDone = false; |
|
225 return NS_OK; |
|
226 } |
|
227 |
|
228 NS_IMETHODIMP |
|
229 nsDNSRecord::ReportUnusable(uint16_t aPort) |
|
230 { |
|
231 // right now we don't use the port in the blacklist |
|
232 |
|
233 MutexAutoLock lock(mHostRecord->addr_info_lock); |
|
234 |
|
235 // Check that we are using a real addr_info (as opposed to a single |
|
236 // constant address), and that the generation count is valid. Otherwise, |
|
237 // ignore the report. |
|
238 |
|
239 if (mHostRecord->addr_info && |
|
240 mIterGenCnt == mHostRecord->addr_info_gencnt && |
|
241 mIter) { |
|
242 mHostRecord->ReportUnusable(&mIter->mAddress); |
|
243 } |
|
244 |
|
245 return NS_OK; |
|
246 } |
|
247 |
|
248 //----------------------------------------------------------------------------- |
|
249 |
|
250 class nsDNSAsyncRequest MOZ_FINAL : public nsResolveHostCallback |
|
251 , public nsICancelable |
|
252 { |
|
253 public: |
|
254 NS_DECL_THREADSAFE_ISUPPORTS |
|
255 NS_DECL_NSICANCELABLE |
|
256 |
|
257 nsDNSAsyncRequest(nsHostResolver *res, |
|
258 const nsACString &host, |
|
259 nsIDNSListener *listener, |
|
260 uint16_t flags, |
|
261 uint16_t af) |
|
262 : mResolver(res) |
|
263 , mHost(host) |
|
264 , mListener(listener) |
|
265 , mFlags(flags) |
|
266 , mAF(af) {} |
|
267 ~nsDNSAsyncRequest() {} |
|
268 |
|
269 void OnLookupComplete(nsHostResolver *, nsHostRecord *, nsresult); |
|
270 // Returns TRUE if the DNS listener arg is the same as the member listener |
|
271 // Used in Cancellations to remove DNS requests associated with a |
|
272 // particular hostname and nsIDNSListener |
|
273 bool EqualsAsyncListener(nsIDNSListener *aListener); |
|
274 |
|
275 size_t SizeOfIncludingThis(mozilla::MallocSizeOf) const; |
|
276 |
|
277 nsRefPtr<nsHostResolver> mResolver; |
|
278 nsCString mHost; // hostname we're resolving |
|
279 nsCOMPtr<nsIDNSListener> mListener; |
|
280 uint16_t mFlags; |
|
281 uint16_t mAF; |
|
282 }; |
|
283 |
|
284 void |
|
285 nsDNSAsyncRequest::OnLookupComplete(nsHostResolver *resolver, |
|
286 nsHostRecord *hostRecord, |
|
287 nsresult status) |
|
288 { |
|
289 // need to have an owning ref when we issue the callback to enable |
|
290 // the caller to be able to addref/release multiple times without |
|
291 // destroying the record prematurely. |
|
292 nsCOMPtr<nsIDNSRecord> rec; |
|
293 if (NS_SUCCEEDED(status)) { |
|
294 NS_ASSERTION(hostRecord, "no host record"); |
|
295 rec = new nsDNSRecord(hostRecord); |
|
296 if (!rec) |
|
297 status = NS_ERROR_OUT_OF_MEMORY; |
|
298 } |
|
299 |
|
300 MOZ_EVENT_TRACER_DONE(this, "net::dns::lookup"); |
|
301 |
|
302 mListener->OnLookupComplete(this, rec, status); |
|
303 mListener = nullptr; |
|
304 |
|
305 // release the reference to ourselves that was added before we were |
|
306 // handed off to the host resolver. |
|
307 NS_RELEASE_THIS(); |
|
308 } |
|
309 |
|
310 bool |
|
311 nsDNSAsyncRequest::EqualsAsyncListener(nsIDNSListener *aListener) |
|
312 { |
|
313 return (aListener == mListener); |
|
314 } |
|
315 |
|
316 size_t |
|
317 nsDNSAsyncRequest::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const |
|
318 { |
|
319 size_t n = mallocSizeOf(this); |
|
320 |
|
321 // The following fields aren't measured. |
|
322 // - mHost, because it's a non-owning pointer |
|
323 // - mResolver, because it's a non-owning pointer |
|
324 // - mListener, because it's a non-owning pointer |
|
325 |
|
326 return n; |
|
327 } |
|
328 |
|
329 NS_IMPL_ISUPPORTS(nsDNSAsyncRequest, nsICancelable) |
|
330 |
|
331 NS_IMETHODIMP |
|
332 nsDNSAsyncRequest::Cancel(nsresult reason) |
|
333 { |
|
334 NS_ENSURE_ARG(NS_FAILED(reason)); |
|
335 mResolver->DetachCallback(mHost.get(), mFlags, mAF, this, reason); |
|
336 return NS_OK; |
|
337 } |
|
338 |
|
339 //----------------------------------------------------------------------------- |
|
340 |
|
341 class nsDNSSyncRequest : public nsResolveHostCallback |
|
342 { |
|
343 public: |
|
344 nsDNSSyncRequest(PRMonitor *mon) |
|
345 : mDone(false) |
|
346 , mStatus(NS_OK) |
|
347 , mMonitor(mon) {} |
|
348 virtual ~nsDNSSyncRequest() {} |
|
349 |
|
350 void OnLookupComplete(nsHostResolver *, nsHostRecord *, nsresult); |
|
351 bool EqualsAsyncListener(nsIDNSListener *aListener); |
|
352 size_t SizeOfIncludingThis(mozilla::MallocSizeOf) const; |
|
353 |
|
354 bool mDone; |
|
355 nsresult mStatus; |
|
356 nsRefPtr<nsHostRecord> mHostRecord; |
|
357 |
|
358 private: |
|
359 PRMonitor *mMonitor; |
|
360 }; |
|
361 |
|
362 void |
|
363 nsDNSSyncRequest::OnLookupComplete(nsHostResolver *resolver, |
|
364 nsHostRecord *hostRecord, |
|
365 nsresult status) |
|
366 { |
|
367 // store results, and wake up nsDNSService::Resolve to process results. |
|
368 PR_EnterMonitor(mMonitor); |
|
369 mDone = true; |
|
370 mStatus = status; |
|
371 mHostRecord = hostRecord; |
|
372 PR_Notify(mMonitor); |
|
373 PR_ExitMonitor(mMonitor); |
|
374 } |
|
375 |
|
376 bool |
|
377 nsDNSSyncRequest::EqualsAsyncListener(nsIDNSListener *aListener) |
|
378 { |
|
379 // Sync request: no listener to compare |
|
380 return false; |
|
381 } |
|
382 |
|
383 size_t |
|
384 nsDNSSyncRequest::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const |
|
385 { |
|
386 size_t n = mallocSizeOf(this); |
|
387 |
|
388 // The following fields aren't measured. |
|
389 // - mHostRecord, because it's a non-owning pointer |
|
390 |
|
391 // Measurement of the following members may be added later if DMD finds it |
|
392 // is worthwhile: |
|
393 // - mMonitor |
|
394 |
|
395 return n; |
|
396 } |
|
397 |
|
398 class NotifyDNSResolution: public nsRunnable |
|
399 { |
|
400 public: |
|
401 NotifyDNSResolution(nsMainThreadPtrHandle<nsIObserverService> &aObs, |
|
402 const nsACString &aHostname) |
|
403 : mObs(aObs) |
|
404 , mHostname(aHostname) |
|
405 { |
|
406 MOZ_ASSERT(mObs); |
|
407 } |
|
408 |
|
409 NS_IMETHOD Run() |
|
410 { |
|
411 MOZ_ASSERT(NS_IsMainThread()); |
|
412 mObs->NotifyObservers(nullptr, |
|
413 "dns-resolution-request", |
|
414 NS_ConvertUTF8toUTF16(mHostname).get()); |
|
415 return NS_OK; |
|
416 } |
|
417 |
|
418 private: |
|
419 nsMainThreadPtrHandle<nsIObserverService> mObs; |
|
420 nsCString mHostname; |
|
421 }; |
|
422 |
|
423 //----------------------------------------------------------------------------- |
|
424 |
|
425 nsDNSService::nsDNSService() |
|
426 : mLock("nsDNSServer.mLock") |
|
427 , mFirstTime(true) |
|
428 , mOffline(false) |
|
429 { |
|
430 } |
|
431 |
|
432 nsDNSService::~nsDNSService() |
|
433 { |
|
434 } |
|
435 |
|
436 NS_IMPL_ISUPPORTS(nsDNSService, nsIDNSService, nsPIDNSService, nsIObserver, |
|
437 nsIMemoryReporter) |
|
438 |
|
439 /****************************************************************************** |
|
440 * nsDNSService impl: |
|
441 * singleton instance ctor/dtor methods |
|
442 ******************************************************************************/ |
|
443 static nsDNSService *gDNSService; |
|
444 |
|
445 nsIDNSService* |
|
446 nsDNSService::GetXPCOMSingleton() |
|
447 { |
|
448 if (IsNeckoChild()) { |
|
449 return ChildDNSService::GetSingleton(); |
|
450 } |
|
451 |
|
452 return GetSingleton(); |
|
453 } |
|
454 |
|
455 nsDNSService* |
|
456 nsDNSService::GetSingleton() |
|
457 { |
|
458 NS_ASSERTION(!IsNeckoChild(), "not a parent process"); |
|
459 |
|
460 if (gDNSService) { |
|
461 NS_ADDREF(gDNSService); |
|
462 return gDNSService; |
|
463 } |
|
464 |
|
465 gDNSService = new nsDNSService(); |
|
466 if (gDNSService) { |
|
467 NS_ADDREF(gDNSService); |
|
468 if (NS_FAILED(gDNSService->Init())) { |
|
469 NS_RELEASE(gDNSService); |
|
470 } |
|
471 } |
|
472 |
|
473 return gDNSService; |
|
474 } |
|
475 |
|
476 NS_IMETHODIMP |
|
477 nsDNSService::Init() |
|
478 { |
|
479 if (mResolver) |
|
480 return NS_OK; |
|
481 NS_ENSURE_TRUE(!mResolver, NS_ERROR_ALREADY_INITIALIZED); |
|
482 |
|
483 // prefs |
|
484 uint32_t maxCacheEntries = 400; |
|
485 uint32_t maxCacheLifetime = 120; // seconds |
|
486 uint32_t lifetimeGracePeriod = 60; // seconds |
|
487 bool disableIPv6 = false; |
|
488 bool disablePrefetch = false; |
|
489 bool disableDNS = false; |
|
490 int proxyType = nsIProtocolProxyService::PROXYCONFIG_DIRECT; |
|
491 bool notifyResolution = false; |
|
492 |
|
493 nsAdoptingCString ipv4OnlyDomains; |
|
494 nsAdoptingCString localDomains; |
|
495 |
|
496 // read prefs |
|
497 nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); |
|
498 if (prefs) { |
|
499 int32_t val; |
|
500 if (NS_SUCCEEDED(prefs->GetIntPref(kPrefDnsCacheEntries, &val))) |
|
501 maxCacheEntries = (uint32_t) val; |
|
502 if (NS_SUCCEEDED(prefs->GetIntPref(kPrefDnsCacheExpiration, &val))) |
|
503 maxCacheLifetime = val; |
|
504 if (NS_SUCCEEDED(prefs->GetIntPref(kPrefDnsCacheGrace, &val))) |
|
505 lifetimeGracePeriod = val; |
|
506 |
|
507 // ASSUMPTION: pref branch does not modify out params on failure |
|
508 prefs->GetBoolPref(kPrefDisableIPv6, &disableIPv6); |
|
509 prefs->GetCharPref(kPrefIPv4OnlyDomains, getter_Copies(ipv4OnlyDomains)); |
|
510 prefs->GetCharPref(kPrefDnsLocalDomains, getter_Copies(localDomains)); |
|
511 prefs->GetBoolPref(kPrefDisablePrefetch, &disablePrefetch); |
|
512 |
|
513 // If a manual proxy is in use, disable prefetch implicitly |
|
514 prefs->GetIntPref("network.proxy.type", &proxyType); |
|
515 |
|
516 // If the user wants remote DNS, we should fail any lookups that still |
|
517 // make it here. |
|
518 prefs->GetBoolPref("network.proxy.socks_remote_dns", &disableDNS); |
|
519 |
|
520 prefs->GetBoolPref(kPrefDnsNotifyResolution, ¬ifyResolution); |
|
521 } |
|
522 |
|
523 if (mFirstTime) { |
|
524 mFirstTime = false; |
|
525 |
|
526 // register as prefs observer |
|
527 if (prefs) { |
|
528 prefs->AddObserver(kPrefDnsCacheEntries, this, false); |
|
529 prefs->AddObserver(kPrefDnsCacheExpiration, this, false); |
|
530 prefs->AddObserver(kPrefDnsCacheGrace, this, false); |
|
531 prefs->AddObserver(kPrefIPv4OnlyDomains, this, false); |
|
532 prefs->AddObserver(kPrefDnsLocalDomains, this, false); |
|
533 prefs->AddObserver(kPrefDisableIPv6, this, false); |
|
534 prefs->AddObserver(kPrefDisablePrefetch, this, false); |
|
535 prefs->AddObserver(kPrefDnsNotifyResolution, this, false); |
|
536 |
|
537 // Monitor these to see if there is a change in proxy configuration |
|
538 // If a manual proxy is in use, disable prefetch implicitly |
|
539 prefs->AddObserver("network.proxy.", this, false); |
|
540 } |
|
541 |
|
542 nsresult rv; |
|
543 nsCOMPtr<nsIObserverService> observerService = |
|
544 do_GetService("@mozilla.org/observer-service;1", &rv); |
|
545 if (NS_SUCCEEDED(rv)) { |
|
546 observerService->AddObserver(this, "last-pb-context-exited", false); |
|
547 } |
|
548 } |
|
549 |
|
550 nsDNSPrefetch::Initialize(this); |
|
551 |
|
552 // Don't initialize the resolver if we're in offline mode. |
|
553 // Later on, the IO service will reinitialize us when going online. |
|
554 if (gIOService->IsOffline() && !gIOService->IsComingOnline()) |
|
555 return NS_OK; |
|
556 |
|
557 nsCOMPtr<nsIIDNService> idn = do_GetService(NS_IDNSERVICE_CONTRACTID); |
|
558 |
|
559 nsCOMPtr<nsIObserverService> obs = |
|
560 do_GetService(NS_OBSERVERSERVICE_CONTRACTID); |
|
561 |
|
562 nsRefPtr<nsHostResolver> res; |
|
563 nsresult rv = nsHostResolver::Create(maxCacheEntries, |
|
564 maxCacheLifetime, |
|
565 lifetimeGracePeriod, |
|
566 getter_AddRefs(res)); |
|
567 if (NS_SUCCEEDED(rv)) { |
|
568 // now, set all of our member variables while holding the lock |
|
569 MutexAutoLock lock(mLock); |
|
570 mResolver = res; |
|
571 mIDN = idn; |
|
572 mIPv4OnlyDomains = ipv4OnlyDomains; // exchanges buffer ownership |
|
573 mDisableIPv6 = disableIPv6; |
|
574 mDisableDNS = disableDNS; |
|
575 |
|
576 // Disable prefetching either by explicit preference or if a manual proxy is configured |
|
577 mDisablePrefetch = disablePrefetch || (proxyType == nsIProtocolProxyService::PROXYCONFIG_MANUAL); |
|
578 |
|
579 mLocalDomains.Clear(); |
|
580 if (localDomains) { |
|
581 nsAdoptingString domains; |
|
582 domains.AssignASCII(nsDependentCString(localDomains).get()); |
|
583 nsCharSeparatedTokenizer tokenizer(domains, ',', |
|
584 nsCharSeparatedTokenizerTemplate<>::SEPARATOR_OPTIONAL); |
|
585 |
|
586 while (tokenizer.hasMoreTokens()) { |
|
587 const nsSubstring& domain = tokenizer.nextToken(); |
|
588 mLocalDomains.PutEntry(nsDependentCString(NS_ConvertUTF16toUTF8(domain).get())); |
|
589 } |
|
590 } |
|
591 mNotifyResolution = notifyResolution; |
|
592 if (mNotifyResolution) { |
|
593 mObserverService = |
|
594 new nsMainThreadPtrHolder<nsIObserverService>(obs); |
|
595 } |
|
596 } |
|
597 |
|
598 RegisterWeakMemoryReporter(this); |
|
599 |
|
600 return rv; |
|
601 } |
|
602 |
|
603 NS_IMETHODIMP |
|
604 nsDNSService::Shutdown() |
|
605 { |
|
606 UnregisterWeakMemoryReporter(this); |
|
607 |
|
608 nsRefPtr<nsHostResolver> res; |
|
609 { |
|
610 MutexAutoLock lock(mLock); |
|
611 res = mResolver; |
|
612 mResolver = nullptr; |
|
613 } |
|
614 if (res) |
|
615 res->Shutdown(); |
|
616 return NS_OK; |
|
617 } |
|
618 |
|
619 NS_IMETHODIMP |
|
620 nsDNSService::GetOffline(bool *offline) |
|
621 { |
|
622 *offline = mOffline; |
|
623 return NS_OK; |
|
624 } |
|
625 |
|
626 NS_IMETHODIMP |
|
627 nsDNSService::SetOffline(bool offline) |
|
628 { |
|
629 mOffline = offline; |
|
630 return NS_OK; |
|
631 } |
|
632 |
|
633 NS_IMETHODIMP |
|
634 nsDNSService::GetPrefetchEnabled(bool *outVal) |
|
635 { |
|
636 *outVal = !mDisablePrefetch; |
|
637 return NS_OK; |
|
638 } |
|
639 |
|
640 NS_IMETHODIMP |
|
641 nsDNSService::SetPrefetchEnabled(bool inVal) |
|
642 { |
|
643 mDisablePrefetch = !inVal; |
|
644 return NS_OK; |
|
645 } |
|
646 |
|
647 |
|
648 NS_IMETHODIMP |
|
649 nsDNSService::AsyncResolve(const nsACString &hostname, |
|
650 uint32_t flags, |
|
651 nsIDNSListener *listener, |
|
652 nsIEventTarget *target_, |
|
653 nsICancelable **result) |
|
654 { |
|
655 // grab reference to global host resolver and IDN service. beware |
|
656 // simultaneous shutdown!! |
|
657 nsRefPtr<nsHostResolver> res; |
|
658 nsCOMPtr<nsIIDNService> idn; |
|
659 nsCOMPtr<nsIEventTarget> target = target_; |
|
660 bool localDomain = false; |
|
661 { |
|
662 MutexAutoLock lock(mLock); |
|
663 |
|
664 if (mDisablePrefetch && (flags & RESOLVE_SPECULATE)) |
|
665 return NS_ERROR_DNS_LOOKUP_QUEUE_FULL; |
|
666 |
|
667 res = mResolver; |
|
668 idn = mIDN; |
|
669 localDomain = mLocalDomains.GetEntry(hostname); |
|
670 } |
|
671 |
|
672 if (mNotifyResolution) { |
|
673 NS_DispatchToMainThread(new NotifyDNSResolution(mObserverService, |
|
674 hostname)); |
|
675 } |
|
676 |
|
677 PRNetAddr tempAddr; |
|
678 if (mDisableDNS) { |
|
679 // Allow IP lookups through, but nothing else. |
|
680 if (PR_StringToNetAddr(hostname.BeginReading(), &tempAddr) != PR_SUCCESS) { |
|
681 return NS_ERROR_UNKNOWN_PROXY_HOST; // XXX: NS_ERROR_NOT_IMPLEMENTED? |
|
682 } |
|
683 } |
|
684 |
|
685 if (!res) |
|
686 return NS_ERROR_OFFLINE; |
|
687 |
|
688 if (mOffline) |
|
689 flags |= RESOLVE_OFFLINE; |
|
690 |
|
691 const nsACString *hostPtr = &hostname; |
|
692 |
|
693 if (localDomain) { |
|
694 hostPtr = &(NS_LITERAL_CSTRING("localhost")); |
|
695 } |
|
696 |
|
697 nsresult rv; |
|
698 nsAutoCString hostACE; |
|
699 if (idn && !IsASCII(*hostPtr)) { |
|
700 if (IsUTF8(*hostPtr) && |
|
701 NS_SUCCEEDED(idn->ConvertUTF8toACE(*hostPtr, hostACE))) { |
|
702 hostPtr = &hostACE; |
|
703 } else { |
|
704 return NS_ERROR_FAILURE; |
|
705 } |
|
706 } |
|
707 |
|
708 // make sure JS callers get notification on the main thread |
|
709 nsCOMPtr<nsIXPConnectWrappedJS> wrappedListener = do_QueryInterface(listener); |
|
710 if (wrappedListener && !target) { |
|
711 nsCOMPtr<nsIThread> mainThread; |
|
712 NS_GetMainThread(getter_AddRefs(mainThread)); |
|
713 target = do_QueryInterface(mainThread); |
|
714 } |
|
715 |
|
716 if (target) { |
|
717 listener = new DNSListenerProxy(listener, target); |
|
718 } |
|
719 |
|
720 uint16_t af = GetAFForLookup(*hostPtr, flags); |
|
721 |
|
722 nsDNSAsyncRequest *req = |
|
723 new nsDNSAsyncRequest(res, *hostPtr, listener, flags, af); |
|
724 if (!req) |
|
725 return NS_ERROR_OUT_OF_MEMORY; |
|
726 NS_ADDREF(*result = req); |
|
727 |
|
728 MOZ_EVENT_TRACER_NAME_OBJECT(req, hostname.BeginReading()); |
|
729 MOZ_EVENT_TRACER_WAIT(req, "net::dns::lookup"); |
|
730 |
|
731 // addref for resolver; will be released when OnLookupComplete is called. |
|
732 NS_ADDREF(req); |
|
733 rv = res->ResolveHost(req->mHost.get(), flags, af, req); |
|
734 if (NS_FAILED(rv)) { |
|
735 NS_RELEASE(req); |
|
736 NS_RELEASE(*result); |
|
737 } |
|
738 return rv; |
|
739 } |
|
740 |
|
741 NS_IMETHODIMP |
|
742 nsDNSService::CancelAsyncResolve(const nsACString &aHostname, |
|
743 uint32_t aFlags, |
|
744 nsIDNSListener *aListener, |
|
745 nsresult aReason) |
|
746 { |
|
747 // grab reference to global host resolver and IDN service. beware |
|
748 // simultaneous shutdown!! |
|
749 nsRefPtr<nsHostResolver> res; |
|
750 nsCOMPtr<nsIIDNService> idn; |
|
751 { |
|
752 MutexAutoLock lock(mLock); |
|
753 |
|
754 if (mDisablePrefetch && (aFlags & RESOLVE_SPECULATE)) |
|
755 return NS_ERROR_DNS_LOOKUP_QUEUE_FULL; |
|
756 |
|
757 res = mResolver; |
|
758 idn = mIDN; |
|
759 } |
|
760 if (!res) |
|
761 return NS_ERROR_OFFLINE; |
|
762 |
|
763 nsCString hostname(aHostname); |
|
764 |
|
765 nsAutoCString hostACE; |
|
766 if (idn && !IsASCII(aHostname)) { |
|
767 if (IsUTF8(aHostname) && |
|
768 NS_SUCCEEDED(idn->ConvertUTF8toACE(aHostname, hostACE))) { |
|
769 hostname = hostACE; |
|
770 } else { |
|
771 return NS_ERROR_FAILURE; |
|
772 } |
|
773 } |
|
774 |
|
775 uint16_t af = GetAFForLookup(hostname, aFlags); |
|
776 |
|
777 res->CancelAsyncRequest(hostname.get(), aFlags, af, aListener, aReason); |
|
778 return NS_OK; |
|
779 } |
|
780 |
|
781 NS_IMETHODIMP |
|
782 nsDNSService::Resolve(const nsACString &hostname, |
|
783 uint32_t flags, |
|
784 nsIDNSRecord **result) |
|
785 { |
|
786 // grab reference to global host resolver and IDN service. beware |
|
787 // simultaneous shutdown!! |
|
788 nsRefPtr<nsHostResolver> res; |
|
789 nsCOMPtr<nsIIDNService> idn; |
|
790 bool localDomain = false; |
|
791 { |
|
792 MutexAutoLock lock(mLock); |
|
793 res = mResolver; |
|
794 idn = mIDN; |
|
795 localDomain = mLocalDomains.GetEntry(hostname); |
|
796 } |
|
797 |
|
798 if (mNotifyResolution) { |
|
799 NS_DispatchToMainThread(new NotifyDNSResolution(mObserverService, |
|
800 hostname)); |
|
801 } |
|
802 |
|
803 NS_ENSURE_TRUE(res, NS_ERROR_OFFLINE); |
|
804 |
|
805 if (mOffline) |
|
806 flags |= RESOLVE_OFFLINE; |
|
807 |
|
808 PRNetAddr tempAddr; |
|
809 if (mDisableDNS) { |
|
810 // Allow IP lookups through, but nothing else. |
|
811 if (PR_StringToNetAddr(hostname.BeginReading(), &tempAddr) != PR_SUCCESS) { |
|
812 return NS_ERROR_UNKNOWN_PROXY_HOST; // XXX: NS_ERROR_NOT_IMPLEMENTED? |
|
813 } |
|
814 } |
|
815 |
|
816 const nsACString *hostPtr = &hostname; |
|
817 |
|
818 if (localDomain) { |
|
819 hostPtr = &(NS_LITERAL_CSTRING("localhost")); |
|
820 } |
|
821 |
|
822 nsresult rv; |
|
823 nsAutoCString hostACE; |
|
824 if (idn && !IsASCII(*hostPtr)) { |
|
825 if (IsUTF8(*hostPtr) && |
|
826 NS_SUCCEEDED(idn->ConvertUTF8toACE(*hostPtr, hostACE))) { |
|
827 hostPtr = &hostACE; |
|
828 } else { |
|
829 return NS_ERROR_FAILURE; |
|
830 } |
|
831 } |
|
832 |
|
833 // |
|
834 // sync resolve: since the host resolver only works asynchronously, we need |
|
835 // to use a mutex and a condvar to wait for the result. however, since the |
|
836 // result may be in the resolvers cache, we might get called back recursively |
|
837 // on the same thread. so, our mutex needs to be re-entrant. in other words, |
|
838 // we need to use a monitor! ;-) |
|
839 // |
|
840 |
|
841 PRMonitor *mon = PR_NewMonitor(); |
|
842 if (!mon) |
|
843 return NS_ERROR_OUT_OF_MEMORY; |
|
844 |
|
845 PR_EnterMonitor(mon); |
|
846 nsDNSSyncRequest syncReq(mon); |
|
847 |
|
848 uint16_t af = GetAFForLookup(*hostPtr, flags); |
|
849 |
|
850 rv = res->ResolveHost(PromiseFlatCString(*hostPtr).get(), flags, af, &syncReq); |
|
851 if (NS_SUCCEEDED(rv)) { |
|
852 // wait for result |
|
853 while (!syncReq.mDone) |
|
854 PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT); |
|
855 |
|
856 if (NS_FAILED(syncReq.mStatus)) |
|
857 rv = syncReq.mStatus; |
|
858 else { |
|
859 NS_ASSERTION(syncReq.mHostRecord, "no host record"); |
|
860 nsDNSRecord *rec = new nsDNSRecord(syncReq.mHostRecord); |
|
861 if (!rec) |
|
862 rv = NS_ERROR_OUT_OF_MEMORY; |
|
863 else |
|
864 NS_ADDREF(*result = rec); |
|
865 } |
|
866 } |
|
867 |
|
868 PR_ExitMonitor(mon); |
|
869 PR_DestroyMonitor(mon); |
|
870 return rv; |
|
871 } |
|
872 |
|
873 NS_IMETHODIMP |
|
874 nsDNSService::GetMyHostName(nsACString &result) |
|
875 { |
|
876 char name[100]; |
|
877 if (PR_GetSystemInfo(PR_SI_HOSTNAME, name, sizeof(name)) == PR_SUCCESS) { |
|
878 result = name; |
|
879 return NS_OK; |
|
880 } |
|
881 return NS_ERROR_FAILURE; |
|
882 } |
|
883 |
|
884 NS_IMETHODIMP |
|
885 nsDNSService::Observe(nsISupports *subject, const char *topic, const char16_t *data) |
|
886 { |
|
887 // we are only getting called if a preference has changed. |
|
888 NS_ASSERTION(strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0 || |
|
889 strcmp(topic, "last-pb-context-exited") == 0, |
|
890 "unexpected observe call"); |
|
891 |
|
892 // |
|
893 // Shutdown and this function are both only called on the UI thread, so we don't |
|
894 // have to worry about mResolver being cleared out from under us. |
|
895 // |
|
896 // NOTE Shutting down and reinitializing the service like this is obviously |
|
897 // suboptimal if Observe gets called several times in a row, but we don't |
|
898 // expect that to be the case. |
|
899 // |
|
900 |
|
901 if (mResolver) { |
|
902 Shutdown(); |
|
903 } |
|
904 Init(); |
|
905 return NS_OK; |
|
906 } |
|
907 |
|
908 uint16_t |
|
909 nsDNSService::GetAFForLookup(const nsACString &host, uint32_t flags) |
|
910 { |
|
911 if (mDisableIPv6 || (flags & RESOLVE_DISABLE_IPV6)) |
|
912 return PR_AF_INET; |
|
913 |
|
914 MutexAutoLock lock(mLock); |
|
915 |
|
916 uint16_t af = PR_AF_UNSPEC; |
|
917 |
|
918 if (!mIPv4OnlyDomains.IsEmpty()) { |
|
919 const char *domain, *domainEnd, *end; |
|
920 uint32_t hostLen, domainLen; |
|
921 |
|
922 // see if host is in one of the IPv4-only domains |
|
923 domain = mIPv4OnlyDomains.BeginReading(); |
|
924 domainEnd = mIPv4OnlyDomains.EndReading(); |
|
925 |
|
926 nsACString::const_iterator hostStart; |
|
927 host.BeginReading(hostStart); |
|
928 hostLen = host.Length(); |
|
929 |
|
930 do { |
|
931 // skip any whitespace |
|
932 while (*domain == ' ' || *domain == '\t') |
|
933 ++domain; |
|
934 |
|
935 // find end of this domain in the string |
|
936 end = strchr(domain, ','); |
|
937 if (!end) |
|
938 end = domainEnd; |
|
939 |
|
940 // to see if the hostname is in the domain, check if the domain |
|
941 // matches the end of the hostname. |
|
942 domainLen = end - domain; |
|
943 if (domainLen && hostLen >= domainLen) { |
|
944 const char *hostTail = hostStart.get() + hostLen - domainLen; |
|
945 if (PL_strncasecmp(domain, hostTail, domainLen) == 0) { |
|
946 // now, make sure either that the hostname is a direct match or |
|
947 // that the hostname begins with a dot. |
|
948 if (hostLen == domainLen || |
|
949 *hostTail == '.' || *(hostTail - 1) == '.') { |
|
950 af = PR_AF_INET; |
|
951 break; |
|
952 } |
|
953 } |
|
954 } |
|
955 |
|
956 domain = end + 1; |
|
957 } while (*end); |
|
958 } |
|
959 |
|
960 if ((af != PR_AF_INET) && (flags & RESOLVE_DISABLE_IPV4)) |
|
961 af = PR_AF_INET6; |
|
962 |
|
963 return af; |
|
964 } |
|
965 |
|
966 NS_IMETHODIMP |
|
967 nsDNSService::GetDNSCacheEntries(nsTArray<mozilla::net::DNSCacheEntries> *args) |
|
968 { |
|
969 NS_ENSURE_TRUE(mResolver, NS_ERROR_NOT_INITIALIZED); |
|
970 mResolver->GetDNSCacheEntries(args); |
|
971 return NS_OK; |
|
972 } |
|
973 |
|
974 static size_t |
|
975 SizeOfLocalDomainsEntryExcludingThis(nsCStringHashKey* entry, |
|
976 MallocSizeOf mallocSizeOf, void*) |
|
977 { |
|
978 return entry->GetKey().SizeOfExcludingThisMustBeUnshared(mallocSizeOf); |
|
979 } |
|
980 |
|
981 size_t |
|
982 nsDNSService::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const |
|
983 { |
|
984 // Measurement of the following members may be added later if DMD finds it |
|
985 // is worthwhile: |
|
986 // - mIDN |
|
987 // - mLock |
|
988 |
|
989 size_t n = mallocSizeOf(this); |
|
990 n += mResolver->SizeOfIncludingThis(mallocSizeOf); |
|
991 n += mIPv4OnlyDomains.SizeOfExcludingThisMustBeUnshared(mallocSizeOf); |
|
992 n += mLocalDomains.SizeOfExcludingThis(SizeOfLocalDomainsEntryExcludingThis, |
|
993 mallocSizeOf); |
|
994 return n; |
|
995 } |
|
996 |
|
997 MOZ_DEFINE_MALLOC_SIZE_OF(DNSServiceMallocSizeOf) |
|
998 |
|
999 NS_IMETHODIMP |
|
1000 nsDNSService::CollectReports(nsIHandleReportCallback* aHandleReport, |
|
1001 nsISupports* aData) |
|
1002 { |
|
1003 return MOZ_COLLECT_REPORT( |
|
1004 "explicit/network/dns-service", KIND_HEAP, UNITS_BYTES, |
|
1005 SizeOfIncludingThis(DNSServiceMallocSizeOf), |
|
1006 "Memory used for the DNS service."); |
|
1007 } |
|
1008 |