From 2eef95e2b91cfe0e5b9a5b0463b0335d9ae6a7ab Mon Sep 17 00:00:00 2001 From: "darin%meer.net" Date: Tue, 9 Mar 2004 20:31:54 +0000 Subject: [PATCH] patch for bug 68796 "IPv6 : Some IPv4 addresses won't resolve w/IPv6 OS" patch by lorenzo@colitti.com r+sr=darin --- modules/libpref/src/init/all.js | 8 ++ netwerk/dns/src/nsDNSService2.cpp | 117 ++++++++++++++++++++++++----- netwerk/dns/src/nsDNSService2.h | 11 ++- netwerk/dns/src/nsHostResolver.cpp | 8 +- netwerk/dns/src/nsHostResolver.h | 5 +- 5 files changed, 127 insertions(+), 22 deletions(-) diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index fe8569e94a8..01241c0870f 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -538,6 +538,14 @@ pref("network.http.proxy.ssl.connect",true); // are handled. IDN requires a nsIIDNService implementation. pref("network.enableIDN", true); +// This preference specifies a list of domains for which DNS lookups will be +// IPv4 only. Works around broken DNS servers which can't handle IPv6 lookups +// and/or allows the user to disable IPv6 on a per-domain basis. See bug 68796. +pref("network.dns.ipv4OnlyDomains", ".doubleclick.net"); + +// This preference can be used to turn off IPv6 name lookups. See bug 68796. +pref("network.dns.disableIPv6, false); + // This preference controls whether or not URLs with UTF-8 characters are // escaped. Set this preference to TRUE for strict RFC2396 conformance. pref("network.standard-url.escape-utf8", true); diff --git a/netwerk/dns/src/nsDNSService2.cpp b/netwerk/dns/src/nsDNSService2.cpp index 1f5d173629f..b44553139fc 100644 --- a/netwerk/dns/src/nsDNSService2.cpp +++ b/netwerk/dns/src/nsDNSService2.cpp @@ -50,10 +50,13 @@ #include "prnetdb.h" #include "prmon.h" #include "prio.h" +#include "plstr.h" static const char kPrefDnsCacheEntries[] = "network.dnsCacheEntries"; static const char kPrefDnsCacheExpiration[] = "network.dnsCacheExpiration"; static const char kPrefEnableIDN[] = "network.enableIDN"; +static const char kPrefIPv4OnlyDomains[] = "network.dns.ipv4OnlyDomains"; +static const char kPrefDisableIPv6[] = "network.dns.disableIPv6"; //----------------------------------------------------------------------------- @@ -281,40 +284,57 @@ nsDNSService::Init() PRUint32 maxCacheEntries = 20; PRUint32 maxCacheLifetime = 1; // minutes PRBool enableIDN = PR_TRUE; + PRBool disableIPv6 = PR_FALSE; + nsAdoptingCString ipv4OnlyDomains; // read prefs nsCOMPtr prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); if (prefs) { PRInt32 val; if (NS_SUCCEEDED(prefs->GetIntPref(kPrefDnsCacheEntries, &val))) - maxCacheEntries = val; + maxCacheEntries = (PRUint32) val; if (NS_SUCCEEDED(prefs->GetIntPref(kPrefDnsCacheExpiration, &val))) - maxCacheLifetime = (val / 60); // convert from seconds to minutes - if (NS_SUCCEEDED(prefs->GetBoolPref(kPrefEnableIDN, (PRBool*)&val))) - enableIDN = (PRBool) val; - } + maxCacheLifetime = val / 60; // convert from seconds to minutes - // we have to null out mIDN since we might be getting re-initialized - // as a result of a pref change. - if (enableIDN) - mIDN = do_GetService(NS_IDNSERVICE_CONTRACTID); - else - mIDN = nsnull; + // ASSUMPTION: pref branch does not modify out params on failure + prefs->GetBoolPref(kPrefEnableIDN, &enableIDN); + prefs->GetBoolPref(kPrefDisableIPv6, &disableIPv6); + prefs->GetCharPref(kPrefIPv4OnlyDomains, getter_Copies(ipv4OnlyDomains)); + } if (firstTime) { mLock = PR_NewLock(); if (!mLock) return NS_ERROR_OUT_OF_MEMORY; - + // register as prefs observer prefs->AddObserver(kPrefDnsCacheEntries, this, PR_FALSE); prefs->AddObserver(kPrefDnsCacheExpiration, this, PR_FALSE); prefs->AddObserver(kPrefEnableIDN, this, PR_FALSE); + prefs->AddObserver(kPrefIPv4OnlyDomains, this, PR_FALSE); + prefs->AddObserver(kPrefDisableIPv6, this, PR_FALSE); } - return nsHostResolver::Create(maxCacheEntries, - maxCacheLifetime, - getter_AddRefs(mResolver)); + // we have to null out mIDN since we might be getting re-initialized + // as a result of a pref change. + nsCOMPtr idn; + if (enableIDN) + idn = do_GetService(NS_IDNSERVICE_CONTRACTID); + + nsRefPtr res; + nsresult rv = nsHostResolver::Create(maxCacheEntries, + maxCacheLifetime, + getter_AddRefs(res)); + if (NS_SUCCEEDED(rv)) { + // now, set all of our member variables while holding the lock + nsAutoLock lock(mLock); + mResolver = res; + mIDN = idn; + mIPv4OnlyDomains = ipv4OnlyDomains; // exchanges buffer ownership + mDisableIPv6 = disableIPv6; + } + + return rv; } NS_IMETHODIMP @@ -374,9 +394,15 @@ nsDNSService::AsyncResolve(const nsACString &hostname, return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(*result = req); + PRUint16 af = GetAFForLookup(req->mHost); + +#ifdef DEBUG_darinf + printf(">>> using af=%hu for %s\n", af, req->mHost.get()); +#endif + // addref for resolver; will be released when OnLookupComplete is called. NS_ADDREF(req); - rv = res->ResolveHost(req->mHost.get(), bypassCache, req); + rv = res->ResolveHost(req->mHost.get(), bypassCache, req, af); if (NS_FAILED(rv)) { NS_RELEASE(req); NS_RELEASE(*result); @@ -424,7 +450,9 @@ nsDNSService::Resolve(const nsACString &hostname, PR_EnterMonitor(mon); nsDNSSyncRequest syncReq(mon); - rv = res->ResolveHost(PromiseFlatCString(*hostPtr).get(), bypassCache, &syncReq); + PRUint16 af = GetAFForLookup(*hostPtr); + + rv = res->ResolveHost(PromiseFlatCString(*hostPtr).get(), bypassCache, &syncReq, af); if (NS_SUCCEEDED(rv)) { // wait for result while (!syncReq.mDone) @@ -480,3 +508,58 @@ nsDNSService::Observe(nsISupports *subject, const char *topic, const PRUnichar * } return NS_OK; } + +PRUint16 +nsDNSService::GetAFForLookup(const nsACString &host) +{ + if (mDisableIPv6) + return PR_AF_INET; + + nsAutoLock lock(mLock); + + PRUint16 af = PR_AF_UNSPEC; + + if (!mIPv4OnlyDomains.IsEmpty()) { + const char *domain, *domainEnd, *end; + PRUint32 hostLen, domainLen; + + // see if host is in one of the IPv4-only domains + domain = mIPv4OnlyDomains.BeginReading(); + domainEnd = mIPv4OnlyDomains.EndReading(); + + nsACString::const_iterator hostStart; + host.BeginReading(hostStart); + hostLen = host.Length(); + + do { + // skip any whitespace + while (*domain == ' ' || *domain == '\t') + ++domain; + + // find end of this domain in the string + end = strchr(domain, ','); + if (!end) + end = domainEnd; + + // to see if the hostname is in the domain, check if the domain + // matches the end of the hostname. + domainLen = end - domain; + if (domainLen && hostLen >= domainLen) { + const char *hostTail = hostStart.get() + hostLen - domainLen; + if (PL_strncasecmp(domain, hostTail, domainLen) == 0) { + // now, make sure either that the hostname is a direct match or + // that the hostname begins with a dot. + if (hostLen == domainLen || + *hostTail == '.' || *(hostTail - 1) == '.') { + af = PR_AF_INET; + break; + } + } + } + + domain = end + 1; + } while (*end); + } + + return af; +} diff --git a/netwerk/dns/src/nsDNSService2.h b/netwerk/dns/src/nsDNSService2.h index b442a74b7e6..fa628876a79 100644 --- a/netwerk/dns/src/nsDNSService2.h +++ b/netwerk/dns/src/nsDNSService2.h @@ -39,6 +39,7 @@ #include "nsIObserver.h" #include "nsHostResolver.h" #include "nsAutoPtr.h" +#include "nsString.h" #include "prlock.h" class nsDNSService : public nsIDNSService @@ -53,9 +54,17 @@ public: virtual ~nsDNSService(); private: + PRUint16 GetAFForLookup(const nsACString &host); + nsRefPtr mResolver; nsCOMPtr mIDN; + + // mLock protects access to mResolver and mIPv4OnlyDomains PRLock *mLock; - // mLock protects access to mResolver + // mIPv4OnlyDomains is a comma-separated list of domains for which only + // IPv4 DNS lookups are performed. This allows the user to disable IPv6 on + // a per-domain basis and work around broken DNS servers. See bug 68796. + nsAdoptingCString mIPv4OnlyDomains; + PRBool mDisableIPv6; }; diff --git a/netwerk/dns/src/nsHostResolver.cpp b/netwerk/dns/src/nsHostResolver.cpp index 1445b53fdaf..d15b9ecdd46 100644 --- a/netwerk/dns/src/nsHostResolver.cpp +++ b/netwerk/dns/src/nsHostResolver.cpp @@ -351,7 +351,8 @@ nsHostResolver::Shutdown() nsresult nsHostResolver::ResolveHost(const char *host, PRBool bypassCache, - nsResolveHostCallback *callback) + nsResolveHostCallback *callback, + PRUint16 af = PR_AF_UNSPEC) { NS_ENSURE_TRUE(host && *host, NS_ERROR_UNEXPECTED); @@ -414,6 +415,7 @@ nsHostResolver::ResolveHost(const char *host, PR_APPEND_LINK(callback, &he->rec->callbacks); if (!he->rec->resolving) { + he->rec->af = af; rv = IssueLookup(he->rec); if (NS_FAILED(rv)) PR_REMOVE_AND_INIT_LINK(callback); @@ -603,10 +605,10 @@ nsHostResolver::ThreadFunc(void *arg) PRAddrInfo *ai; while (resolver->GetHostToLookup(&rec)) { LOG(("resolving %s ...\n", rec->host)); - ai = PR_GetAddrInfoByName(rec->host, PR_AF_UNSPEC, PR_AI_ADDRCONFIG); + ai = PR_GetAddrInfoByName(rec->host, rec->af, PR_AI_ADDRCONFIG); #if defined(RES_RETRY_ON_FAILURE) if (!ai && rs.Reset()) - ai = PR_GetAddrInfoByName(rec->host, PR_AF_UNSPEC, PR_AI_ADDRCONFIG); + ai = PR_GetAddrInfoByName(rec->host, rec->af, PR_AI_ADDRCONFIG); #endif // convert error code to nsresult. nsresult status = ai ? NS_OK : NS_ERROR_UNKNOWN_HOST; diff --git a/netwerk/dns/src/nsHostResolver.h b/netwerk/dns/src/nsHostResolver.h index 93d9ae356fe..45b83c6425b 100644 --- a/netwerk/dns/src/nsHostResolver.h +++ b/netwerk/dns/src/nsHostResolver.h @@ -81,10 +81,12 @@ public: * otherwise, if |addrinfo| is non-null, then it contains one or many * IP addresses corresponding to the given host name. if both |addrinfo| * and |addr| are null, then the given host has not yet been fully resolved. + * |af| is the address family of the record we are querying for. */ char *host; PRAddrInfo *addrinfo; PRNetAddr *addr; + PRUint16 af; PRUint32 expiration; /* measured in minutes since epoch */ PRBool HasResult() const { return (addrinfo || addr) != nsnull; } @@ -163,7 +165,8 @@ public: */ nsresult ResolveHost(const char *hostname, PRBool bypassCache, - nsResolveHostCallback *callback); + nsResolveHostCallback *callback, + PRUint16 af); /** * removes the specified callback from the nsHostRecord for the given