Line data Source code
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 : #include "nsIObserverService.h"
34 : #include "nsINetworkLinkService.h"
35 :
36 : #include "mozilla/Attributes.h"
37 : #include "mozilla/net/NeckoCommon.h"
38 : #include "mozilla/net/ChildDNSService.h"
39 : #include "mozilla/net/DNSListenerProxy.h"
40 : #include "mozilla/Services.h"
41 :
42 : using namespace mozilla;
43 : using namespace mozilla::net;
44 :
45 : static const char kPrefDnsCacheEntries[] = "network.dnsCacheEntries";
46 : static const char kPrefDnsCacheExpiration[] = "network.dnsCacheExpiration";
47 : static const char kPrefDnsCacheGrace[] = "network.dnsCacheExpirationGracePeriod";
48 : static const char kPrefIPv4OnlyDomains[] = "network.dns.ipv4OnlyDomains";
49 : static const char kPrefDisableIPv6[] = "network.dns.disableIPv6";
50 : static const char kPrefDisablePrefetch[] = "network.dns.disablePrefetch";
51 : static const char kPrefBlockDotOnion[] = "network.dns.blockDotOnion";
52 : static const char kPrefDnsLocalDomains[] = "network.dns.localDomains";
53 : static const char kPrefDnsForceResolve[] = "network.dns.forceResolve";
54 : static const char kPrefDnsOfflineLocalhost[] = "network.dns.offline-localhost";
55 : static const char kPrefDnsNotifyResolution[] = "network.dns.notifyResolution";
56 :
57 : //-----------------------------------------------------------------------------
58 :
59 : class nsDNSRecord : public nsIDNSRecord
60 : {
61 : public:
62 : NS_DECL_THREADSAFE_ISUPPORTS
63 : NS_DECL_NSIDNSRECORD
64 :
65 3 : explicit nsDNSRecord(nsHostRecord *hostRecord)
66 3 : : mHostRecord(hostRecord)
67 : , mIter(nullptr)
68 : , mIterGenCnt(-1)
69 3 : , mDone(false) {}
70 :
71 : private:
72 6 : virtual ~nsDNSRecord() = default;
73 :
74 : RefPtr<nsHostRecord> mHostRecord;
75 : NetAddrElement *mIter;
76 : int mIterGenCnt; // the generation count of
77 : // mHostRecord->addr_info when we
78 : // start iterating
79 : bool mDone;
80 : };
81 :
82 35 : NS_IMPL_ISUPPORTS(nsDNSRecord, nsIDNSRecord)
83 :
84 : NS_IMETHODIMP
85 0 : nsDNSRecord::GetCanonicalName(nsACString &result)
86 : {
87 : // this method should only be called if we have a CNAME
88 0 : NS_ENSURE_TRUE(mHostRecord->flags & nsHostResolver::RES_CANON_NAME,
89 : NS_ERROR_NOT_AVAILABLE);
90 :
91 : // if the record is for an IP address literal, then the canonical
92 : // host name is the IP address literal.
93 : const char *cname;
94 : {
95 0 : MutexAutoLock lock(mHostRecord->addr_info_lock);
96 0 : if (mHostRecord->addr_info)
97 0 : cname = mHostRecord->addr_info->mCanonicalName ?
98 0 : mHostRecord->addr_info->mCanonicalName :
99 0 : mHostRecord->addr_info->mHostName;
100 : else
101 0 : cname = mHostRecord->host;
102 0 : result.Assign(cname);
103 : }
104 0 : return NS_OK;
105 : }
106 :
107 : NS_IMETHODIMP
108 3 : nsDNSRecord::GetNextAddr(uint16_t port, NetAddr *addr)
109 : {
110 3 : if (mDone) {
111 0 : return NS_ERROR_NOT_AVAILABLE;
112 : }
113 :
114 3 : mHostRecord->addr_info_lock.Lock();
115 3 : if (mHostRecord->addr_info) {
116 2 : if (mIterGenCnt != mHostRecord->addr_info_gencnt) {
117 : // mHostRecord->addr_info has changed, restart the iteration.
118 2 : mIter = nullptr;
119 2 : mIterGenCnt = mHostRecord->addr_info_gencnt;
120 : }
121 :
122 2 : bool startedFresh = !mIter;
123 :
124 2 : do {
125 2 : if (!mIter) {
126 2 : mIter = mHostRecord->addr_info->mAddresses.getFirst();
127 : } else {
128 0 : mIter = mIter->getNext();
129 : }
130 : }
131 2 : while (mIter && mHostRecord->Blacklisted(&mIter->mAddress));
132 :
133 2 : if (!mIter && startedFresh) {
134 : // If everything was blacklisted we want to reset the blacklist (and
135 : // likely relearn it) and return the first address. That is better
136 : // than nothing.
137 0 : mHostRecord->ResetBlacklist();
138 0 : mIter = mHostRecord->addr_info->mAddresses.getFirst();
139 : }
140 :
141 2 : if (mIter) {
142 2 : memcpy(addr, &mIter->mAddress, sizeof(NetAddr));
143 : }
144 :
145 2 : mHostRecord->addr_info_lock.Unlock();
146 :
147 2 : if (!mIter) {
148 0 : mDone = true;
149 0 : return NS_ERROR_NOT_AVAILABLE;
150 : }
151 : } else {
152 1 : mHostRecord->addr_info_lock.Unlock();
153 :
154 1 : if (!mHostRecord->addr) {
155 : // Both mHostRecord->addr_info and mHostRecord->addr are null.
156 : // This can happen if mHostRecord->addr_info expired and the
157 : // attempt to reresolve it failed.
158 0 : return NS_ERROR_NOT_AVAILABLE;
159 : }
160 1 : memcpy(addr, mHostRecord->addr, sizeof(NetAddr));
161 1 : mDone = true;
162 : }
163 :
164 : // set given port
165 3 : port = htons(port);
166 3 : if (addr->raw.family == AF_INET) {
167 3 : addr->inet.port = port;
168 0 : } else if (addr->raw.family == AF_INET6) {
169 0 : addr->inet6.port = port;
170 : }
171 :
172 3 : return NS_OK;
173 : }
174 :
175 : NS_IMETHODIMP
176 0 : nsDNSRecord::GetAddresses(nsTArray<NetAddr> & aAddressArray)
177 : {
178 0 : if (mDone) {
179 0 : return NS_ERROR_NOT_AVAILABLE;
180 : }
181 :
182 0 : mHostRecord->addr_info_lock.Lock();
183 0 : if (mHostRecord->addr_info) {
184 0 : for (NetAddrElement *iter = mHostRecord->addr_info->mAddresses.getFirst();
185 0 : iter; iter = iter->getNext()) {
186 0 : if (mHostRecord->Blacklisted(&iter->mAddress)) {
187 0 : continue;
188 : }
189 0 : NetAddr *addr = aAddressArray.AppendElement(NetAddr());
190 0 : memcpy(addr, &iter->mAddress, sizeof(NetAddr));
191 0 : if (addr->raw.family == AF_INET) {
192 0 : addr->inet.port = 0;
193 0 : } else if (addr->raw.family == AF_INET6) {
194 0 : addr->inet6.port = 0;
195 : }
196 : }
197 0 : mHostRecord->addr_info_lock.Unlock();
198 : } else {
199 0 : mHostRecord->addr_info_lock.Unlock();
200 :
201 0 : if (!mHostRecord->addr) {
202 0 : return NS_ERROR_NOT_AVAILABLE;
203 : }
204 0 : NetAddr *addr = aAddressArray.AppendElement(NetAddr());
205 0 : memcpy(addr, mHostRecord->addr, sizeof(NetAddr));
206 0 : if (addr->raw.family == AF_INET) {
207 0 : addr->inet.port = 0;
208 0 : } else if (addr->raw.family == AF_INET6) {
209 0 : addr->inet6.port = 0;
210 : }
211 : }
212 0 : return NS_OK;
213 : }
214 :
215 : NS_IMETHODIMP
216 0 : nsDNSRecord::GetScriptableNextAddr(uint16_t port, nsINetAddr * *result)
217 : {
218 : NetAddr addr;
219 0 : nsresult rv = GetNextAddr(port, &addr);
220 0 : if (NS_FAILED(rv)) return rv;
221 :
222 0 : NS_ADDREF(*result = new nsNetAddr(&addr));
223 :
224 0 : return NS_OK;
225 : }
226 :
227 : NS_IMETHODIMP
228 0 : nsDNSRecord::GetNextAddrAsString(nsACString &result)
229 : {
230 : NetAddr addr;
231 0 : nsresult rv = GetNextAddr(0, &addr);
232 0 : if (NS_FAILED(rv)) return rv;
233 :
234 : char buf[kIPv6CStrBufSize];
235 0 : if (NetAddrToString(&addr, buf, sizeof(buf))) {
236 0 : result.Assign(buf);
237 0 : return NS_OK;
238 : }
239 0 : NS_ERROR("NetAddrToString failed unexpectedly");
240 0 : return NS_ERROR_FAILURE; // conversion failed for some reason
241 : }
242 :
243 : NS_IMETHODIMP
244 0 : nsDNSRecord::HasMore(bool *result)
245 : {
246 0 : if (mDone) {
247 0 : *result = false;
248 0 : return NS_OK;
249 : }
250 :
251 0 : NetAddrElement *iterCopy = mIter;
252 0 : int iterGenCntCopy = mIterGenCnt;
253 :
254 : NetAddr addr;
255 0 : *result = NS_SUCCEEDED(GetNextAddr(0, &addr));
256 :
257 0 : mIter = iterCopy;
258 0 : mIterGenCnt = iterGenCntCopy;
259 0 : mDone = false;
260 :
261 0 : return NS_OK;
262 : }
263 :
264 : NS_IMETHODIMP
265 0 : nsDNSRecord::Rewind()
266 : {
267 0 : mIter = nullptr;
268 0 : mIterGenCnt = -1;
269 0 : mDone = false;
270 0 : return NS_OK;
271 : }
272 :
273 : NS_IMETHODIMP
274 0 : nsDNSRecord::ReportUnusable(uint16_t aPort)
275 : {
276 : // right now we don't use the port in the blacklist
277 :
278 0 : MutexAutoLock lock(mHostRecord->addr_info_lock);
279 :
280 : // Check that we are using a real addr_info (as opposed to a single
281 : // constant address), and that the generation count is valid. Otherwise,
282 : // ignore the report.
283 :
284 0 : if (mHostRecord->addr_info &&
285 0 : mIterGenCnt == mHostRecord->addr_info_gencnt &&
286 0 : mIter) {
287 0 : mHostRecord->ReportUnusable(&mIter->mAddress);
288 : }
289 :
290 0 : return NS_OK;
291 : }
292 :
293 : //-----------------------------------------------------------------------------
294 :
295 : class nsDNSAsyncRequest final : public nsResolveHostCallback
296 : , public nsICancelable
297 : {
298 3 : ~nsDNSAsyncRequest() = default;
299 :
300 : public:
301 : NS_DECL_THREADSAFE_ISUPPORTS
302 : NS_DECL_NSICANCELABLE
303 :
304 3 : nsDNSAsyncRequest(nsHostResolver *res,
305 : const nsACString &host,
306 : const OriginAttributes &attrs,
307 : nsIDNSListener *listener,
308 : uint16_t flags,
309 : uint16_t af,
310 : const nsACString &netInterface)
311 3 : : mResolver(res)
312 : , mHost(host)
313 : , mOriginAttributes(attrs)
314 : , mListener(listener)
315 : , mFlags(flags)
316 : , mAF(af)
317 3 : , mNetworkInterface(netInterface) {}
318 :
319 : void OnLookupComplete(nsHostResolver *, nsHostRecord *, nsresult) override;
320 : // Returns TRUE if the DNS listener arg is the same as the member listener
321 : // Used in Cancellations to remove DNS requests associated with a
322 : // particular hostname and nsIDNSListener
323 : bool EqualsAsyncListener(nsIDNSListener *aListener) override;
324 :
325 : size_t SizeOfIncludingThis(mozilla::MallocSizeOf) const override;
326 :
327 : RefPtr<nsHostResolver> mResolver;
328 : nsCString mHost; // hostname we're resolving
329 : const OriginAttributes mOriginAttributes; // The originAttributes for this resolving
330 : nsCOMPtr<nsIDNSListener> mListener;
331 : uint16_t mFlags;
332 : uint16_t mAF;
333 : nsCString mNetworkInterface;
334 : };
335 :
336 : void
337 3 : nsDNSAsyncRequest::OnLookupComplete(nsHostResolver *resolver,
338 : nsHostRecord *hostRecord,
339 : nsresult status)
340 : {
341 : // need to have an owning ref when we issue the callback to enable
342 : // the caller to be able to addref/release multiple times without
343 : // destroying the record prematurely.
344 6 : nsCOMPtr<nsIDNSRecord> rec;
345 3 : if (NS_SUCCEEDED(status)) {
346 3 : NS_ASSERTION(hostRecord, "no host record");
347 3 : rec = new nsDNSRecord(hostRecord);
348 : }
349 :
350 3 : mListener->OnLookupComplete(this, rec, status);
351 3 : mListener = nullptr;
352 :
353 : // release the reference to ourselves that was added before we were
354 : // handed off to the host resolver.
355 3 : NS_RELEASE_THIS();
356 3 : }
357 :
358 : bool
359 0 : nsDNSAsyncRequest::EqualsAsyncListener(nsIDNSListener *aListener)
360 : {
361 0 : nsCOMPtr<nsIDNSListenerProxy> wrapper = do_QueryInterface(mListener);
362 0 : if (wrapper) {
363 0 : nsCOMPtr<nsIDNSListener> originalListener;
364 0 : wrapper->GetOriginalListener(getter_AddRefs(originalListener));
365 0 : return aListener == originalListener;
366 : }
367 0 : return (aListener == mListener);
368 : }
369 :
370 : size_t
371 0 : nsDNSAsyncRequest::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const
372 : {
373 0 : size_t n = mallocSizeOf(this);
374 :
375 : // The following fields aren't measured.
376 : // - mHost, because it's a non-owning pointer
377 : // - mResolver, because it's a non-owning pointer
378 : // - mListener, because it's a non-owning pointer
379 :
380 0 : return n;
381 : }
382 :
383 24 : NS_IMPL_ISUPPORTS(nsDNSAsyncRequest, nsICancelable)
384 :
385 : NS_IMETHODIMP
386 0 : nsDNSAsyncRequest::Cancel(nsresult reason)
387 : {
388 0 : NS_ENSURE_ARG(NS_FAILED(reason));
389 0 : mResolver->DetachCallback(mHost.get(), mOriginAttributes, mFlags, mAF,
390 0 : mNetworkInterface.get(), this, reason);
391 0 : return NS_OK;
392 : }
393 :
394 : //-----------------------------------------------------------------------------
395 :
396 : class nsDNSSyncRequest : public nsResolveHostCallback
397 : {
398 : public:
399 0 : explicit nsDNSSyncRequest(PRMonitor *mon)
400 0 : : mDone(false)
401 : , mStatus(NS_OK)
402 0 : , mMonitor(mon) {}
403 0 : virtual ~nsDNSSyncRequest() = default;
404 :
405 : void OnLookupComplete(nsHostResolver *, nsHostRecord *, nsresult) override;
406 : bool EqualsAsyncListener(nsIDNSListener *aListener) override;
407 : size_t SizeOfIncludingThis(mozilla::MallocSizeOf) const override;
408 :
409 : bool mDone;
410 : nsresult mStatus;
411 : RefPtr<nsHostRecord> mHostRecord;
412 :
413 : private:
414 : PRMonitor *mMonitor;
415 : };
416 :
417 : void
418 0 : nsDNSSyncRequest::OnLookupComplete(nsHostResolver *resolver,
419 : nsHostRecord *hostRecord,
420 : nsresult status)
421 : {
422 : // store results, and wake up nsDNSService::Resolve to process results.
423 0 : PR_EnterMonitor(mMonitor);
424 0 : mDone = true;
425 0 : mStatus = status;
426 0 : mHostRecord = hostRecord;
427 0 : PR_Notify(mMonitor);
428 0 : PR_ExitMonitor(mMonitor);
429 0 : }
430 :
431 : bool
432 0 : nsDNSSyncRequest::EqualsAsyncListener(nsIDNSListener *aListener)
433 : {
434 : // Sync request: no listener to compare
435 0 : return false;
436 : }
437 :
438 : size_t
439 0 : nsDNSSyncRequest::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const
440 : {
441 0 : size_t n = mallocSizeOf(this);
442 :
443 : // The following fields aren't measured.
444 : // - mHostRecord, because it's a non-owning pointer
445 :
446 : // Measurement of the following members may be added later if DMD finds it
447 : // is worthwhile:
448 : // - mMonitor
449 :
450 0 : return n;
451 : }
452 :
453 0 : class NotifyDNSResolution: public Runnable
454 : {
455 : public:
456 0 : explicit NotifyDNSResolution(const nsACString& aHostname)
457 0 : : mozilla::Runnable("NotifyDNSResolution")
458 0 : , mHostname(aHostname)
459 : {
460 0 : }
461 :
462 0 : NS_IMETHOD Run() override
463 : {
464 0 : MOZ_ASSERT(NS_IsMainThread());
465 0 : nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
466 0 : if (obs) {
467 0 : obs->NotifyObservers(nullptr,
468 : "dns-resolution-request",
469 0 : NS_ConvertUTF8toUTF16(mHostname).get());
470 : }
471 0 : return NS_OK;
472 : }
473 :
474 : private:
475 : nsCString mHostname;
476 : };
477 :
478 : //-----------------------------------------------------------------------------
479 :
480 1 : nsDNSService::nsDNSService()
481 : : mLock("nsDNSServer.mLock")
482 : , mDisableIPv6(false)
483 : , mDisablePrefetch(false)
484 : , mFirstTime(true)
485 : , mNotifyResolution(false)
486 : , mOfflineLocalhost(false)
487 1 : , mForceResolveOn(false)
488 : {
489 1 : }
490 :
491 : nsDNSService::~nsDNSService() = default;
492 :
493 153 : NS_IMPL_ISUPPORTS(nsDNSService, nsIDNSService, nsPIDNSService, nsIObserver,
494 : nsIMemoryReporter)
495 :
496 : /******************************************************************************
497 : * nsDNSService impl:
498 : * singleton instance ctor/dtor methods
499 : ******************************************************************************/
500 : static nsDNSService *gDNSService;
501 :
502 : nsIDNSService*
503 3 : nsDNSService::GetXPCOMSingleton()
504 : {
505 3 : if (IsNeckoChild()) {
506 2 : return ChildDNSService::GetSingleton();
507 : }
508 :
509 1 : return GetSingleton();
510 : }
511 :
512 : nsDNSService*
513 1 : nsDNSService::GetSingleton()
514 : {
515 1 : NS_ASSERTION(!IsNeckoChild(), "not a parent process");
516 :
517 1 : if (gDNSService) {
518 0 : NS_ADDREF(gDNSService);
519 0 : return gDNSService;
520 : }
521 :
522 1 : gDNSService = new nsDNSService();
523 1 : if (gDNSService) {
524 1 : NS_ADDREF(gDNSService);
525 1 : if (NS_FAILED(gDNSService->Init())) {
526 0 : NS_RELEASE(gDNSService);
527 : }
528 : }
529 :
530 1 : return gDNSService;
531 : }
532 :
533 : NS_IMETHODIMP
534 3 : nsDNSService::Init()
535 : {
536 3 : if (mResolver)
537 1 : return NS_OK;
538 2 : NS_ENSURE_TRUE(!mResolver, NS_ERROR_ALREADY_INITIALIZED);
539 : // prefs
540 2 : uint32_t maxCacheEntries = 400;
541 2 : uint32_t defaultCacheLifetime = 120; // seconds
542 2 : uint32_t defaultGracePeriod = 60; // seconds
543 2 : bool disableIPv6 = false;
544 2 : bool offlineLocalhost = true;
545 2 : bool disablePrefetch = false;
546 2 : bool blockDotOnion = true;
547 2 : int proxyType = nsIProtocolProxyService::PROXYCONFIG_DIRECT;
548 2 : bool notifyResolution = false;
549 :
550 4 : nsAdoptingCString ipv4OnlyDomains;
551 4 : nsAdoptingCString localDomains;
552 4 : nsAdoptingCString forceResolve;
553 :
554 : // read prefs
555 4 : nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
556 2 : if (prefs) {
557 : int32_t val;
558 2 : if (NS_SUCCEEDED(prefs->GetIntPref(kPrefDnsCacheEntries, &val)))
559 2 : maxCacheEntries = (uint32_t) val;
560 2 : if (NS_SUCCEEDED(prefs->GetIntPref(kPrefDnsCacheExpiration, &val)))
561 2 : defaultCacheLifetime = val;
562 2 : if (NS_SUCCEEDED(prefs->GetIntPref(kPrefDnsCacheGrace, &val)))
563 2 : defaultGracePeriod = val;
564 :
565 : // ASSUMPTION: pref branch does not modify out params on failure
566 2 : prefs->GetBoolPref(kPrefDisableIPv6, &disableIPv6);
567 2 : prefs->GetCharPref(kPrefIPv4OnlyDomains, getter_Copies(ipv4OnlyDomains));
568 2 : prefs->GetCharPref(kPrefDnsLocalDomains, getter_Copies(localDomains));
569 2 : prefs->GetCharPref(kPrefDnsForceResolve, getter_Copies(forceResolve));
570 2 : prefs->GetBoolPref(kPrefDnsOfflineLocalhost, &offlineLocalhost);
571 2 : prefs->GetBoolPref(kPrefDisablePrefetch, &disablePrefetch);
572 2 : prefs->GetBoolPref(kPrefBlockDotOnion, &blockDotOnion);
573 :
574 : // If a manual proxy is in use, disable prefetch implicitly
575 2 : prefs->GetIntPref("network.proxy.type", &proxyType);
576 2 : prefs->GetBoolPref(kPrefDnsNotifyResolution, ¬ifyResolution);
577 :
578 2 : if (mFirstTime) {
579 1 : mFirstTime = false;
580 :
581 : // register as prefs observer
582 1 : prefs->AddObserver(kPrefDnsCacheEntries, this, false);
583 1 : prefs->AddObserver(kPrefDnsCacheExpiration, this, false);
584 1 : prefs->AddObserver(kPrefDnsCacheGrace, this, false);
585 1 : prefs->AddObserver(kPrefIPv4OnlyDomains, this, false);
586 1 : prefs->AddObserver(kPrefDnsLocalDomains, this, false);
587 1 : prefs->AddObserver(kPrefDnsForceResolve, this, false);
588 1 : prefs->AddObserver(kPrefDisableIPv6, this, false);
589 1 : prefs->AddObserver(kPrefDnsOfflineLocalhost, this, false);
590 1 : prefs->AddObserver(kPrefDisablePrefetch, this, false);
591 1 : prefs->AddObserver(kPrefBlockDotOnion, this, false);
592 1 : prefs->AddObserver(kPrefDnsNotifyResolution, this, false);
593 :
594 : // Monitor these to see if there is a change in proxy configuration
595 : // If a manual proxy is in use, disable prefetch implicitly
596 1 : prefs->AddObserver("network.proxy.type", this, false);
597 : }
598 : }
599 :
600 : nsCOMPtr<nsIObserverService> observerService =
601 4 : mozilla::services::GetObserverService();
602 2 : if (observerService) {
603 2 : observerService->AddObserver(this, "last-pb-context-exited", false);
604 2 : observerService->AddObserver(this, NS_NETWORK_LINK_TOPIC, false);
605 : }
606 :
607 2 : nsDNSPrefetch::Initialize(this);
608 :
609 4 : nsCOMPtr<nsIIDNService> idn = do_GetService(NS_IDNSERVICE_CONTRACTID);
610 :
611 4 : RefPtr<nsHostResolver> res;
612 2 : nsresult rv = nsHostResolver::Create(maxCacheEntries,
613 : defaultCacheLifetime,
614 : defaultGracePeriod,
615 4 : getter_AddRefs(res));
616 2 : if (NS_SUCCEEDED(rv)) {
617 : // now, set all of our member variables while holding the lock
618 4 : MutexAutoLock lock(mLock);
619 2 : mResolver = res;
620 2 : mIDN = idn;
621 2 : mIPv4OnlyDomains = ipv4OnlyDomains; // exchanges buffer ownership
622 2 : mOfflineLocalhost = offlineLocalhost;
623 2 : mDisableIPv6 = disableIPv6;
624 2 : mBlockDotOnion = blockDotOnion;
625 2 : mForceResolve = forceResolve;
626 2 : mForceResolveOn = !mForceResolve.IsEmpty();
627 :
628 : // Disable prefetching either by explicit preference or if a manual proxy is configured
629 2 : mDisablePrefetch = disablePrefetch || (proxyType == nsIProtocolProxyService::PROXYCONFIG_MANUAL);
630 :
631 2 : mLocalDomains.Clear();
632 2 : if (localDomains) {
633 : nsCCharSeparatedTokenizer tokenizer(localDomains, ',',
634 2 : nsCCharSeparatedTokenizer::SEPARATOR_OPTIONAL);
635 :
636 2 : while (tokenizer.hasMoreTokens()) {
637 0 : mLocalDomains.PutEntry(tokenizer.nextToken());
638 : }
639 : }
640 2 : mNotifyResolution = notifyResolution;
641 : }
642 :
643 2 : RegisterWeakMemoryReporter(this);
644 :
645 2 : return rv;
646 : }
647 :
648 : NS_IMETHODIMP
649 1 : nsDNSService::Shutdown()
650 : {
651 1 : UnregisterWeakMemoryReporter(this);
652 :
653 2 : RefPtr<nsHostResolver> res;
654 : {
655 2 : MutexAutoLock lock(mLock);
656 1 : res = mResolver;
657 1 : mResolver = nullptr;
658 : }
659 1 : if (res) {
660 1 : res->Shutdown();
661 : }
662 :
663 : nsCOMPtr<nsIObserverService> observerService =
664 2 : mozilla::services::GetObserverService();
665 1 : if (observerService) {
666 1 : observerService->RemoveObserver(this, NS_NETWORK_LINK_TOPIC);
667 1 : observerService->RemoveObserver(this, "last-pb-context-exited");
668 : }
669 :
670 2 : return NS_OK;
671 : }
672 :
673 : bool
674 3 : nsDNSService::GetOffline() const
675 : {
676 3 : bool offline = false;
677 6 : nsCOMPtr<nsIIOService> io = do_GetService(NS_IOSERVICE_CONTRACTID);
678 3 : if (io) {
679 3 : io->GetOffline(&offline);
680 : }
681 6 : return offline;
682 : }
683 :
684 : NS_IMETHODIMP
685 0 : nsDNSService::GetPrefetchEnabled(bool *outVal)
686 : {
687 0 : MutexAutoLock lock(mLock);
688 0 : *outVal = !mDisablePrefetch;
689 0 : return NS_OK;
690 : }
691 :
692 : NS_IMETHODIMP
693 0 : nsDNSService::SetPrefetchEnabled(bool inVal)
694 : {
695 0 : MutexAutoLock lock(mLock);
696 0 : mDisablePrefetch = !inVal;
697 0 : return NS_OK;
698 : }
699 :
700 : nsresult
701 3 : nsDNSService::PreprocessHostname(bool aLocalDomain,
702 : const nsACString &aInput,
703 : nsIIDNService *aIDN,
704 : nsACString &aACE)
705 : {
706 : // Enforce RFC 7686
707 12 : if (mBlockDotOnion &&
708 12 : StringEndsWith(aInput, NS_LITERAL_CSTRING(".onion"))) {
709 0 : return NS_ERROR_UNKNOWN_HOST;
710 : }
711 :
712 3 : if (aLocalDomain) {
713 0 : aACE.AssignLiteral("localhost");
714 0 : return NS_OK;
715 : }
716 :
717 3 : if (mForceResolveOn) {
718 0 : MutexAutoLock lock(mLock);
719 0 : if (!aInput.LowerCaseEqualsASCII("localhost") &&
720 0 : !aInput.LowerCaseEqualsASCII("127.0.0.1")) {
721 0 : aACE.Assign(mForceResolve);
722 0 : return NS_OK;
723 : }
724 : }
725 :
726 3 : if (!aIDN || IsASCII(aInput)) {
727 3 : aACE = aInput;
728 3 : return NS_OK;
729 : }
730 :
731 0 : if (!(IsUTF8(aInput) && NS_SUCCEEDED(aIDN->ConvertUTF8toACE(aInput, aACE)))) {
732 0 : return NS_ERROR_FAILURE;
733 : }
734 0 : return NS_OK;
735 : }
736 :
737 : NS_IMETHODIMP
738 0 : nsDNSService::AsyncResolve(const nsACString &aHostname,
739 : uint32_t flags,
740 : nsIDNSListener *listener,
741 : nsIEventTarget *target_,
742 : JS::HandleValue aOriginAttributes,
743 : JSContext *aCx,
744 : uint8_t aArgc,
745 : nsICancelable **result)
746 : {
747 0 : OriginAttributes attrs;
748 :
749 0 : if (aArgc == 1) {
750 0 : if (!aOriginAttributes.isObject() ||
751 0 : !attrs.Init(aCx, aOriginAttributes)) {
752 0 : return NS_ERROR_INVALID_ARG;
753 : }
754 : }
755 :
756 0 : return AsyncResolveExtendedNative(aHostname, flags, EmptyCString(),
757 : listener, target_, attrs,
758 0 : result);
759 : }
760 :
761 : NS_IMETHODIMP
762 4 : nsDNSService::AsyncResolveNative(const nsACString &aHostname,
763 : uint32_t flags,
764 : nsIDNSListener *listener,
765 : nsIEventTarget *target_,
766 : const OriginAttributes &aOriginAttributes,
767 : nsICancelable **result)
768 : {
769 4 : return AsyncResolveExtendedNative(aHostname, flags, EmptyCString(),
770 : listener, target_, aOriginAttributes,
771 4 : result);
772 : }
773 :
774 : NS_IMETHODIMP
775 0 : nsDNSService::AsyncResolveExtended(const nsACString &aHostname,
776 : uint32_t flags,
777 : const nsACString &aNetworkInterface,
778 : nsIDNSListener *listener,
779 : nsIEventTarget *target_,
780 : JS::HandleValue aOriginAttributes,
781 : JSContext *aCx,
782 : uint8_t aArgc,
783 : nsICancelable **result)
784 : {
785 0 : OriginAttributes attrs;
786 :
787 0 : if (aArgc == 1) {
788 0 : if (!aOriginAttributes.isObject() ||
789 0 : !attrs.Init(aCx, aOriginAttributes)) {
790 0 : return NS_ERROR_INVALID_ARG;
791 : }
792 : }
793 :
794 : return AsyncResolveExtendedNative(aHostname, flags, aNetworkInterface,
795 : listener, target_, attrs,
796 0 : result);
797 : }
798 :
799 : NS_IMETHODIMP
800 7 : nsDNSService::AsyncResolveExtendedNative(const nsACString &aHostname,
801 : uint32_t flags,
802 : const nsACString &aNetworkInterface,
803 : nsIDNSListener *listener,
804 : nsIEventTarget *target_,
805 : const OriginAttributes &aOriginAttributes,
806 : nsICancelable **result)
807 : {
808 : // grab reference to global host resolver and IDN service. beware
809 : // simultaneous shutdown!!
810 14 : RefPtr<nsHostResolver> res;
811 14 : nsCOMPtr<nsIIDNService> idn;
812 14 : nsCOMPtr<nsIEventTarget> target = target_;
813 7 : bool localDomain = false;
814 : {
815 10 : MutexAutoLock lock(mLock);
816 :
817 7 : if (mDisablePrefetch && (flags & RESOLVE_SPECULATE))
818 4 : return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
819 :
820 3 : res = mResolver;
821 3 : idn = mIDN;
822 3 : localDomain = mLocalDomains.GetEntry(aHostname);
823 : }
824 :
825 3 : if (mNotifyResolution) {
826 0 : NS_DispatchToMainThread(new NotifyDNSResolution(aHostname));
827 : }
828 :
829 3 : if (!res)
830 0 : return NS_ERROR_OFFLINE;
831 :
832 6 : nsCString hostname;
833 3 : nsresult rv = PreprocessHostname(localDomain, aHostname, idn, hostname);
834 3 : if (NS_FAILED(rv)) {
835 0 : return rv;
836 : }
837 :
838 3 : if (GetOffline() &&
839 0 : (!mOfflineLocalhost || !hostname.LowerCaseEqualsASCII("localhost"))) {
840 0 : flags |= RESOLVE_OFFLINE;
841 : }
842 :
843 : // make sure JS callers get notification on the main thread
844 6 : nsCOMPtr<nsIXPConnectWrappedJS> wrappedListener = do_QueryInterface(listener);
845 3 : if (wrappedListener && !target) {
846 0 : target = GetMainThreadEventTarget();
847 : }
848 :
849 3 : if (target) {
850 0 : listener = new DNSListenerProxy(listener, target);
851 : }
852 :
853 3 : uint16_t af = GetAFForLookup(hostname, flags);
854 :
855 : auto *req =
856 : new nsDNSAsyncRequest(res, hostname, aOriginAttributes, listener, flags, af,
857 6 : aNetworkInterface);
858 3 : if (!req)
859 0 : return NS_ERROR_OUT_OF_MEMORY;
860 3 : NS_ADDREF(*result = req);
861 :
862 : // addref for resolver; will be released when OnLookupComplete is called.
863 3 : NS_ADDREF(req);
864 3 : rv = res->ResolveHost(req->mHost.get(), req->mOriginAttributes, flags, af,
865 3 : req->mNetworkInterface.get(), req);
866 3 : if (NS_FAILED(rv)) {
867 0 : NS_RELEASE(req);
868 0 : NS_RELEASE(*result);
869 : }
870 3 : return rv;
871 : }
872 :
873 : NS_IMETHODIMP
874 0 : nsDNSService::CancelAsyncResolve(const nsACString &aHostname,
875 : uint32_t aFlags,
876 : nsIDNSListener *aListener,
877 : nsresult aReason,
878 : JS::HandleValue aOriginAttributes,
879 : JSContext *aCx,
880 : uint8_t aArgc)
881 : {
882 0 : OriginAttributes attrs;
883 :
884 0 : if (aArgc == 1) {
885 0 : if (!aOriginAttributes.isObject() ||
886 0 : !attrs.Init(aCx, aOriginAttributes)) {
887 0 : return NS_ERROR_INVALID_ARG;
888 : }
889 : }
890 :
891 0 : return CancelAsyncResolveExtendedNative(aHostname, aFlags, EmptyCString(),
892 0 : aListener, aReason, attrs);
893 : }
894 :
895 : NS_IMETHODIMP
896 0 : nsDNSService::CancelAsyncResolveNative(const nsACString &aHostname,
897 : uint32_t aFlags,
898 : nsIDNSListener *aListener,
899 : nsresult aReason,
900 : const OriginAttributes &aOriginAttributes)
901 : {
902 0 : return CancelAsyncResolveExtendedNative(aHostname, aFlags, EmptyCString(),
903 0 : aListener, aReason, aOriginAttributes);
904 : }
905 :
906 : NS_IMETHODIMP
907 0 : nsDNSService::CancelAsyncResolveExtended(const nsACString &aHostname,
908 : uint32_t aFlags,
909 : const nsACString &aNetworkInterface,
910 : nsIDNSListener *aListener,
911 : nsresult aReason,
912 : JS::HandleValue aOriginAttributes,
913 : JSContext *aCx,
914 : uint8_t aArgc)
915 : {
916 0 : OriginAttributes attrs;
917 :
918 0 : if (aArgc == 1) {
919 0 : if (!aOriginAttributes.isObject() ||
920 0 : !attrs.Init(aCx, aOriginAttributes)) {
921 0 : return NS_ERROR_INVALID_ARG;
922 : }
923 : }
924 :
925 : return CancelAsyncResolveExtendedNative(aHostname, aFlags, aNetworkInterface,
926 0 : aListener, aReason, attrs);
927 : }
928 :
929 : NS_IMETHODIMP
930 0 : nsDNSService::CancelAsyncResolveExtendedNative(const nsACString &aHostname,
931 : uint32_t aFlags,
932 : const nsACString &aNetworkInterface,
933 : nsIDNSListener *aListener,
934 : nsresult aReason,
935 : const OriginAttributes &aOriginAttributes)
936 : {
937 : // grab reference to global host resolver and IDN service. beware
938 : // simultaneous shutdown!!
939 0 : RefPtr<nsHostResolver> res;
940 0 : nsCOMPtr<nsIIDNService> idn;
941 0 : bool localDomain = false;
942 : {
943 0 : MutexAutoLock lock(mLock);
944 :
945 0 : if (mDisablePrefetch && (aFlags & RESOLVE_SPECULATE))
946 0 : return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
947 :
948 0 : res = mResolver;
949 0 : idn = mIDN;
950 0 : localDomain = mLocalDomains.GetEntry(aHostname);
951 : }
952 0 : if (!res)
953 0 : return NS_ERROR_OFFLINE;
954 :
955 0 : nsCString hostname;
956 0 : nsresult rv = PreprocessHostname(localDomain, aHostname, idn, hostname);
957 0 : if (NS_FAILED(rv)) {
958 0 : return rv;
959 : }
960 :
961 0 : uint16_t af = GetAFForLookup(hostname, aFlags);
962 :
963 0 : res->CancelAsyncRequest(hostname.get(), aOriginAttributes, aFlags, af,
964 0 : nsPromiseFlatCString(aNetworkInterface).get(), aListener,
965 0 : aReason);
966 0 : return NS_OK;
967 : }
968 :
969 : NS_IMETHODIMP
970 0 : nsDNSService::Resolve(const nsACString &aHostname,
971 : uint32_t flags,
972 : JS::HandleValue aOriginAttributes,
973 : JSContext *aCx,
974 : uint8_t aArgc,
975 : nsIDNSRecord **result)
976 : {
977 0 : OriginAttributes attrs;
978 :
979 0 : if (aArgc == 1) {
980 0 : if (!aOriginAttributes.isObject() ||
981 0 : !attrs.Init(aCx, aOriginAttributes)) {
982 0 : return NS_ERROR_INVALID_ARG;
983 : }
984 : }
985 :
986 0 : return ResolveNative(aHostname, flags, attrs, result);
987 : }
988 :
989 : NS_IMETHODIMP
990 0 : nsDNSService::ResolveNative(const nsACString &aHostname,
991 : uint32_t flags,
992 : const OriginAttributes &aOriginAttributes,
993 : nsIDNSRecord **result)
994 : {
995 : // grab reference to global host resolver and IDN service. beware
996 : // simultaneous shutdown!!
997 0 : RefPtr<nsHostResolver> res;
998 0 : nsCOMPtr<nsIIDNService> idn;
999 0 : bool localDomain = false;
1000 : {
1001 0 : MutexAutoLock lock(mLock);
1002 0 : res = mResolver;
1003 0 : idn = mIDN;
1004 0 : localDomain = mLocalDomains.GetEntry(aHostname);
1005 : }
1006 :
1007 0 : if (mNotifyResolution) {
1008 0 : NS_DispatchToMainThread(new NotifyDNSResolution(aHostname));
1009 : }
1010 :
1011 0 : NS_ENSURE_TRUE(res, NS_ERROR_OFFLINE);
1012 :
1013 0 : nsCString hostname;
1014 0 : nsresult rv = PreprocessHostname(localDomain, aHostname, idn, hostname);
1015 0 : if (NS_FAILED(rv)) {
1016 0 : return rv;
1017 : }
1018 :
1019 0 : if (GetOffline() &&
1020 0 : (!mOfflineLocalhost || !hostname.LowerCaseEqualsASCII("localhost"))) {
1021 0 : flags |= RESOLVE_OFFLINE;
1022 : }
1023 :
1024 : //
1025 : // sync resolve: since the host resolver only works asynchronously, we need
1026 : // to use a mutex and a condvar to wait for the result. however, since the
1027 : // result may be in the resolvers cache, we might get called back recursively
1028 : // on the same thread. so, our mutex needs to be re-entrant. in other words,
1029 : // we need to use a monitor! ;-)
1030 : //
1031 :
1032 0 : PRMonitor *mon = PR_NewMonitor();
1033 0 : if (!mon)
1034 0 : return NS_ERROR_OUT_OF_MEMORY;
1035 :
1036 0 : PR_EnterMonitor(mon);
1037 0 : nsDNSSyncRequest syncReq(mon);
1038 :
1039 0 : uint16_t af = GetAFForLookup(hostname, flags);
1040 :
1041 0 : rv = res->ResolveHost(hostname.get(), aOriginAttributes, flags, af, "", &syncReq);
1042 0 : if (NS_SUCCEEDED(rv)) {
1043 : // wait for result
1044 0 : while (!syncReq.mDone)
1045 0 : PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
1046 :
1047 0 : if (NS_FAILED(syncReq.mStatus))
1048 0 : rv = syncReq.mStatus;
1049 : else {
1050 0 : NS_ASSERTION(syncReq.mHostRecord, "no host record");
1051 0 : auto *rec = new nsDNSRecord(syncReq.mHostRecord);
1052 0 : if (!rec)
1053 0 : rv = NS_ERROR_OUT_OF_MEMORY;
1054 : else
1055 0 : NS_ADDREF(*result = rec);
1056 : }
1057 : }
1058 :
1059 0 : PR_ExitMonitor(mon);
1060 0 : PR_DestroyMonitor(mon);
1061 0 : return rv;
1062 : }
1063 :
1064 : NS_IMETHODIMP
1065 0 : nsDNSService::GetMyHostName(nsACString &result)
1066 : {
1067 : char name[100];
1068 0 : if (PR_GetSystemInfo(PR_SI_HOSTNAME, name, sizeof(name)) == PR_SUCCESS) {
1069 0 : result = name;
1070 0 : return NS_OK;
1071 : }
1072 0 : return NS_ERROR_FAILURE;
1073 : }
1074 :
1075 : NS_IMETHODIMP
1076 1 : nsDNSService::Observe(nsISupports *subject, const char *topic, const char16_t *data)
1077 : {
1078 : // We are only getting called if a preference has changed or there's a
1079 : // network link event.
1080 1 : NS_ASSERTION(strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0 ||
1081 : strcmp(topic, "last-pb-context-exited") == 0 ||
1082 : strcmp(topic, NS_NETWORK_LINK_TOPIC) == 0,
1083 : "unexpected observe call");
1084 :
1085 1 : bool flushCache = false;
1086 1 : if (!strcmp(topic, NS_NETWORK_LINK_TOPIC)) {
1087 0 : nsAutoCString converted = NS_ConvertUTF16toUTF8(data);
1088 0 : if (mResolver && !strcmp(converted.get(), NS_NETWORK_LINK_DATA_CHANGED)) {
1089 0 : flushCache = true;
1090 : }
1091 1 : } else if (!strcmp(topic, "last-pb-context-exited")) {
1092 0 : flushCache = true;
1093 : }
1094 1 : if (flushCache) {
1095 0 : mResolver->FlushCache();
1096 0 : return NS_OK;
1097 : }
1098 :
1099 : //
1100 : // Shutdown and this function are both only called on the UI thread, so we don't
1101 : // have to worry about mResolver being cleared out from under us.
1102 : //
1103 : // NOTE Shutting down and reinitializing the service like this is obviously
1104 : // suboptimal if Observe gets called several times in a row, but we don't
1105 : // expect that to be the case.
1106 : //
1107 :
1108 1 : if (mResolver) {
1109 1 : Shutdown();
1110 : }
1111 1 : Init();
1112 1 : return NS_OK;
1113 : }
1114 :
1115 : uint16_t
1116 3 : nsDNSService::GetAFForLookup(const nsACString &host, uint32_t flags)
1117 : {
1118 3 : if (mDisableIPv6 || (flags & RESOLVE_DISABLE_IPV6))
1119 1 : return PR_AF_INET;
1120 :
1121 4 : MutexAutoLock lock(mLock);
1122 :
1123 2 : uint16_t af = PR_AF_UNSPEC;
1124 :
1125 2 : if (!mIPv4OnlyDomains.IsEmpty()) {
1126 : const char *domain, *domainEnd, *end;
1127 : uint32_t hostLen, domainLen;
1128 :
1129 : // see if host is in one of the IPv4-only domains
1130 0 : domain = mIPv4OnlyDomains.BeginReading();
1131 0 : domainEnd = mIPv4OnlyDomains.EndReading();
1132 :
1133 0 : nsACString::const_iterator hostStart;
1134 0 : host.BeginReading(hostStart);
1135 0 : hostLen = host.Length();
1136 :
1137 0 : do {
1138 : // skip any whitespace
1139 0 : while (*domain == ' ' || *domain == '\t')
1140 0 : ++domain;
1141 :
1142 : // find end of this domain in the string
1143 0 : end = strchr(domain, ',');
1144 0 : if (!end)
1145 0 : end = domainEnd;
1146 :
1147 : // to see if the hostname is in the domain, check if the domain
1148 : // matches the end of the hostname.
1149 0 : domainLen = end - domain;
1150 0 : if (domainLen && hostLen >= domainLen) {
1151 0 : const char *hostTail = hostStart.get() + hostLen - domainLen;
1152 0 : if (PL_strncasecmp(domain, hostTail, domainLen) == 0) {
1153 : // now, make sure either that the hostname is a direct match or
1154 : // that the hostname begins with a dot.
1155 0 : if (hostLen == domainLen ||
1156 0 : *hostTail == '.' || *(hostTail - 1) == '.') {
1157 0 : af = PR_AF_INET;
1158 0 : break;
1159 : }
1160 : }
1161 : }
1162 :
1163 0 : domain = end + 1;
1164 0 : } while (*end);
1165 : }
1166 :
1167 2 : if ((af != PR_AF_INET) && (flags & RESOLVE_DISABLE_IPV4))
1168 0 : af = PR_AF_INET6;
1169 :
1170 2 : return af;
1171 : }
1172 :
1173 : NS_IMETHODIMP
1174 0 : nsDNSService::GetDNSCacheEntries(nsTArray<mozilla::net::DNSCacheEntries> *args)
1175 : {
1176 0 : NS_ENSURE_TRUE(mResolver, NS_ERROR_NOT_INITIALIZED);
1177 0 : mResolver->GetDNSCacheEntries(args);
1178 0 : return NS_OK;
1179 : }
1180 :
1181 : size_t
1182 0 : nsDNSService::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
1183 : {
1184 : // Measurement of the following members may be added later if DMD finds it
1185 : // is worthwhile:
1186 : // - mIDN
1187 : // - mLock
1188 :
1189 0 : size_t n = mallocSizeOf(this);
1190 0 : n += mResolver ? mResolver->SizeOfIncludingThis(mallocSizeOf) : 0;
1191 0 : n += mIPv4OnlyDomains.SizeOfExcludingThisIfUnshared(mallocSizeOf);
1192 0 : n += mLocalDomains.SizeOfExcludingThis(mallocSizeOf);
1193 0 : return n;
1194 : }
1195 :
1196 0 : MOZ_DEFINE_MALLOC_SIZE_OF(DNSServiceMallocSizeOf)
1197 :
1198 : NS_IMETHODIMP
1199 0 : nsDNSService::CollectReports(nsIHandleReportCallback* aHandleReport,
1200 : nsISupports* aData, bool aAnonymize)
1201 : {
1202 0 : MOZ_COLLECT_REPORT(
1203 : "explicit/network/dns-service", KIND_HEAP, UNITS_BYTES,
1204 : SizeOfIncludingThis(DNSServiceMallocSizeOf),
1205 0 : "Memory used for the DNS service.");
1206 :
1207 0 : return NS_OK;
1208 : }
1209 :
|