ChildDNSService.cpp 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  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. #include "mozilla/net/ChildDNSService.h"
  5. #include "nsIDNSListener.h"
  6. #include "nsIIOService.h"
  7. #include "nsIThread.h"
  8. #include "nsThreadUtils.h"
  9. #include "nsIXPConnect.h"
  10. #include "nsIPrefService.h"
  11. #include "nsIProtocolProxyService.h"
  12. #include "nsNetCID.h"
  13. #include "mozilla/net/NeckoChild.h"
  14. #include "mozilla/net/DNSListenerProxy.h"
  15. #include "nsServiceManagerUtils.h"
  16. namespace mozilla {
  17. namespace net {
  18. //-----------------------------------------------------------------------------
  19. // ChildDNSService
  20. //-----------------------------------------------------------------------------
  21. static ChildDNSService *gChildDNSService;
  22. static const char kPrefNameDisablePrefetch[] = "network.dns.disablePrefetch";
  23. ChildDNSService* ChildDNSService::GetSingleton()
  24. {
  25. MOZ_ASSERT(IsNeckoChild());
  26. if (!gChildDNSService) {
  27. gChildDNSService = new ChildDNSService();
  28. }
  29. NS_ADDREF(gChildDNSService);
  30. return gChildDNSService;
  31. }
  32. NS_IMPL_ISUPPORTS(ChildDNSService,
  33. nsIDNSService,
  34. nsPIDNSService,
  35. nsIObserver)
  36. ChildDNSService::ChildDNSService()
  37. : mFirstTime(true)
  38. , mDisablePrefetch(false)
  39. , mPendingRequestsLock("DNSPendingRequestsLock")
  40. {
  41. MOZ_ASSERT(IsNeckoChild());
  42. }
  43. ChildDNSService::~ChildDNSService()
  44. {
  45. }
  46. void
  47. ChildDNSService::GetDNSRecordHashKey(const nsACString &aHost,
  48. uint32_t aFlags,
  49. const nsACString &aNetworkInterface,
  50. nsIDNSListener* aListener,
  51. nsACString &aHashKey)
  52. {
  53. aHashKey.Assign(aHost);
  54. aHashKey.AppendInt(aFlags);
  55. if (!aNetworkInterface.IsEmpty()) {
  56. aHashKey.Append(aNetworkInterface);
  57. }
  58. aHashKey.AppendPrintf("%p", aListener);
  59. }
  60. //-----------------------------------------------------------------------------
  61. // ChildDNSService::nsIDNSService
  62. //-----------------------------------------------------------------------------
  63. NS_IMETHODIMP
  64. ChildDNSService::AsyncResolve(const nsACString &hostname,
  65. uint32_t flags,
  66. nsIDNSListener *listener,
  67. nsIEventTarget *target_,
  68. nsICancelable **result)
  69. {
  70. return AsyncResolveExtended(hostname, flags, EmptyCString(), listener,
  71. target_, result);
  72. }
  73. NS_IMETHODIMP
  74. ChildDNSService::AsyncResolveExtended(const nsACString &hostname,
  75. uint32_t flags,
  76. const nsACString &aNetworkInterface,
  77. nsIDNSListener *listener,
  78. nsIEventTarget *target_,
  79. nsICancelable **result)
  80. {
  81. NS_ENSURE_TRUE(gNeckoChild != nullptr, NS_ERROR_FAILURE);
  82. if (mDisablePrefetch && (flags & RESOLVE_SPECULATE)) {
  83. return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
  84. }
  85. // We need original flags for the pending requests hash.
  86. uint32_t originalFlags = flags;
  87. // Support apps being 'offline' even if parent is not: avoids DNS traffic by
  88. // apps that have been told they are offline.
  89. if (GetOffline()) {
  90. flags |= RESOLVE_OFFLINE;
  91. }
  92. // We need original listener for the pending requests hash.
  93. nsIDNSListener *originalListener = listener;
  94. // make sure JS callers get notification on the main thread
  95. nsCOMPtr<nsIEventTarget> target = target_;
  96. nsCOMPtr<nsIXPConnectWrappedJS> wrappedListener = do_QueryInterface(listener);
  97. if (wrappedListener && !target) {
  98. nsCOMPtr<nsIThread> mainThread;
  99. NS_GetMainThread(getter_AddRefs(mainThread));
  100. target = do_QueryInterface(mainThread);
  101. }
  102. if (target) {
  103. // Guarantee listener freed on main thread. Not sure we need this in child
  104. // (or in parent in nsDNSService.cpp) but doesn't hurt.
  105. listener = new DNSListenerProxy(listener, target);
  106. }
  107. RefPtr<DNSRequestChild> childReq =
  108. new DNSRequestChild(nsCString(hostname), flags,
  109. nsCString(aNetworkInterface),
  110. listener, target);
  111. {
  112. MutexAutoLock lock(mPendingRequestsLock);
  113. nsCString key;
  114. GetDNSRecordHashKey(hostname, originalFlags, aNetworkInterface,
  115. originalListener, key);
  116. nsTArray<RefPtr<DNSRequestChild>> *hashEntry;
  117. if (mPendingRequests.Get(key, &hashEntry)) {
  118. hashEntry->AppendElement(childReq);
  119. } else {
  120. hashEntry = new nsTArray<RefPtr<DNSRequestChild>>();
  121. hashEntry->AppendElement(childReq);
  122. mPendingRequests.Put(key, hashEntry);
  123. }
  124. }
  125. childReq->StartRequest();
  126. childReq.forget(result);
  127. return NS_OK;
  128. }
  129. NS_IMETHODIMP
  130. ChildDNSService::CancelAsyncResolve(const nsACString &aHostname,
  131. uint32_t aFlags,
  132. nsIDNSListener *aListener,
  133. nsresult aReason)
  134. {
  135. return CancelAsyncResolveExtended(aHostname, aFlags, EmptyCString(),
  136. aListener, aReason);
  137. }
  138. NS_IMETHODIMP
  139. ChildDNSService::CancelAsyncResolveExtended(const nsACString &aHostname,
  140. uint32_t aFlags,
  141. const nsACString &aNetworkInterface,
  142. nsIDNSListener *aListener,
  143. nsresult aReason)
  144. {
  145. if (mDisablePrefetch && (aFlags & RESOLVE_SPECULATE)) {
  146. return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
  147. }
  148. MutexAutoLock lock(mPendingRequestsLock);
  149. nsTArray<RefPtr<DNSRequestChild>> *hashEntry;
  150. nsCString key;
  151. GetDNSRecordHashKey(aHostname, aFlags, aNetworkInterface, aListener, key);
  152. if (mPendingRequests.Get(key, &hashEntry)) {
  153. // We cancel just one.
  154. hashEntry->ElementAt(0)->Cancel(aReason);
  155. }
  156. return NS_OK;
  157. }
  158. NS_IMETHODIMP
  159. ChildDNSService::Resolve(const nsACString &hostname,
  160. uint32_t flags,
  161. nsIDNSRecord **result)
  162. {
  163. // not planning to ever support this, since sync IPDL is evil.
  164. return NS_ERROR_NOT_AVAILABLE;
  165. }
  166. NS_IMETHODIMP
  167. ChildDNSService::GetDNSCacheEntries(nsTArray<mozilla::net::DNSCacheEntries> *args)
  168. {
  169. // Only used by networking dashboard, so may not ever need this in child.
  170. // (and would provide a way to spy on what hosts other apps are connecting to,
  171. // unless we start keeping per-app DNS caches).
  172. return NS_ERROR_NOT_AVAILABLE;
  173. }
  174. NS_IMETHODIMP
  175. ChildDNSService::GetMyHostName(nsACString &result)
  176. {
  177. // TODO: get value from parent during PNecko construction?
  178. return NS_ERROR_NOT_AVAILABLE;
  179. }
  180. void
  181. ChildDNSService::NotifyRequestDone(DNSRequestChild *aDnsRequest)
  182. {
  183. // We need the original flags and listener for the pending requests hash.
  184. uint32_t originalFlags = aDnsRequest->mFlags & ~RESOLVE_OFFLINE;
  185. nsCOMPtr<nsIDNSListener> originalListener = aDnsRequest->mListener;
  186. nsCOMPtr<nsIDNSListenerProxy> wrapper = do_QueryInterface(originalListener);
  187. if (wrapper) {
  188. wrapper->GetOriginalListener(getter_AddRefs(originalListener));
  189. if (NS_WARN_IF(!originalListener)) {
  190. MOZ_ASSERT(originalListener);
  191. return;
  192. }
  193. }
  194. MutexAutoLock lock(mPendingRequestsLock);
  195. nsCString key;
  196. GetDNSRecordHashKey(aDnsRequest->mHost, originalFlags,
  197. aDnsRequest->mNetworkInterface, originalListener, key);
  198. nsTArray<RefPtr<DNSRequestChild>> *hashEntry;
  199. if (mPendingRequests.Get(key, &hashEntry)) {
  200. int idx;
  201. if ((idx = hashEntry->IndexOf(aDnsRequest))) {
  202. hashEntry->RemoveElementAt(idx);
  203. if (hashEntry->IsEmpty()) {
  204. mPendingRequests.Remove(key);
  205. }
  206. }
  207. }
  208. }
  209. //-----------------------------------------------------------------------------
  210. // ChildDNSService::nsPIDNSService
  211. //-----------------------------------------------------------------------------
  212. nsresult
  213. ChildDNSService::Init()
  214. {
  215. // Disable prefetching either by explicit preference or if a manual proxy
  216. // is configured
  217. bool disablePrefetch = false;
  218. int proxyType = nsIProtocolProxyService::PROXYCONFIG_DIRECT;
  219. nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
  220. if (prefs) {
  221. prefs->GetIntPref("network.proxy.type", &proxyType);
  222. prefs->GetBoolPref(kPrefNameDisablePrefetch, &disablePrefetch);
  223. }
  224. if (mFirstTime) {
  225. mFirstTime = false;
  226. if (prefs) {
  227. prefs->AddObserver(kPrefNameDisablePrefetch, this, false);
  228. // Monitor these to see if there is a change in proxy configuration
  229. // If a manual proxy is in use, disable prefetch implicitly
  230. prefs->AddObserver("network.proxy.type", this, false);
  231. }
  232. }
  233. mDisablePrefetch = disablePrefetch ||
  234. (proxyType == nsIProtocolProxyService::PROXYCONFIG_MANUAL);
  235. return NS_OK;
  236. }
  237. nsresult
  238. ChildDNSService::Shutdown()
  239. {
  240. return NS_OK;
  241. }
  242. NS_IMETHODIMP
  243. ChildDNSService::GetPrefetchEnabled(bool *outVal)
  244. {
  245. *outVal = !mDisablePrefetch;
  246. return NS_OK;
  247. }
  248. NS_IMETHODIMP
  249. ChildDNSService::SetPrefetchEnabled(bool inVal)
  250. {
  251. mDisablePrefetch = !inVal;
  252. return NS_OK;
  253. }
  254. bool
  255. ChildDNSService::GetOffline() const
  256. {
  257. bool offline = false;
  258. nsCOMPtr<nsIIOService> io = do_GetService(NS_IOSERVICE_CONTRACTID);
  259. if (io) {
  260. io->GetOffline(&offline);
  261. }
  262. return offline;
  263. }
  264. //-----------------------------------------------------------------------------
  265. // ChildDNSService::nsIObserver
  266. //-----------------------------------------------------------------------------
  267. NS_IMETHODIMP
  268. ChildDNSService::Observe(nsISupports *subject, const char *topic,
  269. const char16_t *data)
  270. {
  271. // we are only getting called if a preference has changed.
  272. NS_ASSERTION(strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0,
  273. "unexpected observe call");
  274. // Reread prefs
  275. Init();
  276. return NS_OK;
  277. }
  278. } // namespace net
  279. } // namespace mozilla