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
4 : *
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/
9 : *
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.
14 : *
15 : * The Original Code is Mozilla.
16 : *
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.
20 : *
21 : * Contributor(s):
22 : * IBM Corp.
23 : *
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.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include "nsDNSService2.h"
39 : #include "nsIDNSRecord.h"
40 : #include "nsIDNSListener.h"
41 : #include "nsICancelable.h"
42 : #include "nsIPrefService.h"
43 : #include "nsIPrefBranch.h"
44 : #include "nsIServiceManager.h"
45 : #include "nsProxyRelease.h"
46 : #include "nsReadableUtils.h"
47 : #include "nsString.h"
48 : #include "nsAutoPtr.h"
49 : #include "nsNetCID.h"
50 : #include "nsNetError.h"
51 : #include "nsDNSPrefetch.h"
52 : #include "nsThreadUtils.h"
53 : #include "nsIProtocolProxyService.h"
54 : #include "prsystem.h"
55 : #include "prnetdb.h"
56 : #include "prmon.h"
57 : #include "prio.h"
58 : #include "plstr.h"
59 : #include "nsIOService.h"
60 :
61 : #include "mozilla/FunctionTimer.h"
62 :
63 : using namespace mozilla;
64 :
65 : static const char kPrefDnsCacheEntries[] = "network.dnsCacheEntries";
66 : static const char kPrefDnsCacheExpiration[] = "network.dnsCacheExpiration";
67 : static const char kPrefDnsCacheGrace[] = "network.dnsCacheExpirationGracePeriod";
68 : static const char kPrefEnableIDN[] = "network.enableIDN";
69 : static const char kPrefIPv4OnlyDomains[] = "network.dns.ipv4OnlyDomains";
70 : static const char kPrefDisableIPv6[] = "network.dns.disableIPv6";
71 : static const char kPrefDisablePrefetch[] = "network.dns.disablePrefetch";
72 :
73 : //-----------------------------------------------------------------------------
74 :
75 : class nsDNSRecord : public nsIDNSRecord
76 : {
77 : public:
78 : NS_DECL_ISUPPORTS
79 : NS_DECL_NSIDNSRECORD
80 :
81 6205 : nsDNSRecord(nsHostRecord *hostRecord)
82 : : mHostRecord(hostRecord)
83 : , mIter(nsnull)
84 : , mLastIter(nsnull)
85 : , mIterGenCnt(-1)
86 6205 : , mDone(false) {}
87 :
88 : private:
89 24820 : virtual ~nsDNSRecord() {}
90 :
91 : nsRefPtr<nsHostRecord> mHostRecord;
92 : void *mIter; // enum ptr for PR_EnumerateAddrInfo
93 : void *mLastIter; // previous enum ptr, for use in
94 : // getting addrinfo in ReportUnusable
95 : int mIterGenCnt; // the generation count of
96 : // mHostRecord->addr_info when we
97 : // start iterating
98 : bool mDone;
99 : };
100 :
101 52126 : NS_IMPL_THREADSAFE_ISUPPORTS1(nsDNSRecord, nsIDNSRecord)
102 :
103 : NS_IMETHODIMP
104 0 : nsDNSRecord::GetCanonicalName(nsACString &result)
105 : {
106 : // this method should only be called if we have a CNAME
107 0 : NS_ENSURE_TRUE(mHostRecord->flags & nsHostResolver::RES_CANON_NAME,
108 : NS_ERROR_NOT_AVAILABLE);
109 :
110 : // if the record is for an IP address literal, then the canonical
111 : // host name is the IP address literal.
112 : const char *cname;
113 : {
114 0 : MutexAutoLock lock(mHostRecord->addr_info_lock);
115 0 : if (mHostRecord->addr_info)
116 0 : cname = PR_GetCanonNameFromAddrInfo(mHostRecord->addr_info);
117 : else
118 0 : cname = mHostRecord->host;
119 0 : result.Assign(cname);
120 : }
121 0 : return NS_OK;
122 : }
123 :
124 : NS_IMETHODIMP
125 3143 : nsDNSRecord::GetNextAddr(PRUint16 port, PRNetAddr *addr)
126 : {
127 : // not a programming error to poke the DNS record when it has no more
128 : // entries. just fail without any debug warnings. this enables consumers
129 : // to enumerate the DNS record without calling HasMore.
130 3143 : if (mDone)
131 68 : return NS_ERROR_NOT_AVAILABLE;
132 :
133 3075 : mHostRecord->addr_info_lock.Lock();
134 3075 : bool startedFresh = !mIter;
135 :
136 3075 : if (mHostRecord->addr_info) {
137 2987 : if (!mIter)
138 2919 : mIterGenCnt = mHostRecord->addr_info_gencnt;
139 68 : else if (mIterGenCnt != mHostRecord->addr_info_gencnt) {
140 : // mHostRecord->addr_info has changed, so mIter is invalid.
141 : // Restart the iteration. Alternatively, we could just fail.
142 0 : mIter = nsnull;
143 0 : mIterGenCnt = mHostRecord->addr_info_gencnt;
144 0 : startedFresh = true;
145 : }
146 :
147 5956 : do {
148 3037 : mLastIter = mIter;
149 3037 : mIter = PR_EnumerateAddrInfo(mIter, mHostRecord->addr_info,
150 6074 : port, addr);
151 : }
152 2919 : while (mIter && mHostRecord->Blacklisted(addr));
153 :
154 2987 : if (startedFresh && !mIter) {
155 : // if everything was blacklisted we want to reset the blacklist (and
156 : // likely relearn it) and return the first address. That is better
157 : // than nothing
158 50 : mHostRecord->ResetBlacklist();
159 50 : mLastIter = nsnull;
160 50 : mIter = PR_EnumerateAddrInfo(nsnull, mHostRecord->addr_info,
161 100 : port, addr);
162 : }
163 :
164 2987 : mHostRecord->addr_info_lock.Unlock();
165 2987 : if (!mIter) {
166 68 : mDone = true;
167 68 : return NS_ERROR_NOT_AVAILABLE;
168 : }
169 : }
170 : else {
171 88 : mHostRecord->addr_info_lock.Unlock();
172 88 : if (!mHostRecord->addr) {
173 : // Both mHostRecord->addr_info and mHostRecord->addr are null.
174 : // This can happen if mHostRecord->addr_info expired and the
175 : // attempt to reresolve it failed.
176 0 : return NS_ERROR_NOT_AVAILABLE;
177 : }
178 88 : memcpy(addr, mHostRecord->addr, sizeof(PRNetAddr));
179 : // set given port
180 88 : port = PR_htons(port);
181 88 : if (addr->raw.family == PR_AF_INET)
182 88 : addr->inet.port = port;
183 : else
184 0 : addr->ipv6.port = port;
185 88 : mDone = true; // no iterations
186 : }
187 :
188 3007 : return NS_OK;
189 : }
190 :
191 : NS_IMETHODIMP
192 1 : nsDNSRecord::GetNextAddrAsString(nsACString &result)
193 : {
194 : PRNetAddr addr;
195 1 : nsresult rv = GetNextAddr(0, &addr);
196 1 : if (NS_FAILED(rv)) return rv;
197 :
198 : char buf[64];
199 1 : if (PR_NetAddrToString(&addr, buf, sizeof(buf)) == PR_SUCCESS) {
200 1 : result.Assign(buf);
201 1 : return NS_OK;
202 : }
203 0 : NS_ERROR("PR_NetAddrToString failed unexpectedly");
204 0 : return NS_ERROR_FAILURE; // conversion failed for some reason
205 : }
206 :
207 : NS_IMETHODIMP
208 0 : nsDNSRecord::HasMore(bool *result)
209 : {
210 0 : if (mDone)
211 0 : *result = false;
212 : else {
213 : // unfortunately, NSPR does not provide a way for us to determine if
214 : // there is another address other than to simply get the next address.
215 0 : void *iterCopy = mIter;
216 0 : void *iterLastCopy = mLastIter;
217 : PRNetAddr addr;
218 0 : *result = NS_SUCCEEDED(GetNextAddr(0, &addr));
219 0 : mIter = iterCopy; // backup iterator
220 0 : mLastIter = iterLastCopy; // backup iterator
221 0 : mDone = false;
222 : }
223 0 : return NS_OK;
224 : }
225 :
226 : NS_IMETHODIMP
227 0 : nsDNSRecord::Rewind()
228 : {
229 0 : mIter = nsnull;
230 0 : mLastIter = nsnull;
231 0 : mIterGenCnt = -1;
232 0 : mDone = false;
233 0 : return NS_OK;
234 : }
235 :
236 : NS_IMETHODIMP
237 136 : nsDNSRecord::ReportUnusable(PRUint16 aPort)
238 : {
239 : // right now we don't use the port in the blacklist
240 :
241 136 : mHostRecord->addr_info_lock.Lock();
242 :
243 : // Check that we are using a real addr_info (as opposed to a single
244 : // constant address), and that the generation count is valid. Otherwise,
245 : // ignore the report.
246 :
247 204 : if (mHostRecord->addr_info &&
248 68 : mIterGenCnt == mHostRecord->addr_info_gencnt) {
249 : PRNetAddr addr;
250 68 : void *id = PR_EnumerateAddrInfo(mLastIter, mHostRecord->addr_info,
251 136 : aPort, &addr);
252 68 : if (id)
253 68 : mHostRecord->ReportUnusable(&addr);
254 : }
255 :
256 136 : mHostRecord->addr_info_lock.Unlock();
257 136 : return NS_OK;
258 : }
259 :
260 : //-----------------------------------------------------------------------------
261 :
262 : class nsDNSAsyncRequest : public nsResolveHostCallback
263 : , public nsICancelable
264 : {
265 : public:
266 : NS_DECL_ISUPPORTS
267 : NS_DECL_NSICANCELABLE
268 :
269 6492 : nsDNSAsyncRequest(nsHostResolver *res,
270 : const nsACString &host,
271 : nsIDNSListener *listener,
272 : PRUint16 flags,
273 : PRUint16 af)
274 : : mResolver(res)
275 : , mHost(host)
276 : , mListener(listener)
277 : , mFlags(flags)
278 6492 : , mAF(af) {}
279 6492 : ~nsDNSAsyncRequest() {}
280 :
281 : void OnLookupComplete(nsHostResolver *, nsHostRecord *, nsresult);
282 : // Returns TRUE if the DNS listener arg is the same as the member listener
283 : // Used in Cancellations to remove DNS requests associated with a
284 : // particular hostname and nsIDNSListener
285 : bool EqualsAsyncListener(nsIDNSListener *aListener);
286 :
287 : nsRefPtr<nsHostResolver> mResolver;
288 : nsCString mHost; // hostname we're resolving
289 : nsCOMPtr<nsIDNSListener> mListener;
290 : PRUint16 mFlags;
291 : PRUint16 mAF;
292 : };
293 :
294 : void
295 6492 : nsDNSAsyncRequest::OnLookupComplete(nsHostResolver *resolver,
296 : nsHostRecord *hostRecord,
297 : nsresult status)
298 : {
299 : // need to have an owning ref when we issue the callback to enable
300 : // the caller to be able to addref/release multiple times without
301 : // destroying the record prematurely.
302 12984 : nsCOMPtr<nsIDNSRecord> rec;
303 6492 : if (NS_SUCCEEDED(status)) {
304 6204 : NS_ASSERTION(hostRecord, "no host record");
305 6204 : rec = new nsDNSRecord(hostRecord);
306 6204 : if (!rec)
307 0 : status = NS_ERROR_OUT_OF_MEMORY;
308 : }
309 :
310 6492 : mListener->OnLookupComplete(this, rec, status);
311 6492 : mListener = nsnull;
312 :
313 : // release the reference to ourselves that was added before we were
314 : // handed off to the host resolver.
315 6492 : NS_RELEASE_THIS();
316 6492 : }
317 :
318 : bool
319 0 : nsDNSAsyncRequest::EqualsAsyncListener(nsIDNSListener *aListener)
320 : {
321 0 : return (aListener == mListener);
322 : }
323 :
324 51991 : NS_IMPL_THREADSAFE_ISUPPORTS1(nsDNSAsyncRequest, nsICancelable)
325 :
326 : NS_IMETHODIMP
327 0 : nsDNSAsyncRequest::Cancel(nsresult reason)
328 : {
329 0 : NS_ENSURE_ARG(NS_FAILED(reason));
330 0 : mResolver->DetachCallback(mHost.get(), mFlags, mAF, this, reason);
331 0 : return NS_OK;
332 : }
333 :
334 : //-----------------------------------------------------------------------------
335 :
336 : class nsDNSSyncRequest : public nsResolveHostCallback
337 : {
338 : public:
339 1 : nsDNSSyncRequest(PRMonitor *mon)
340 : : mDone(false)
341 : , mStatus(NS_OK)
342 1 : , mMonitor(mon) {}
343 2 : virtual ~nsDNSSyncRequest() {}
344 :
345 : void OnLookupComplete(nsHostResolver *, nsHostRecord *, nsresult);
346 : bool EqualsAsyncListener(nsIDNSListener *aListener);
347 :
348 : bool mDone;
349 : nsresult mStatus;
350 : nsRefPtr<nsHostRecord> mHostRecord;
351 :
352 : private:
353 : PRMonitor *mMonitor;
354 : };
355 :
356 : void
357 1 : nsDNSSyncRequest::OnLookupComplete(nsHostResolver *resolver,
358 : nsHostRecord *hostRecord,
359 : nsresult status)
360 : {
361 : // store results, and wake up nsDNSService::Resolve to process results.
362 1 : PR_EnterMonitor(mMonitor);
363 1 : mDone = true;
364 1 : mStatus = status;
365 1 : mHostRecord = hostRecord;
366 1 : PR_Notify(mMonitor);
367 1 : PR_ExitMonitor(mMonitor);
368 1 : }
369 :
370 : bool
371 0 : nsDNSSyncRequest::EqualsAsyncListener(nsIDNSListener *aListener)
372 : {
373 : // Sync request: no listener to compare
374 0 : return false;
375 : }
376 :
377 : //-----------------------------------------------------------------------------
378 :
379 1419 : nsDNSService::nsDNSService()
380 : : mLock("nsDNSServer.mLock")
381 1419 : , mFirstTime(true)
382 : {
383 1419 : }
384 :
385 1416 : nsDNSService::~nsDNSService()
386 : {
387 1416 : }
388 :
389 155846 : NS_IMPL_THREADSAFE_ISUPPORTS3(nsDNSService, nsIDNSService, nsPIDNSService,
390 : nsIObserver)
391 :
392 : NS_IMETHODIMP
393 2863 : nsDNSService::Init()
394 : {
395 : NS_TIME_FUNCTION;
396 :
397 2863 : NS_ENSURE_TRUE(!mResolver, NS_ERROR_ALREADY_INITIALIZED);
398 :
399 : // prefs
400 2863 : PRUint32 maxCacheEntries = 400;
401 2863 : PRUint32 maxCacheLifetime = 2; // minutes
402 2863 : PRUint32 lifetimeGracePeriod = 1;
403 2863 : bool enableIDN = true;
404 2863 : bool disableIPv6 = false;
405 2863 : bool disablePrefetch = false;
406 2863 : int proxyType = nsIProtocolProxyService::PROXYCONFIG_DIRECT;
407 :
408 5726 : nsAdoptingCString ipv4OnlyDomains;
409 :
410 : // read prefs
411 5726 : nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
412 2863 : if (prefs) {
413 : PRInt32 val;
414 2863 : if (NS_SUCCEEDED(prefs->GetIntPref(kPrefDnsCacheEntries, &val)))
415 0 : maxCacheEntries = (PRUint32) val;
416 2863 : if (NS_SUCCEEDED(prefs->GetIntPref(kPrefDnsCacheExpiration, &val)))
417 0 : maxCacheLifetime = val / 60; // convert from seconds to minutes
418 2863 : if (NS_SUCCEEDED(prefs->GetIntPref(kPrefDnsCacheGrace, &val)))
419 0 : lifetimeGracePeriod = val / 60; // convert from seconds to minutes
420 :
421 : // ASSUMPTION: pref branch does not modify out params on failure
422 2863 : prefs->GetBoolPref(kPrefEnableIDN, &enableIDN);
423 2863 : prefs->GetBoolPref(kPrefDisableIPv6, &disableIPv6);
424 2863 : prefs->GetCharPref(kPrefIPv4OnlyDomains, getter_Copies(ipv4OnlyDomains));
425 2863 : prefs->GetBoolPref(kPrefDisablePrefetch, &disablePrefetch);
426 :
427 : // If a manual proxy is in use, disable prefetch implicitly
428 2863 : prefs->GetIntPref("network.proxy.type", &proxyType);
429 : }
430 :
431 2863 : if (mFirstTime) {
432 1419 : mFirstTime = false;
433 :
434 : // register as prefs observer
435 1419 : if (prefs) {
436 1419 : prefs->AddObserver(kPrefDnsCacheEntries, this, false);
437 1419 : prefs->AddObserver(kPrefDnsCacheExpiration, this, false);
438 1419 : prefs->AddObserver(kPrefDnsCacheGrace, this, false);
439 1419 : prefs->AddObserver(kPrefEnableIDN, this, false);
440 1419 : prefs->AddObserver(kPrefIPv4OnlyDomains, this, false);
441 1419 : prefs->AddObserver(kPrefDisableIPv6, this, false);
442 1419 : prefs->AddObserver(kPrefDisablePrefetch, this, false);
443 :
444 : // Monitor these to see if there is a change in proxy configuration
445 : // If a manual proxy is in use, disable prefetch implicitly
446 1419 : prefs->AddObserver("network.proxy.type", this, false);
447 : }
448 : }
449 :
450 : // we have to null out mIDN since we might be getting re-initialized
451 : // as a result of a pref change.
452 5726 : nsCOMPtr<nsIIDNService> idn;
453 2863 : if (enableIDN)
454 2863 : idn = do_GetService(NS_IDNSERVICE_CONTRACTID);
455 :
456 2863 : nsDNSPrefetch::Initialize(this);
457 :
458 : // Don't initialize the resolver if we're in offline mode.
459 : // Later on, the IO service will reinitialize us when going online.
460 2863 : if (gIOService->IsOffline() && !gIOService->IsComingOnline())
461 1419 : return NS_OK;
462 :
463 2888 : nsRefPtr<nsHostResolver> res;
464 : nsresult rv = nsHostResolver::Create(maxCacheEntries,
465 : maxCacheLifetime,
466 : lifetimeGracePeriod,
467 1444 : getter_AddRefs(res));
468 1444 : if (NS_SUCCEEDED(rv)) {
469 : // now, set all of our member variables while holding the lock
470 2888 : MutexAutoLock lock(mLock);
471 1444 : mResolver = res;
472 1444 : mIDN = idn;
473 1444 : mIPv4OnlyDomains = ipv4OnlyDomains; // exchanges buffer ownership
474 1444 : mDisableIPv6 = disableIPv6;
475 :
476 : // Disable prefetching either by explicit preference or if a manual proxy is configured
477 1444 : mDisablePrefetch = disablePrefetch || (proxyType == nsIProtocolProxyService::PROXYCONFIG_MANUAL);
478 : }
479 1444 : return rv;
480 : }
481 :
482 : NS_IMETHODIMP
483 1444 : nsDNSService::Shutdown()
484 : {
485 2888 : nsRefPtr<nsHostResolver> res;
486 : {
487 2888 : MutexAutoLock lock(mLock);
488 1444 : res = mResolver;
489 1444 : mResolver = nsnull;
490 : }
491 1444 : if (res)
492 1444 : res->Shutdown();
493 1444 : return NS_OK;
494 : }
495 :
496 : namespace {
497 :
498 : class DNSListenerProxy : public nsIDNSListener
499 : {
500 : public:
501 11 : DNSListenerProxy(nsIDNSListener* aListener, nsIEventTarget* aTargetThread)
502 : : mListener(aListener)
503 11 : , mTargetThread(aTargetThread)
504 11 : { }
505 :
506 11 : ~DNSListenerProxy()
507 22 : {
508 22 : nsCOMPtr<nsIThread> mainThread(do_GetMainThread());
509 11 : NS_ProxyRelease(mainThread, mListener);
510 11 : }
511 :
512 : NS_DECL_ISUPPORTS
513 : NS_DECL_NSIDNSLISTENER
514 :
515 : class OnLookupCompleteRunnable : public nsRunnable
516 : {
517 : public:
518 11 : OnLookupCompleteRunnable(nsIDNSListener* aListener,
519 : nsICancelable* aRequest,
520 : nsIDNSRecord* aRecord,
521 : nsresult aStatus)
522 : : mListener(aListener)
523 : , mRequest(aRequest)
524 : , mRecord(aRecord)
525 11 : , mStatus(aStatus)
526 11 : { }
527 :
528 22 : ~OnLookupCompleteRunnable()
529 22 : {
530 22 : nsCOMPtr<nsIThread> mainThread(do_GetMainThread());
531 11 : NS_ProxyRelease(mainThread, mListener);
532 44 : }
533 :
534 : NS_DECL_NSIRUNNABLE
535 :
536 : private:
537 : nsCOMPtr<nsIDNSListener> mListener;
538 : nsCOMPtr<nsICancelable> mRequest;
539 : nsCOMPtr<nsIDNSRecord> mRecord;
540 : nsresult mStatus;
541 : };
542 :
543 : private:
544 : nsCOMPtr<nsIDNSListener> mListener;
545 : nsCOMPtr<nsIEventTarget> mTargetThread;
546 : };
547 :
548 66 : NS_IMPL_THREADSAFE_ISUPPORTS1(DNSListenerProxy, nsIDNSListener)
549 :
550 : NS_IMETHODIMP
551 11 : DNSListenerProxy::OnLookupComplete(nsICancelable* aRequest,
552 : nsIDNSRecord* aRecord,
553 : nsresult aStatus)
554 : {
555 : nsRefPtr<OnLookupCompleteRunnable> r =
556 33 : new OnLookupCompleteRunnable(mListener, aRequest, aRecord, aStatus);
557 11 : return mTargetThread->Dispatch(r, NS_DISPATCH_NORMAL);
558 : }
559 :
560 : NS_IMETHODIMP
561 11 : DNSListenerProxy::OnLookupCompleteRunnable::Run()
562 : {
563 11 : mListener->OnLookupComplete(mRequest, mRecord, mStatus);
564 11 : return NS_OK;
565 : }
566 :
567 : } // anonymous namespace
568 :
569 : NS_IMETHODIMP
570 6495 : nsDNSService::AsyncResolve(const nsACString &hostname,
571 : PRUint32 flags,
572 : nsIDNSListener *listener,
573 : nsIEventTarget *target,
574 : nsICancelable **result)
575 : {
576 : // grab reference to global host resolver and IDN service. beware
577 : // simultaneous shutdown!!
578 12990 : nsRefPtr<nsHostResolver> res;
579 12990 : nsCOMPtr<nsIIDNService> idn;
580 : {
581 12990 : MutexAutoLock lock(mLock);
582 :
583 6495 : if (mDisablePrefetch && (flags & RESOLVE_SPECULATE))
584 0 : return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
585 :
586 6495 : res = mResolver;
587 12990 : idn = mIDN;
588 : }
589 6495 : if (!res)
590 3 : return NS_ERROR_OFFLINE;
591 :
592 6492 : const nsACString *hostPtr = &hostname;
593 :
594 : nsresult rv;
595 12984 : nsCAutoString hostACE;
596 6492 : if (idn && !IsASCII(hostname)) {
597 0 : if (NS_SUCCEEDED(idn->ConvertUTF8toACE(hostname, hostACE)))
598 0 : hostPtr = &hostACE;
599 : }
600 :
601 6492 : if (target) {
602 11 : listener = new DNSListenerProxy(listener, target);
603 : }
604 :
605 6492 : PRUint16 af = GetAFForLookup(*hostPtr, flags);
606 :
607 : nsDNSAsyncRequest *req =
608 12984 : new nsDNSAsyncRequest(res, *hostPtr, listener, flags, af);
609 6492 : if (!req)
610 0 : return NS_ERROR_OUT_OF_MEMORY;
611 6492 : NS_ADDREF(*result = req);
612 :
613 : // addref for resolver; will be released when OnLookupComplete is called.
614 6492 : NS_ADDREF(req);
615 6492 : rv = res->ResolveHost(req->mHost.get(), flags, af, req);
616 6492 : if (NS_FAILED(rv)) {
617 0 : NS_RELEASE(req);
618 0 : NS_RELEASE(*result);
619 : }
620 6492 : return rv;
621 : }
622 :
623 : NS_IMETHODIMP
624 0 : nsDNSService::CancelAsyncResolve(const nsACString &aHostname,
625 : PRUint32 aFlags,
626 : nsIDNSListener *aListener,
627 : nsresult aReason)
628 : {
629 : // grab reference to global host resolver and IDN service. beware
630 : // simultaneous shutdown!!
631 0 : nsRefPtr<nsHostResolver> res;
632 0 : nsCOMPtr<nsIIDNService> idn;
633 : {
634 0 : MutexAutoLock lock(mLock);
635 :
636 0 : if (mDisablePrefetch && (aFlags & RESOLVE_SPECULATE))
637 0 : return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
638 :
639 0 : res = mResolver;
640 0 : idn = mIDN;
641 : }
642 0 : if (!res)
643 0 : return NS_ERROR_OFFLINE;
644 :
645 0 : nsCString hostname(aHostname);
646 :
647 0 : nsCAutoString hostACE;
648 0 : if (idn && !IsASCII(aHostname)) {
649 0 : if (NS_SUCCEEDED(idn->ConvertUTF8toACE(aHostname, hostACE)))
650 0 : hostname = hostACE;
651 : }
652 :
653 0 : PRUint16 af = GetAFForLookup(hostname, aFlags);
654 :
655 0 : res->CancelAsyncRequest(hostname.get(), aFlags, af, aListener, aReason);
656 0 : return NS_OK;
657 : }
658 :
659 : NS_IMETHODIMP
660 1 : nsDNSService::Resolve(const nsACString &hostname,
661 : PRUint32 flags,
662 : nsIDNSRecord **result)
663 : {
664 : // grab reference to global host resolver and IDN service. beware
665 : // simultaneous shutdown!!
666 2 : nsRefPtr<nsHostResolver> res;
667 2 : nsCOMPtr<nsIIDNService> idn;
668 : {
669 2 : MutexAutoLock lock(mLock);
670 1 : res = mResolver;
671 1 : idn = mIDN;
672 : }
673 1 : NS_ENSURE_TRUE(res, NS_ERROR_OFFLINE);
674 :
675 1 : const nsACString *hostPtr = &hostname;
676 :
677 : nsresult rv;
678 2 : nsCAutoString hostACE;
679 1 : if (idn && !IsASCII(hostname)) {
680 0 : if (NS_SUCCEEDED(idn->ConvertUTF8toACE(hostname, hostACE)))
681 0 : hostPtr = &hostACE;
682 : }
683 :
684 : //
685 : // sync resolve: since the host resolver only works asynchronously, we need
686 : // to use a mutex and a condvar to wait for the result. however, since the
687 : // result may be in the resolvers cache, we might get called back recursively
688 : // on the same thread. so, our mutex needs to be re-entrant. in other words,
689 : // we need to use a monitor! ;-)
690 : //
691 :
692 1 : PRMonitor *mon = PR_NewMonitor();
693 1 : if (!mon)
694 0 : return NS_ERROR_OUT_OF_MEMORY;
695 :
696 1 : PR_EnterMonitor(mon);
697 2 : nsDNSSyncRequest syncReq(mon);
698 :
699 1 : PRUint16 af = GetAFForLookup(*hostPtr, flags);
700 :
701 1 : rv = res->ResolveHost(PromiseFlatCString(*hostPtr).get(), flags, af, &syncReq);
702 1 : if (NS_SUCCEEDED(rv)) {
703 : // wait for result
704 3 : while (!syncReq.mDone)
705 1 : PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
706 :
707 1 : if (NS_FAILED(syncReq.mStatus))
708 0 : rv = syncReq.mStatus;
709 : else {
710 1 : NS_ASSERTION(syncReq.mHostRecord, "no host record");
711 2 : nsDNSRecord *rec = new nsDNSRecord(syncReq.mHostRecord);
712 1 : if (!rec)
713 0 : rv = NS_ERROR_OUT_OF_MEMORY;
714 : else
715 1 : NS_ADDREF(*result = rec);
716 : }
717 : }
718 :
719 1 : PR_ExitMonitor(mon);
720 1 : PR_DestroyMonitor(mon);
721 1 : return rv;
722 : }
723 :
724 : NS_IMETHODIMP
725 0 : nsDNSService::GetMyHostName(nsACString &result)
726 : {
727 : char name[100];
728 0 : if (PR_GetSystemInfo(PR_SI_HOSTNAME, name, sizeof(name)) == PR_SUCCESS) {
729 0 : result = name;
730 0 : return NS_OK;
731 : }
732 0 : return NS_ERROR_FAILURE;
733 : }
734 :
735 : NS_IMETHODIMP
736 18 : nsDNSService::Observe(nsISupports *subject, const char *topic, const PRUnichar *data)
737 : {
738 : // we are only getting called if a preference has changed.
739 18 : NS_ASSERTION(strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0,
740 : "unexpected observe call");
741 :
742 : //
743 : // Shutdown and this function are both only called on the UI thread, so we don't
744 : // have to worry about mResolver being cleared out from under us.
745 : //
746 : // NOTE Shutting down and reinitializing the service like this is obviously
747 : // suboptimal if Observe gets called several times in a row, but we don't
748 : // expect that to be the case.
749 : //
750 :
751 18 : if (mResolver) {
752 18 : Shutdown();
753 : }
754 18 : Init();
755 18 : return NS_OK;
756 : }
757 :
758 : PRUint16
759 6493 : nsDNSService::GetAFForLookup(const nsACString &host, PRUint32 flags)
760 : {
761 6493 : if (mDisableIPv6 || (flags & RESOLVE_DISABLE_IPV6))
762 0 : return PR_AF_INET;
763 :
764 12986 : MutexAutoLock lock(mLock);
765 :
766 6493 : PRUint16 af = PR_AF_UNSPEC;
767 :
768 6493 : if (!mIPv4OnlyDomains.IsEmpty()) {
769 : const char *domain, *domainEnd, *end;
770 : PRUint32 hostLen, domainLen;
771 :
772 : // see if host is in one of the IPv4-only domains
773 0 : domain = mIPv4OnlyDomains.BeginReading();
774 0 : domainEnd = mIPv4OnlyDomains.EndReading();
775 :
776 0 : nsACString::const_iterator hostStart;
777 0 : host.BeginReading(hostStart);
778 0 : hostLen = host.Length();
779 :
780 0 : do {
781 : // skip any whitespace
782 0 : while (*domain == ' ' || *domain == '\t')
783 0 : ++domain;
784 :
785 : // find end of this domain in the string
786 0 : end = strchr(domain, ',');
787 0 : if (!end)
788 0 : end = domainEnd;
789 :
790 : // to see if the hostname is in the domain, check if the domain
791 : // matches the end of the hostname.
792 0 : domainLen = end - domain;
793 0 : if (domainLen && hostLen >= domainLen) {
794 0 : const char *hostTail = hostStart.get() + hostLen - domainLen;
795 0 : if (PL_strncasecmp(domain, hostTail, domainLen) == 0) {
796 : // now, make sure either that the hostname is a direct match or
797 : // that the hostname begins with a dot.
798 0 : if (hostLen == domainLen ||
799 0 : *hostTail == '.' || *(hostTail - 1) == '.') {
800 0 : af = PR_AF_INET;
801 0 : break;
802 : }
803 : }
804 : }
805 :
806 0 : domain = end + 1;
807 : } while (*end);
808 : }
809 :
810 6493 : return af;
811 : }
|