Line data Source code
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/. */
4 :
5 : #include "mozilla/net/ChildDNSService.h"
6 : #include "nsIDNSListener.h"
7 : #include "nsIIOService.h"
8 : #include "nsIThread.h"
9 : #include "nsThreadUtils.h"
10 : #include "nsIXPConnect.h"
11 : #include "nsIPrefService.h"
12 : #include "nsIProtocolProxyService.h"
13 : #include "nsNetCID.h"
14 : #include "mozilla/SystemGroup.h"
15 : #include "mozilla/net/NeckoChild.h"
16 : #include "mozilla/net/DNSListenerProxy.h"
17 : #include "nsServiceManagerUtils.h"
18 :
19 : namespace mozilla {
20 : namespace net {
21 :
22 : //-----------------------------------------------------------------------------
23 : // ChildDNSService
24 : //-----------------------------------------------------------------------------
25 :
26 : static ChildDNSService *gChildDNSService;
27 : static const char kPrefNameDisablePrefetch[] = "network.dns.disablePrefetch";
28 :
29 2 : ChildDNSService* ChildDNSService::GetSingleton()
30 : {
31 2 : MOZ_ASSERT(IsNeckoChild());
32 :
33 2 : if (!gChildDNSService) {
34 2 : gChildDNSService = new ChildDNSService();
35 : }
36 :
37 2 : NS_ADDREF(gChildDNSService);
38 2 : return gChildDNSService;
39 : }
40 :
41 66 : NS_IMPL_ISUPPORTS(ChildDNSService,
42 : nsIDNSService,
43 : nsPIDNSService,
44 : nsIObserver)
45 :
46 2 : ChildDNSService::ChildDNSService()
47 : : mFirstTime(true)
48 : , mDisablePrefetch(false)
49 2 : , mPendingRequestsLock("DNSPendingRequestsLock")
50 : {
51 2 : MOZ_ASSERT(IsNeckoChild());
52 2 : }
53 :
54 0 : ChildDNSService::~ChildDNSService()
55 : {
56 :
57 0 : }
58 :
59 : void
60 0 : ChildDNSService::GetDNSRecordHashKey(const nsACString &aHost,
61 : const OriginAttributes &aOriginAttributes,
62 : uint32_t aFlags,
63 : const nsACString &aNetworkInterface,
64 : nsIDNSListener* aListener,
65 : nsACString &aHashKey)
66 : {
67 0 : aHashKey.Assign(aHost);
68 :
69 0 : nsAutoCString originSuffix;
70 0 : aOriginAttributes.CreateSuffix(originSuffix);
71 0 : aHashKey.Assign(originSuffix);
72 :
73 0 : aHashKey.AppendInt(aFlags);
74 0 : if (!aNetworkInterface.IsEmpty()) {
75 0 : aHashKey.Append(aNetworkInterface);
76 : }
77 0 : aHashKey.AppendPrintf("%p", aListener);
78 0 : }
79 :
80 : //-----------------------------------------------------------------------------
81 : // ChildDNSService::nsIDNSService
82 : //-----------------------------------------------------------------------------
83 :
84 : NS_IMETHODIMP
85 0 : ChildDNSService::AsyncResolve(const nsACString &hostname,
86 : uint32_t flags,
87 : nsIDNSListener *listener,
88 : nsIEventTarget *target_,
89 : JS::HandleValue aOriginAttributes,
90 : JSContext *aCx,
91 : uint8_t aArgc,
92 : nsICancelable **result)
93 : {
94 0 : OriginAttributes attrs;
95 :
96 0 : if (aArgc == 1) {
97 0 : if (!aOriginAttributes.isObject() ||
98 0 : !attrs.Init(aCx, aOriginAttributes)) {
99 0 : return NS_ERROR_INVALID_ARG;
100 : }
101 : }
102 :
103 0 : return AsyncResolveExtendedNative(hostname, flags, EmptyCString(),
104 : listener, target_, attrs,
105 0 : result);
106 : }
107 :
108 : NS_IMETHODIMP
109 0 : ChildDNSService::AsyncResolveNative(const nsACString &hostname,
110 : uint32_t flags,
111 : nsIDNSListener *listener,
112 : nsIEventTarget *target_,
113 : const OriginAttributes &aOriginAttributes,
114 : nsICancelable **result)
115 : {
116 0 : return AsyncResolveExtendedNative(hostname, flags, EmptyCString(),
117 : listener, target_, aOriginAttributes,
118 0 : result);
119 : }
120 :
121 : NS_IMETHODIMP
122 0 : ChildDNSService::AsyncResolveExtended(const nsACString &aHostname,
123 : uint32_t flags,
124 : const nsACString &aNetworkInterface,
125 : nsIDNSListener *listener,
126 : nsIEventTarget *target_,
127 : JS::HandleValue aOriginAttributes,
128 : JSContext *aCx,
129 : uint8_t aArgc,
130 : nsICancelable **result)
131 : {
132 0 : OriginAttributes attrs;
133 :
134 0 : if (aArgc == 1) {
135 0 : if (!aOriginAttributes.isObject() ||
136 0 : !attrs.Init(aCx, aOriginAttributes)) {
137 0 : return NS_ERROR_INVALID_ARG;
138 : }
139 : }
140 :
141 : return AsyncResolveExtendedNative(aHostname, flags, aNetworkInterface,
142 : listener, target_, attrs,
143 0 : result);
144 : }
145 :
146 : NS_IMETHODIMP
147 0 : ChildDNSService::AsyncResolveExtendedNative(const nsACString &hostname,
148 : uint32_t flags,
149 : const nsACString &aNetworkInterface,
150 : nsIDNSListener *listener,
151 : nsIEventTarget *target_,
152 : const OriginAttributes &aOriginAttributes,
153 : nsICancelable **result)
154 : {
155 0 : NS_ENSURE_TRUE(gNeckoChild != nullptr, NS_ERROR_FAILURE);
156 :
157 0 : if (mDisablePrefetch && (flags & RESOLVE_SPECULATE)) {
158 0 : return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
159 : }
160 :
161 : // We need original flags for the pending requests hash.
162 0 : uint32_t originalFlags = flags;
163 :
164 : // Support apps being 'offline' even if parent is not: avoids DNS traffic by
165 : // apps that have been told they are offline.
166 0 : if (GetOffline()) {
167 0 : flags |= RESOLVE_OFFLINE;
168 : }
169 :
170 : // We need original listener for the pending requests hash.
171 0 : nsIDNSListener *originalListener = listener;
172 :
173 : // make sure JS callers get notification on the main thread
174 0 : nsCOMPtr<nsIEventTarget> target = target_;
175 0 : nsCOMPtr<nsIXPConnectWrappedJS> wrappedListener = do_QueryInterface(listener);
176 0 : if (wrappedListener && !target) {
177 0 : target = SystemGroup::EventTargetFor(TaskCategory::Network);
178 : }
179 0 : if (target) {
180 : // Guarantee listener freed on main thread. Not sure we need this in child
181 : // (or in parent in nsDNSService.cpp) but doesn't hurt.
182 0 : listener = new DNSListenerProxy(listener, target);
183 : }
184 :
185 : RefPtr<DNSRequestChild> childReq =
186 0 : new DNSRequestChild(nsCString(hostname),
187 : aOriginAttributes,
188 : flags,
189 0 : nsCString(aNetworkInterface),
190 0 : listener, target);
191 :
192 : {
193 0 : MutexAutoLock lock(mPendingRequestsLock);
194 0 : nsCString key;
195 : GetDNSRecordHashKey(hostname, aOriginAttributes, originalFlags, aNetworkInterface,
196 0 : originalListener, key);
197 : nsTArray<RefPtr<DNSRequestChild>> *hashEntry;
198 0 : if (mPendingRequests.Get(key, &hashEntry)) {
199 0 : hashEntry->AppendElement(childReq);
200 : } else {
201 0 : hashEntry = new nsTArray<RefPtr<DNSRequestChild>>();
202 0 : hashEntry->AppendElement(childReq);
203 0 : mPendingRequests.Put(key, hashEntry);
204 : }
205 : }
206 :
207 0 : childReq->StartRequest();
208 :
209 0 : childReq.forget(result);
210 0 : return NS_OK;
211 : }
212 :
213 : NS_IMETHODIMP
214 0 : ChildDNSService::CancelAsyncResolve(const nsACString &aHostname,
215 : uint32_t aFlags,
216 : nsIDNSListener *aListener,
217 : nsresult aReason,
218 : JS::HandleValue aOriginAttributes,
219 : JSContext *aCx,
220 : uint8_t aArgc)
221 : {
222 0 : OriginAttributes attrs;
223 :
224 0 : if (aArgc == 1) {
225 0 : if (!aOriginAttributes.isObject() ||
226 0 : !attrs.Init(aCx, aOriginAttributes)) {
227 0 : return NS_ERROR_INVALID_ARG;
228 : }
229 : }
230 :
231 0 : return CancelAsyncResolveExtendedNative(aHostname, aFlags, EmptyCString(),
232 0 : aListener, aReason, attrs);
233 : }
234 :
235 : NS_IMETHODIMP
236 0 : ChildDNSService::CancelAsyncResolveNative(const nsACString &aHostname,
237 : uint32_t aFlags,
238 : nsIDNSListener *aListener,
239 : nsresult aReason,
240 : const OriginAttributes &aOriginAttributes)
241 : {
242 0 : return CancelAsyncResolveExtendedNative(aHostname, aFlags, EmptyCString(),
243 0 : aListener, aReason, aOriginAttributes);
244 : }
245 :
246 : NS_IMETHODIMP
247 0 : ChildDNSService::CancelAsyncResolveExtended(const nsACString &aHostname,
248 : uint32_t aFlags,
249 : const nsACString &aNetworkInterface,
250 : nsIDNSListener *aListener,
251 : nsresult aReason,
252 : JS::HandleValue aOriginAttributes,
253 : JSContext *aCx,
254 : uint8_t aArgc)
255 : {
256 0 : OriginAttributes attrs;
257 :
258 0 : if (aArgc == 1) {
259 0 : if (!aOriginAttributes.isObject() ||
260 0 : !attrs.Init(aCx, aOriginAttributes)) {
261 0 : return NS_ERROR_INVALID_ARG;
262 : }
263 : }
264 :
265 : return CancelAsyncResolveExtendedNative(aHostname, aFlags, aNetworkInterface,
266 0 : aListener, aReason, attrs);
267 : }
268 :
269 : NS_IMETHODIMP
270 0 : ChildDNSService::CancelAsyncResolveExtendedNative(const nsACString &aHostname,
271 : uint32_t aFlags,
272 : const nsACString &aNetworkInterface,
273 : nsIDNSListener *aListener,
274 : nsresult aReason,
275 : const OriginAttributes &aOriginAttributes)
276 : {
277 0 : if (mDisablePrefetch && (aFlags & RESOLVE_SPECULATE)) {
278 0 : return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
279 : }
280 :
281 0 : MutexAutoLock lock(mPendingRequestsLock);
282 : nsTArray<RefPtr<DNSRequestChild>> *hashEntry;
283 0 : nsCString key;
284 : GetDNSRecordHashKey(aHostname, aOriginAttributes, aFlags,
285 0 : aNetworkInterface, aListener, key);
286 0 : if (mPendingRequests.Get(key, &hashEntry)) {
287 : // We cancel just one.
288 0 : hashEntry->ElementAt(0)->Cancel(aReason);
289 : }
290 :
291 0 : return NS_OK;
292 : }
293 :
294 : NS_IMETHODIMP
295 0 : ChildDNSService::Resolve(const nsACString &hostname,
296 : uint32_t flags,
297 : JS::HandleValue aOriginAttributes,
298 : JSContext *aCx,
299 : uint8_t aArgc,
300 : nsIDNSRecord **result)
301 : {
302 : // not planning to ever support this, since sync IPDL is evil.
303 0 : return NS_ERROR_NOT_AVAILABLE;
304 : }
305 :
306 : NS_IMETHODIMP
307 0 : ChildDNSService::ResolveNative(const nsACString &hostname,
308 : uint32_t flags,
309 : const OriginAttributes &aOriginAttributes,
310 : nsIDNSRecord **result)
311 : {
312 : // not planning to ever support this, since sync IPDL is evil.
313 0 : return NS_ERROR_NOT_AVAILABLE;
314 : }
315 :
316 : NS_IMETHODIMP
317 0 : ChildDNSService::GetDNSCacheEntries(nsTArray<mozilla::net::DNSCacheEntries> *args)
318 : {
319 : // Only used by networking dashboard, so may not ever need this in child.
320 : // (and would provide a way to spy on what hosts other apps are connecting to,
321 : // unless we start keeping per-app DNS caches).
322 0 : return NS_ERROR_NOT_AVAILABLE;
323 : }
324 :
325 : NS_IMETHODIMP
326 0 : ChildDNSService::GetMyHostName(nsACString &result)
327 : {
328 : // TODO: get value from parent during PNecko construction?
329 0 : return NS_ERROR_NOT_AVAILABLE;
330 : }
331 :
332 : void
333 0 : ChildDNSService::NotifyRequestDone(DNSRequestChild *aDnsRequest)
334 : {
335 : // We need the original flags and listener for the pending requests hash.
336 0 : uint32_t originalFlags = aDnsRequest->mFlags & ~RESOLVE_OFFLINE;
337 0 : nsCOMPtr<nsIDNSListener> originalListener = aDnsRequest->mListener;
338 0 : nsCOMPtr<nsIDNSListenerProxy> wrapper = do_QueryInterface(originalListener);
339 0 : if (wrapper) {
340 0 : wrapper->GetOriginalListener(getter_AddRefs(originalListener));
341 0 : if (NS_WARN_IF(!originalListener)) {
342 0 : MOZ_ASSERT(originalListener);
343 0 : return;
344 : }
345 : }
346 :
347 0 : MutexAutoLock lock(mPendingRequestsLock);
348 :
349 0 : nsCString key;
350 0 : GetDNSRecordHashKey(aDnsRequest->mHost, aDnsRequest->mOriginAttributes, originalFlags,
351 0 : aDnsRequest->mNetworkInterface, originalListener, key);
352 :
353 : nsTArray<RefPtr<DNSRequestChild>> *hashEntry;
354 :
355 0 : if (mPendingRequests.Get(key, &hashEntry)) {
356 : int idx;
357 0 : if ((idx = hashEntry->IndexOf(aDnsRequest))) {
358 0 : hashEntry->RemoveElementAt(idx);
359 0 : if (hashEntry->IsEmpty()) {
360 0 : mPendingRequests.Remove(key);
361 : }
362 : }
363 : }
364 : }
365 :
366 : //-----------------------------------------------------------------------------
367 : // ChildDNSService::nsPIDNSService
368 : //-----------------------------------------------------------------------------
369 :
370 : nsresult
371 6 : ChildDNSService::Init()
372 : {
373 : // Disable prefetching either by explicit preference or if a manual proxy
374 : // is configured
375 6 : bool disablePrefetch = false;
376 6 : int proxyType = nsIProtocolProxyService::PROXYCONFIG_DIRECT;
377 :
378 12 : nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
379 6 : if (prefs) {
380 6 : prefs->GetIntPref("network.proxy.type", &proxyType);
381 6 : prefs->GetBoolPref(kPrefNameDisablePrefetch, &disablePrefetch);
382 : }
383 :
384 6 : if (mFirstTime) {
385 2 : mFirstTime = false;
386 2 : if (prefs) {
387 2 : prefs->AddObserver(kPrefNameDisablePrefetch, this, false);
388 :
389 : // Monitor these to see if there is a change in proxy configuration
390 : // If a manual proxy is in use, disable prefetch implicitly
391 2 : prefs->AddObserver("network.proxy.type", this, false);
392 : }
393 : }
394 :
395 12 : mDisablePrefetch = disablePrefetch ||
396 6 : (proxyType == nsIProtocolProxyService::PROXYCONFIG_MANUAL);
397 :
398 12 : return NS_OK;
399 : }
400 :
401 : nsresult
402 0 : ChildDNSService::Shutdown()
403 : {
404 0 : return NS_OK;
405 : }
406 :
407 : NS_IMETHODIMP
408 0 : ChildDNSService::GetPrefetchEnabled(bool *outVal)
409 : {
410 0 : *outVal = !mDisablePrefetch;
411 0 : return NS_OK;
412 : }
413 :
414 : NS_IMETHODIMP
415 0 : ChildDNSService::SetPrefetchEnabled(bool inVal)
416 : {
417 0 : mDisablePrefetch = !inVal;
418 0 : return NS_OK;
419 : }
420 :
421 : bool
422 0 : ChildDNSService::GetOffline() const
423 : {
424 0 : bool offline = false;
425 0 : nsCOMPtr<nsIIOService> io = do_GetService(NS_IOSERVICE_CONTRACTID);
426 0 : if (io) {
427 0 : io->GetOffline(&offline);
428 : }
429 0 : return offline;
430 : }
431 :
432 : //-----------------------------------------------------------------------------
433 : // ChildDNSService::nsIObserver
434 : //-----------------------------------------------------------------------------
435 :
436 : NS_IMETHODIMP
437 4 : ChildDNSService::Observe(nsISupports *subject, const char *topic,
438 : const char16_t *data)
439 : {
440 : // we are only getting called if a preference has changed.
441 4 : NS_ASSERTION(strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0,
442 : "unexpected observe call");
443 :
444 : // Reread prefs
445 4 : Init();
446 4 : return NS_OK;
447 : }
448 :
449 : } // namespace net
450 : } // namespace mozilla
|