Coverage Report

Created: 2018-09-25 14:53

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