1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "mozilla/net/ChildDNSService.h"
6 #include "nsIDNSListener.h"
9 #include "nsThreadUtils.h"
10 #include "nsIXPConnect.h"
11 #include "nsIPrefService.h"
12 #include "nsIProtocolProxyService.h"
13 #include "mozilla/net/NeckoChild.h"
14 #include "mozilla/net/DNSListenerProxy.h"
19 //-----------------------------------------------------------------------------
21 //-----------------------------------------------------------------------------
23 static ChildDNSService
*gChildDNSService
;
24 static const char kPrefNameDisablePrefetch
[] = "network.dns.disablePrefetch";
26 ChildDNSService
* ChildDNSService::GetSingleton()
28 MOZ_ASSERT(IsNeckoChild());
30 if (!gChildDNSService
) {
31 gChildDNSService
= new ChildDNSService();
34 NS_ADDREF(gChildDNSService
);
35 return gChildDNSService
;
38 NS_IMPL_ISUPPORTS(ChildDNSService
,
43 ChildDNSService::ChildDNSService()
46 , mPendingRequestsLock("DNSPendingRequestsLock")
48 MOZ_ASSERT(IsNeckoChild());
51 ChildDNSService::~ChildDNSService()
57 ChildDNSService::GetDNSRecordHashKey(const nsACString
&aHost
,
59 const nsACString
&aNetworkInterface
,
60 nsIDNSListener
* aListener
,
63 aHashKey
.Assign(aHost
);
64 aHashKey
.AppendInt(aFlags
);
65 if (!aNetworkInterface
.IsEmpty()) {
66 aHashKey
.Append(aNetworkInterface
);
68 aHashKey
.AppendPrintf("%p", aListener
);
71 //-----------------------------------------------------------------------------
72 // ChildDNSService::nsIDNSService
73 //-----------------------------------------------------------------------------
76 ChildDNSService::AsyncResolve(const nsACString
&hostname
,
78 nsIDNSListener
*listener
,
79 nsIEventTarget
*target_
,
80 nsICancelable
**result
)
82 return AsyncResolveExtended(hostname
, flags
, EmptyCString(), listener
,
87 ChildDNSService::AsyncResolveExtended(const nsACString
&hostname
,
89 const nsACString
&aNetworkInterface
,
90 nsIDNSListener
*listener
,
91 nsIEventTarget
*target_
,
92 nsICancelable
**result
)
94 NS_ENSURE_TRUE(gNeckoChild
!= nullptr, NS_ERROR_FAILURE
);
96 if (mDisablePrefetch
&& (flags
& RESOLVE_SPECULATE
)) {
97 return NS_ERROR_DNS_LOOKUP_QUEUE_FULL
;
100 // We need original flags for the pending requests hash.
101 uint32_t originalFlags
= flags
;
103 // Support apps being 'offline' even if parent is not: avoids DNS traffic by
104 // apps that have been told they are offline.
106 flags
|= RESOLVE_OFFLINE
;
109 // We need original listener for the pending requests hash.
110 nsIDNSListener
*originalListener
= listener
;
112 // make sure JS callers get notification on the main thread
113 nsCOMPtr
<nsIEventTarget
> target
= target_
;
114 nsCOMPtr
<nsIXPConnectWrappedJS
> wrappedListener
= do_QueryInterface(listener
);
115 if (wrappedListener
&& !target
) {
116 nsCOMPtr
<nsIThread
> mainThread
;
117 NS_GetMainThread(getter_AddRefs(mainThread
));
118 target
= do_QueryInterface(mainThread
);
121 // Guarantee listener freed on main thread. Not sure we need this in child
122 // (or in parent in nsDNSService.cpp) but doesn't hurt.
123 listener
= new DNSListenerProxy(listener
, target
);
126 nsRefPtr
<DNSRequestChild
> childReq
=
127 new DNSRequestChild(nsCString(hostname
), flags
,
128 nsCString(aNetworkInterface
),
132 MutexAutoLock
lock(mPendingRequestsLock
);
134 GetDNSRecordHashKey(hostname
, originalFlags
, aNetworkInterface
,
135 originalListener
, key
);
136 nsTArray
<nsRefPtr
<DNSRequestChild
>> *hashEntry
;
137 if (mPendingRequests
.Get(key
, &hashEntry
)) {
138 hashEntry
->AppendElement(childReq
);
140 hashEntry
= new nsTArray
<nsRefPtr
<DNSRequestChild
>>();
141 hashEntry
->AppendElement(childReq
);
142 mPendingRequests
.Put(key
, hashEntry
);
146 childReq
->StartRequest();
148 childReq
.forget(result
);
153 ChildDNSService::CancelAsyncResolve(const nsACString
&aHostname
,
155 nsIDNSListener
*aListener
,
158 return CancelAsyncResolveExtended(aHostname
, aFlags
, EmptyCString(),
163 ChildDNSService::CancelAsyncResolveExtended(const nsACString
&aHostname
,
165 const nsACString
&aNetworkInterface
,
166 nsIDNSListener
*aListener
,
169 if (mDisablePrefetch
&& (aFlags
& RESOLVE_SPECULATE
)) {
170 return NS_ERROR_DNS_LOOKUP_QUEUE_FULL
;
173 MutexAutoLock
lock(mPendingRequestsLock
);
174 nsTArray
<nsRefPtr
<DNSRequestChild
>> *hashEntry
;
176 GetDNSRecordHashKey(aHostname
, aFlags
, aNetworkInterface
, aListener
, key
);
177 if (mPendingRequests
.Get(key
, &hashEntry
)) {
178 // We cancel just one.
179 hashEntry
->ElementAt(0)->Cancel(aReason
);
186 ChildDNSService::Resolve(const nsACString
&hostname
,
188 nsIDNSRecord
**result
)
190 // not planning to ever support this, since sync IPDL is evil.
191 return NS_ERROR_NOT_AVAILABLE
;
195 ChildDNSService::GetDNSCacheEntries(nsTArray
<mozilla::net::DNSCacheEntries
> *args
)
197 // Only used by networking dashboard, so may not ever need this in child.
198 // (and would provide a way to spy on what hosts other apps are connecting to,
199 // unless we start keeping per-app DNS caches).
200 return NS_ERROR_NOT_AVAILABLE
;
204 ChildDNSService::GetMyHostName(nsACString
&result
)
206 // TODO: get value from parent during PNecko construction?
207 return NS_ERROR_NOT_AVAILABLE
;
211 ChildDNSService::NotifyRequestDone(DNSRequestChild
*aDnsRequest
)
213 // We need the original flags and listener for the pending requests hash.
214 uint32_t originalFlags
= aDnsRequest
->mFlags
& ~RESOLVE_OFFLINE
;
215 nsCOMPtr
<nsIDNSListener
> originalListener
= aDnsRequest
->mListener
;
216 nsCOMPtr
<nsIDNSListenerProxy
> wrapper
= do_QueryInterface(originalListener
);
218 wrapper
->GetOriginalListener(getter_AddRefs(originalListener
));
219 if (NS_WARN_IF(!originalListener
)) {
220 MOZ_ASSERT(originalListener
);
225 MutexAutoLock
lock(mPendingRequestsLock
);
228 GetDNSRecordHashKey(aDnsRequest
->mHost
, originalFlags
,
229 aDnsRequest
->mNetworkInterface
, originalListener
, key
);
231 nsTArray
<nsRefPtr
<DNSRequestChild
>> *hashEntry
;
233 if (mPendingRequests
.Get(key
, &hashEntry
)) {
235 if ((idx
= hashEntry
->IndexOf(aDnsRequest
))) {
236 hashEntry
->RemoveElementAt(idx
);
237 if (hashEntry
->IsEmpty()) {
238 mPendingRequests
.Remove(key
);
244 //-----------------------------------------------------------------------------
245 // ChildDNSService::nsPIDNSService
246 //-----------------------------------------------------------------------------
249 ChildDNSService::Init()
251 // Disable prefetching either by explicit preference or if a manual proxy
253 bool disablePrefetch
= false;
254 int proxyType
= nsIProtocolProxyService::PROXYCONFIG_DIRECT
;
256 nsCOMPtr
<nsIPrefBranch
> prefs
= do_GetService(NS_PREFSERVICE_CONTRACTID
);
257 prefs
->GetBoolPref(kPrefNameDisablePrefetch
, &disablePrefetch
);
259 prefs
->GetIntPref("network.proxy.type", &proxyType
);
260 prefs
->GetBoolPref(kPrefNameDisablePrefetch
, &disablePrefetch
);
266 prefs
->AddObserver(kPrefNameDisablePrefetch
, this, false);
268 // Monitor these to see if there is a change in proxy configuration
269 // If a manual proxy is in use, disable prefetch implicitly
270 prefs
->AddObserver("network.proxy.type", this, false);
274 mDisablePrefetch
= disablePrefetch
||
275 (proxyType
== nsIProtocolProxyService::PROXYCONFIG_MANUAL
);
281 ChildDNSService::Shutdown()
287 ChildDNSService::GetPrefetchEnabled(bool *outVal
)
289 *outVal
= !mDisablePrefetch
;
294 ChildDNSService::SetPrefetchEnabled(bool inVal
)
296 mDisablePrefetch
= !inVal
;
301 ChildDNSService::GetOffline(bool* aResult
)
308 ChildDNSService::SetOffline(bool value
)
314 //-----------------------------------------------------------------------------
315 // ChildDNSService::nsIObserver
316 //-----------------------------------------------------------------------------
319 ChildDNSService::Observe(nsISupports
*subject
, const char *topic
,
320 const char16_t
*data
)
322 // we are only getting called if a preference has changed.
323 NS_ASSERTION(strcmp(topic
, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID
) == 0,
324 "unexpected observe call");
332 } // namespace mozilla