Bug 628949 - Update visible region / glass regions after we paint. r=roc a=2.0.
[mozilla-central.git] / netwerk / dns / nsDNSService2.cpp
blob9fbb54f0ae36a0a178e1493ce178698d178f6f38
1 /* vim:set ts=4 sw=4 sts=4 et cin: */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is Mozilla.
17 * The Initial Developer of the Original Code is IBM Corporation.
18 * Portions created by IBM Corporation are Copyright (C) 2003
19 * IBM Corporation. All Rights Reserved.
21 * Contributor(s):
22 * IBM Corp.
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
38 #include "nsDNSService2.h"
39 #include "nsIDNSRecord.h"
40 #include "nsIDNSListener.h"
41 #include "nsICancelable.h"
42 #include "nsIProxyObjectManager.h"
43 #include "nsIPrefService.h"
44 #include "nsIPrefBranch.h"
45 #include "nsIPrefBranch2.h"
46 #include "nsIServiceManager.h"
47 #include "nsReadableUtils.h"
48 #include "nsString.h"
49 #include "nsAutoLock.h"
50 #include "nsAutoPtr.h"
51 #include "nsNetCID.h"
52 #include "nsNetError.h"
53 #include "nsDNSPrefetch.h"
54 #include "nsIProtocolProxyService.h"
55 #include "prsystem.h"
56 #include "prnetdb.h"
57 #include "prmon.h"
58 #include "prio.h"
59 #include "plstr.h"
60 #include "nsIOService.h"
62 #include "mozilla/FunctionTimer.h"
64 static const char kPrefDnsCacheEntries[] = "network.dnsCacheEntries";
65 static const char kPrefDnsCacheExpiration[] = "network.dnsCacheExpiration";
66 static const char kPrefEnableIDN[] = "network.enableIDN";
67 static const char kPrefIPv4OnlyDomains[] = "network.dns.ipv4OnlyDomains";
68 static const char kPrefDisableIPv6[] = "network.dns.disableIPv6";
69 static const char kPrefDisablePrefetch[] = "network.dns.disablePrefetch";
71 //-----------------------------------------------------------------------------
73 class nsDNSRecord : public nsIDNSRecord
75 public:
76 NS_DECL_ISUPPORTS
77 NS_DECL_NSIDNSRECORD
79 nsDNSRecord(nsHostRecord *hostRecord)
80 : mHostRecord(hostRecord)
81 , mIter(nsnull)
82 , mIterGenCnt(-1)
83 , mDone(PR_FALSE) {}
85 private:
86 virtual ~nsDNSRecord() {}
88 nsRefPtr<nsHostRecord> mHostRecord;
89 void *mIter;
90 int mIterGenCnt; // the generation count of
91 // mHostRecord->addr_info when we
92 // start iterating
93 PRBool mDone;
96 NS_IMPL_THREADSAFE_ISUPPORTS1(nsDNSRecord, nsIDNSRecord)
98 NS_IMETHODIMP
99 nsDNSRecord::GetCanonicalName(nsACString &result)
101 // this method should only be called if we have a CNAME
102 NS_ENSURE_TRUE(mHostRecord->flags & nsHostResolver::RES_CANON_NAME,
103 NS_ERROR_NOT_AVAILABLE);
105 // if the record is for an IP address literal, then the canonical
106 // host name is the IP address literal.
107 const char *cname;
108 PR_Lock(mHostRecord->addr_info_lock);
109 if (mHostRecord->addr_info)
110 cname = PR_GetCanonNameFromAddrInfo(mHostRecord->addr_info);
111 else
112 cname = mHostRecord->host;
113 result.Assign(cname);
114 PR_Unlock(mHostRecord->addr_info_lock);
115 return NS_OK;
118 NS_IMETHODIMP
119 nsDNSRecord::GetNextAddr(PRUint16 port, PRNetAddr *addr)
121 // not a programming error to poke the DNS record when it has no more
122 // entries. just fail without any debug warnings. this enables consumers
123 // to enumerate the DNS record without calling HasMore.
124 if (mDone)
125 return NS_ERROR_NOT_AVAILABLE;
127 PR_Lock(mHostRecord->addr_info_lock);
128 if (mHostRecord->addr_info) {
129 if (!mIter)
130 mIterGenCnt = mHostRecord->addr_info_gencnt;
131 else if (mIterGenCnt != mHostRecord->addr_info_gencnt) {
132 // mHostRecord->addr_info has changed, so mIter is invalid.
133 // Restart the iteration. Alternatively, we could just fail.
134 mIter = nsnull;
135 mIterGenCnt = mHostRecord->addr_info_gencnt;
137 mIter = PR_EnumerateAddrInfo(mIter, mHostRecord->addr_info, port, addr);
138 PR_Unlock(mHostRecord->addr_info_lock);
139 if (!mIter) {
140 mDone = PR_TRUE;
141 return NS_ERROR_NOT_AVAILABLE;
144 else {
145 PR_Unlock(mHostRecord->addr_info_lock);
146 if (!mHostRecord->addr) {
147 // Both mHostRecord->addr_info and mHostRecord->addr are null.
148 // This can happen if mHostRecord->addr_info expired and the
149 // attempt to reresolve it failed.
150 return NS_ERROR_NOT_AVAILABLE;
152 memcpy(addr, mHostRecord->addr, sizeof(PRNetAddr));
153 // set given port
154 port = PR_htons(port);
155 if (addr->raw.family == PR_AF_INET)
156 addr->inet.port = port;
157 else
158 addr->ipv6.port = port;
159 mDone = PR_TRUE; // no iterations
162 return NS_OK;
165 NS_IMETHODIMP
166 nsDNSRecord::GetNextAddrAsString(nsACString &result)
168 PRNetAddr addr;
169 nsresult rv = GetNextAddr(0, &addr);
170 if (NS_FAILED(rv)) return rv;
172 char buf[64];
173 if (PR_NetAddrToString(&addr, buf, sizeof(buf)) == PR_SUCCESS) {
174 result.Assign(buf);
175 return NS_OK;
177 NS_ERROR("PR_NetAddrToString failed unexpectedly");
178 return NS_ERROR_FAILURE; // conversion failed for some reason
181 NS_IMETHODIMP
182 nsDNSRecord::HasMore(PRBool *result)
184 if (mDone)
185 *result = PR_FALSE;
186 else {
187 // unfortunately, NSPR does not provide a way for us to determine if
188 // there is another address other than to simply get the next address.
189 void *iterCopy = mIter;
190 PRNetAddr addr;
191 *result = NS_SUCCEEDED(GetNextAddr(0, &addr));
192 mIter = iterCopy; // backup iterator
193 mDone = PR_FALSE;
195 return NS_OK;
198 NS_IMETHODIMP
199 nsDNSRecord::Rewind()
201 mIter = nsnull;
202 mIterGenCnt = -1;
203 mDone = PR_FALSE;
204 return NS_OK;
207 //-----------------------------------------------------------------------------
209 class nsDNSAsyncRequest : public nsResolveHostCallback
210 , public nsICancelable
212 public:
213 NS_DECL_ISUPPORTS
214 NS_DECL_NSICANCELABLE
216 nsDNSAsyncRequest(nsHostResolver *res,
217 const nsACString &host,
218 nsIDNSListener *listener,
219 PRUint16 flags,
220 PRUint16 af)
221 : mResolver(res)
222 , mHost(host)
223 , mListener(listener)
224 , mFlags(flags)
225 , mAF(af) {}
226 ~nsDNSAsyncRequest() {}
228 void OnLookupComplete(nsHostResolver *, nsHostRecord *, nsresult);
230 nsRefPtr<nsHostResolver> mResolver;
231 nsCString mHost; // hostname we're resolving
232 nsCOMPtr<nsIDNSListener> mListener;
233 PRUint16 mFlags;
234 PRUint16 mAF;
237 void
238 nsDNSAsyncRequest::OnLookupComplete(nsHostResolver *resolver,
239 nsHostRecord *hostRecord,
240 nsresult status)
242 // need to have an owning ref when we issue the callback to enable
243 // the caller to be able to addref/release multiple times without
244 // destroying the record prematurely.
245 nsCOMPtr<nsIDNSRecord> rec;
246 if (NS_SUCCEEDED(status)) {
247 NS_ASSERTION(hostRecord, "no host record");
248 rec = new nsDNSRecord(hostRecord);
249 if (!rec)
250 status = NS_ERROR_OUT_OF_MEMORY;
253 mListener->OnLookupComplete(this, rec, status);
254 mListener = nsnull;
256 // release the reference to ourselves that was added before we were
257 // handed off to the host resolver.
258 NS_RELEASE_THIS();
261 NS_IMPL_THREADSAFE_ISUPPORTS1(nsDNSAsyncRequest, nsICancelable)
263 NS_IMETHODIMP
264 nsDNSAsyncRequest::Cancel(nsresult reason)
266 NS_ENSURE_ARG(NS_FAILED(reason));
267 mResolver->DetachCallback(mHost.get(), mFlags, mAF, this, reason);
268 return NS_OK;
271 //-----------------------------------------------------------------------------
273 class nsDNSSyncRequest : public nsResolveHostCallback
275 public:
276 nsDNSSyncRequest(PRMonitor *mon)
277 : mDone(PR_FALSE)
278 , mStatus(NS_OK)
279 , mMonitor(mon) {}
280 virtual ~nsDNSSyncRequest() {}
282 void OnLookupComplete(nsHostResolver *, nsHostRecord *, nsresult);
284 PRBool mDone;
285 nsresult mStatus;
286 nsRefPtr<nsHostRecord> mHostRecord;
288 private:
289 PRMonitor *mMonitor;
292 void
293 nsDNSSyncRequest::OnLookupComplete(nsHostResolver *resolver,
294 nsHostRecord *hostRecord,
295 nsresult status)
297 // store results, and wake up nsDNSService::Resolve to process results.
298 PR_EnterMonitor(mMonitor);
299 mDone = PR_TRUE;
300 mStatus = status;
301 mHostRecord = hostRecord;
302 PR_Notify(mMonitor);
303 PR_ExitMonitor(mMonitor);
306 //-----------------------------------------------------------------------------
308 nsDNSService::nsDNSService()
309 : mLock(nsnull)
313 nsDNSService::~nsDNSService()
315 if (mLock)
316 PR_DestroyLock(mLock);
319 NS_IMPL_THREADSAFE_ISUPPORTS3(nsDNSService, nsIDNSService, nsPIDNSService,
320 nsIObserver)
322 NS_IMETHODIMP
323 nsDNSService::Init()
325 NS_TIME_FUNCTION;
327 NS_ENSURE_TRUE(!mResolver, NS_ERROR_ALREADY_INITIALIZED);
329 PRBool firstTime = (mLock == nsnull);
331 // prefs
332 PRUint32 maxCacheEntries = 400;
333 PRUint32 maxCacheLifetime = 3; // minutes
334 PRBool enableIDN = PR_TRUE;
335 PRBool disableIPv6 = PR_FALSE;
336 PRBool disablePrefetch = PR_FALSE;
337 int proxyType = nsIProtocolProxyService::PROXYCONFIG_DIRECT;
339 nsAdoptingCString ipv4OnlyDomains;
341 // read prefs
342 nsCOMPtr<nsIPrefBranch2> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
343 if (prefs) {
344 PRInt32 val;
345 if (NS_SUCCEEDED(prefs->GetIntPref(kPrefDnsCacheEntries, &val)))
346 maxCacheEntries = (PRUint32) val;
347 if (NS_SUCCEEDED(prefs->GetIntPref(kPrefDnsCacheExpiration, &val)))
348 maxCacheLifetime = val / 60; // convert from seconds to minutes
350 // ASSUMPTION: pref branch does not modify out params on failure
351 prefs->GetBoolPref(kPrefEnableIDN, &enableIDN);
352 prefs->GetBoolPref(kPrefDisableIPv6, &disableIPv6);
353 prefs->GetCharPref(kPrefIPv4OnlyDomains, getter_Copies(ipv4OnlyDomains));
354 prefs->GetBoolPref(kPrefDisablePrefetch, &disablePrefetch);
356 // If a manual proxy is in use, disable prefetch implicitly
357 prefs->GetIntPref("network.proxy.type", &proxyType);
360 if (firstTime) {
361 mLock = PR_NewLock();
362 if (!mLock)
363 return NS_ERROR_OUT_OF_MEMORY;
365 // register as prefs observer
366 if (prefs) {
367 prefs->AddObserver(kPrefDnsCacheEntries, this, PR_FALSE);
368 prefs->AddObserver(kPrefDnsCacheExpiration, this, PR_FALSE);
369 prefs->AddObserver(kPrefEnableIDN, this, PR_FALSE);
370 prefs->AddObserver(kPrefIPv4OnlyDomains, this, PR_FALSE);
371 prefs->AddObserver(kPrefDisableIPv6, this, PR_FALSE);
372 prefs->AddObserver(kPrefDisablePrefetch, this, PR_FALSE);
374 // Monitor these to see if there is a change in proxy configuration
375 // If a manual proxy is in use, disable prefetch implicitly
376 prefs->AddObserver("network.proxy.type", this, PR_FALSE);
380 // we have to null out mIDN since we might be getting re-initialized
381 // as a result of a pref change.
382 nsCOMPtr<nsIIDNService> idn;
383 if (enableIDN)
384 idn = do_GetService(NS_IDNSERVICE_CONTRACTID);
386 nsDNSPrefetch::Initialize(this);
388 // Don't initialize the resolver if we're in offline mode.
389 // Later on, the IO service will reinitialize us when going online.
390 if (gIOService->IsOffline() && !gIOService->IsComingOnline())
391 return NS_OK;
393 nsRefPtr<nsHostResolver> res;
394 nsresult rv = nsHostResolver::Create(maxCacheEntries,
395 maxCacheLifetime,
396 getter_AddRefs(res));
397 if (NS_SUCCEEDED(rv)) {
398 // now, set all of our member variables while holding the lock
399 nsAutoLock lock(mLock);
400 mResolver = res;
401 mIDN = idn;
402 mIPv4OnlyDomains = ipv4OnlyDomains; // exchanges buffer ownership
403 mDisableIPv6 = disableIPv6;
405 // Disable prefetching either by explicit preference or if a manual proxy is configured
406 mDisablePrefetch = disablePrefetch || (proxyType == nsIProtocolProxyService::PROXYCONFIG_MANUAL);
408 return rv;
411 NS_IMETHODIMP
412 nsDNSService::Shutdown()
414 nsRefPtr<nsHostResolver> res;
416 nsAutoLock lock(mLock);
417 res = mResolver;
418 mResolver = nsnull;
420 if (res)
421 res->Shutdown();
422 return NS_OK;
425 NS_IMETHODIMP
426 nsDNSService::AsyncResolve(const nsACString &hostname,
427 PRUint32 flags,
428 nsIDNSListener *listener,
429 nsIEventTarget *target,
430 nsICancelable **result)
432 // grab reference to global host resolver and IDN service. beware
433 // simultaneous shutdown!!
434 nsRefPtr<nsHostResolver> res;
435 nsCOMPtr<nsIIDNService> idn;
437 nsAutoLock lock(mLock);
439 if (mDisablePrefetch && (flags & RESOLVE_SPECULATE))
440 return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
442 res = mResolver;
443 idn = mIDN;
445 if (!res)
446 return NS_ERROR_OFFLINE;
448 const nsACString *hostPtr = &hostname;
450 nsresult rv;
451 nsCAutoString hostACE;
452 if (idn && !IsASCII(hostname)) {
453 if (NS_SUCCEEDED(idn->ConvertUTF8toACE(hostname, hostACE)))
454 hostPtr = &hostACE;
457 nsCOMPtr<nsIDNSListener> listenerProxy;
458 if (target) {
459 rv = NS_GetProxyForObject(target,
460 NS_GET_IID(nsIDNSListener),
461 listener,
462 NS_PROXY_ASYNC | NS_PROXY_ALWAYS,
463 getter_AddRefs(listenerProxy));
464 if (NS_FAILED(rv)) return rv;
465 listener = listenerProxy;
468 PRUint16 af = GetAFForLookup(*hostPtr);
470 nsDNSAsyncRequest *req =
471 new nsDNSAsyncRequest(res, *hostPtr, listener, flags, af);
472 if (!req)
473 return NS_ERROR_OUT_OF_MEMORY;
474 NS_ADDREF(*result = req);
476 // addref for resolver; will be released when OnLookupComplete is called.
477 NS_ADDREF(req);
478 rv = res->ResolveHost(req->mHost.get(), flags, af, req);
479 if (NS_FAILED(rv)) {
480 NS_RELEASE(req);
481 NS_RELEASE(*result);
483 return rv;
486 NS_IMETHODIMP
487 nsDNSService::Resolve(const nsACString &hostname,
488 PRUint32 flags,
489 nsIDNSRecord **result)
491 // grab reference to global host resolver and IDN service. beware
492 // simultaneous shutdown!!
493 nsRefPtr<nsHostResolver> res;
494 nsCOMPtr<nsIIDNService> idn;
496 nsAutoLock lock(mLock);
497 res = mResolver;
498 idn = mIDN;
500 NS_ENSURE_TRUE(res, NS_ERROR_OFFLINE);
502 const nsACString *hostPtr = &hostname;
504 nsresult rv;
505 nsCAutoString hostACE;
506 if (idn && !IsASCII(hostname)) {
507 if (NS_SUCCEEDED(idn->ConvertUTF8toACE(hostname, hostACE)))
508 hostPtr = &hostACE;
512 // sync resolve: since the host resolver only works asynchronously, we need
513 // to use a mutex and a condvar to wait for the result. however, since the
514 // result may be in the resolvers cache, we might get called back recursively
515 // on the same thread. so, our mutex needs to be re-entrant. in other words,
516 // we need to use a monitor! ;-)
519 PRMonitor *mon = PR_NewMonitor();
520 if (!mon)
521 return NS_ERROR_OUT_OF_MEMORY;
523 PR_EnterMonitor(mon);
524 nsDNSSyncRequest syncReq(mon);
526 PRUint16 af = GetAFForLookup(*hostPtr);
528 rv = res->ResolveHost(PromiseFlatCString(*hostPtr).get(), flags, af, &syncReq);
529 if (NS_SUCCEEDED(rv)) {
530 // wait for result
531 while (!syncReq.mDone)
532 PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
534 if (NS_FAILED(syncReq.mStatus))
535 rv = syncReq.mStatus;
536 else {
537 NS_ASSERTION(syncReq.mHostRecord, "no host record");
538 nsDNSRecord *rec = new nsDNSRecord(syncReq.mHostRecord);
539 if (!rec)
540 rv = NS_ERROR_OUT_OF_MEMORY;
541 else
542 NS_ADDREF(*result = rec);
546 PR_ExitMonitor(mon);
547 PR_DestroyMonitor(mon);
548 return rv;
551 NS_IMETHODIMP
552 nsDNSService::GetMyHostName(nsACString &result)
554 char name[100];
555 if (PR_GetSystemInfo(PR_SI_HOSTNAME, name, sizeof(name)) == PR_SUCCESS) {
556 result = name;
557 return NS_OK;
559 return NS_ERROR_FAILURE;
562 NS_IMETHODIMP
563 nsDNSService::Observe(nsISupports *subject, const char *topic, const PRUnichar *data)
565 // we are only getting called if a preference has changed.
566 NS_ASSERTION(strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0,
567 "unexpected observe call");
570 // Shutdown and this function are both only called on the UI thread, so we don't
571 // have to worry about mResolver being cleared out from under us.
573 // NOTE Shutting down and reinitializing the service like this is obviously
574 // suboptimal if Observe gets called several times in a row, but we don't
575 // expect that to be the case.
578 if (mResolver) {
579 Shutdown();
581 Init();
582 return NS_OK;
585 PRUint16
586 nsDNSService::GetAFForLookup(const nsACString &host)
588 if (mDisableIPv6)
589 return PR_AF_INET;
591 nsAutoLock lock(mLock);
593 PRUint16 af = PR_AF_UNSPEC;
595 if (!mIPv4OnlyDomains.IsEmpty()) {
596 const char *domain, *domainEnd, *end;
597 PRUint32 hostLen, domainLen;
599 // see if host is in one of the IPv4-only domains
600 domain = mIPv4OnlyDomains.BeginReading();
601 domainEnd = mIPv4OnlyDomains.EndReading();
603 nsACString::const_iterator hostStart;
604 host.BeginReading(hostStart);
605 hostLen = host.Length();
607 do {
608 // skip any whitespace
609 while (*domain == ' ' || *domain == '\t')
610 ++domain;
612 // find end of this domain in the string
613 end = strchr(domain, ',');
614 if (!end)
615 end = domainEnd;
617 // to see if the hostname is in the domain, check if the domain
618 // matches the end of the hostname.
619 domainLen = end - domain;
620 if (domainLen && hostLen >= domainLen) {
621 const char *hostTail = hostStart.get() + hostLen - domainLen;
622 if (PL_strncasecmp(domain, hostTail, domainLen) == 0) {
623 // now, make sure either that the hostname is a direct match or
624 // that the hostname begins with a dot.
625 if (hostLen == domainLen ||
626 *hostTail == '.' || *(hostTail - 1) == '.') {
627 af = PR_AF_INET;
628 break;
633 domain = end + 1;
634 } while (*end);
637 return af;