diff --git a/mozilla/browser/base/content/urlbarBindings.xml b/mozilla/browser/base/content/urlbarBindings.xml index ac0cad8db6c..3428d07dd64 100644 --- a/mozilla/browser/base/content/urlbarBindings.xml +++ b/mozilla/browser/base/content/urlbarBindings.xml @@ -231,24 +231,23 @@ subdomain = "["; port = "]" + port; } - else if (0 && !/^[.0-9]+$/.test(host)) { + else if (0) { //XXX subdomain detection disabled (bug 386727) //XXX subdomain detection disabled for IP addresses (bug 364129) - // getEffectiveTLDLength might convert our host and return a misleading length. - // To avoid this, pass the ASCII host, count the dots of its effective TLD - // and use that number to operate on our actual host. - - var asciiHost = this._uri.asciiHost; - var domainSegments = host.split("."); - var cSubdomain = domainSegments.length - - asciiHost.slice(asciiHost.length - - this.tldService.getEffectiveTLDLength(asciiHost)) - .split(".").length - 1; - if (cSubdomain > 0) { - host = domainSegments; - subdomain = host.splice(0, cSubdomain).join(".") + "."; - host = host.join("."); + try { + var domainSegments = host.split("."); + var cSubdomain = domainSegments.length - + this.tldService.getBaseDomain(this._uri) + .split(".").length; + if (cSubdomain > 0) { + host = domainSegments; + subdomain = host.splice(0, cSubdomain).join(".") + "."; + host = host.join("."); + } + } catch (e) { + // ip address, or other failure + subdomain = ""; } } else { subdomain = ""; diff --git a/mozilla/content/html/document/src/nsHTMLDocument.cpp b/mozilla/content/html/document/src/nsHTMLDocument.cpp index f08c0caaf14..5fe09f474f1 100644 --- a/mozilla/content/html/document/src/nsHTMLDocument.cpp +++ b/mozilla/content/html/document/src/nsHTMLDocument.cpp @@ -1703,30 +1703,21 @@ nsHTMLDocument::SetDomain(const nsAString& aDomain) nsAutoString current; if (NS_FAILED(GetDomain(current))) return NS_ERROR_FAILURE; - PRBool ok = PR_FALSE; - if (current.Equals(aDomain)) { - ok = PR_TRUE; - } else if (aDomain.Length() < current.Length()) { - nsAutoString suffix; - current.Right(suffix, aDomain.Length()); - PRUnichar c = current.CharAt(current.Length() - aDomain.Length() - 1); - if (suffix.Equals(aDomain, nsCaseInsensitiveStringComparator()) && - (c == '.')) { - // Using only a TLD is forbidden (bug 368700) - nsCOMPtr tldService = - do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID); - if (!tldService) - return NS_ERROR_NOT_AVAILABLE; + PRBool ok = current.Equals(aDomain); + if (current.Length() > aDomain.Length() && + StringEndsWith(current, aDomain, nsCaseInsensitiveStringComparator()) && + current.CharAt(current.Length() - aDomain.Length() - 1) == '.') { + // Using only a TLD is forbidden (bug 368700) + nsCOMPtr tldService = + do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID); + if (!tldService) + return NS_ERROR_NOT_AVAILABLE; - NS_ConvertUTF16toUTF8 str(aDomain); - PRUint32 tldLength; - nsresult rv = tldService->GetEffectiveTLDLength(str, &tldLength); - if (NS_FAILED(rv)) - return rv; - - if (tldLength < str.Length()) - ok = PR_TRUE; - } + // try to get the base domain; if this works, we're ok + NS_ConvertUTF16toUTF8 str(aDomain); + nsCAutoString etld; + nsresult rv = tldService->GetBaseDomainFromHost(str, 0, etld); + ok = NS_SUCCEEDED(rv); } if (!ok) { // Error: illegal domain diff --git a/mozilla/netwerk/dns/public/nsIEffectiveTLDService.idl b/mozilla/netwerk/dns/public/nsIEffectiveTLDService.idl index cd48f046092..cced0a3b0e8 100644 --- a/mozilla/netwerk/dns/public/nsIEffectiveTLDService.idl +++ b/mozilla/netwerk/dns/public/nsIEffectiveTLDService.idl @@ -21,6 +21,7 @@ * * Contributor(s): * Pamela Greene (original author) + * Daniel Witte * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -38,36 +39,84 @@ #include "nsISupports.idl" -[scriptable, uuid(4b8cdd85-e186-46e5-9ec0-9488ae7f0b92)] +interface nsIURI; + +[scriptable, uuid(b07cb0f0-3394-572e-6260-dbaed0a292ba)] interface nsIEffectiveTLDService : nsISupports { /** - * getEffectiveTLDLength + * Returns the public suffix of a URI. A public suffix is the highest-level domain + * under which individual domains may be registered; it may therefore contain one + * or more dots. For example, the public suffix for "www.bbc.co.uk" is "co.uk", + * because the .uk TLD does not allow the registration of domains at the + * second level ("bbc.uk" is forbidden). * - * Finds the length of the effective TLD of a hostname. An effective TLD - * is the highest-level domain under which individual domains may be - * registered; it may therefore contain one or more dots. For example, - * the effective TLD for "www.bbc.co.uk" is "co.uk", because the .uk TLD - * does not allow the registration of domains at the second level ("bbc.uk" - * is forbidden). Similarly, the effective TLD of "developer.mozilla.com" - * is "com". + * The public suffix will be returned encoded in UTF8 and will be normalized using + * nsIIDNService::normalize, which follows RFC 3454. * - * The hostname will be normalized using nsIIDNService::normalize, which - * follows RFC 3454. getEffectiveTLDLength() will fail, generating an - * error, if the hostname contains characters that are invalid in URIs. + * @param aURI The URI to be analyzed * - * @param aHostname The hostname to be analyzed, in UTF-8 - * - * @returns the number of bytes that the longest identified effective TLD - * (TLD or TLD-like higher-level subdomain) occupies, not including - * the leading dot: - * bugzilla.mozilla.org -> org -> 3 - * theregister.co.uk -> co.uk -> 5 - * mysite.us -> us -> 2 + * @returns the public suffix * * @throws NS_ERROR_UNEXPECTED * or other error returned by nsIIDNService::normalize when - * aHostname is not UTF-8 or contains characters disallowed in URIs + * the hostname contains characters disallowed in URIs + * @throws NS_ERROR_HOST_IS_IP_ADDRESS + * if the host is a numeric IPv4 or IPv6 address (as determined by + * the success of a call to PR_StringToNetAddr()). */ - PRUint32 getEffectiveTLDLength(in AUTF8String aHostname); + AUTF8String getPublicSuffix(in nsIURI aURI); + + /** + * Returns the base domain of a URI; that is, the public suffix with a given + * number of additional domain name parts. For example, the result of this method + * for "www.bbc.co.uk", depending on the value of aAdditionalParts parameter, will + * be: + * + * 0 (default) -> bbc.co.uk + * 1 -> news.bbc.co.uk + * + * Similarly, the public suffix for "developer.mozilla.org" is "org", and the base + * domain will be: + * + * 0 (default) -> mozilla.org + * 1 -> developer.mozilla.org + * + * The base domain will be returned encoded in UTF8 and will be normalized using + * nsIIDNService::normalize, which follows RFC 3454. + * + * @param aURI The URI to be analyzed + * @param aAdditionalParts Number of domain name parts to be + * returned in addition to the public suffix + * + * @returns the base domain (public suffix plus the requested number of additional parts) + * + * @throws NS_ERROR_UNEXPECTED + * or other error returned by nsIIDNService::normalize when + * the hostname contains characters disallowed in URIs + * @throws NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS + * when there are insufficient subdomain levels in the hostname to satisfy the + * requested aAdditionalParts value. + * @throws NS_ERROR_HOST_IS_IP_ADDRESS + * if aHost is a numeric IPv4 or IPv6 address (as determined by + * the success of a call to PR_StringToNetAddr()). + * + * @see getPublicSuffix() + */ + AUTF8String getBaseDomain(in nsIURI aURI, [optional] in PRUint32 aAdditionalParts); + + /** + * Returns the public suffix of a host string. Otherwise identical to getPublicSuffix(). + * + * @see getPublicSuffix() + */ + AUTF8String getPublicSuffixFromHost(in AUTF8String aHost); + + /** + * Returns the base domain of a host string. Otherwise identical to getBaseDomain(). + * + * @see getBaseDomain() + */ + AUTF8String getBaseDomainFromHost(in AUTF8String aHost, [optional] in PRUint32 aAdditionalParts); }; + diff --git a/mozilla/netwerk/dns/src/nsEffectiveTLDService.cpp b/mozilla/netwerk/dns/src/nsEffectiveTLDService.cpp index 140087aaa38..08e87d1a257 100644 --- a/mozilla/netwerk/dns/src/nsEffectiveTLDService.cpp +++ b/mozilla/netwerk/dns/src/nsEffectiveTLDService.cpp @@ -49,6 +49,7 @@ #include "nsIFile.h" #include "nsIIDNService.h" #include "nsNetUtil.h" +#include "prnetdb.h" // The file name of the list of TLD-like names. A file with this name in the // system "res" directory will always be used. In addition, if a file with @@ -96,6 +97,10 @@ nsEffectiveTLDService::Init() if (!mHash.Init()) return NS_ERROR_OUT_OF_MEMORY; + nsresult rv; + mIDNService = do_GetService(NS_IDNSERVICE_CONTRACTID, &rv); + if (NS_FAILED(rv)) return rv; + return LoadEffectiveTLDFiles(); } @@ -112,27 +117,94 @@ nsEffectiveTLDService::~nsEffectiveTLDService() gArena = nsnull; } -// nsEffectiveTLDService::getEffectiveTLDLength -// -// The main external function: finds the length in bytes of the effective TLD -// for the given hostname. This will fail, generating an error, if the -// hostname is not UTF-8 or includes characters that are not valid in a URL. +// External function for dealing with URI's correctly. +// Pulls out the host portion from an nsIURI, and calls through to +// GetPublicSuffixFromHost(). NS_IMETHODIMP -nsEffectiveTLDService::GetEffectiveTLDLength(const nsACString &aHostname, - PRUint32 *effTLDLength) +nsEffectiveTLDService::GetPublicSuffix(nsIURI *aURI, + nsACString &aPublicSuffix) { - // Create a mutable copy of the hostname and normalize it. This will fail - // if the hostname includes invalid characters. + NS_ENSURE_ARG_POINTER(aURI); + + nsCOMPtr innerURI = NS_GetInnermostURI(aURI); + NS_ENSURE_ARG_POINTER(innerURI); + + nsCAutoString host; + innerURI->GetHost(host); + + return GetBaseDomainInternal(host, 0, aPublicSuffix); +} + +// External function for dealing with URI's correctly. +// Pulls out the host portion from an nsIURI, and calls through to +// GetBaseDomainFromHost(). +NS_IMETHODIMP +nsEffectiveTLDService::GetBaseDomain(nsIURI *aURI, + PRUint32 aAdditionalParts, + nsACString &aBaseDomain) +{ + NS_ENSURE_ARG_POINTER(aURI); + + nsCOMPtr innerURI = NS_GetInnermostURI(aURI); + NS_ENSURE_ARG_POINTER(innerURI); + + nsCAutoString host; + innerURI->GetHost(host); + + return GetBaseDomainInternal(host, aAdditionalParts + 1, aBaseDomain); +} + +// External function for dealing with a host string directly: finds the public +// suffix (e.g. co.uk) for the given hostname. See GetBaseDomainInternal(). +NS_IMETHODIMP +nsEffectiveTLDService::GetPublicSuffixFromHost(const nsACString &aHostname, + nsACString &aPublicSuffix) +{ + return GetBaseDomainInternal(aHostname, 0, aPublicSuffix); +} + +// External function for dealing with a host string directly: finds the base +// domain (e.g. www.co.uk) for the given hostname and number of subdomain parts +// requested. See GetBaseDomainInternal(). +NS_IMETHODIMP +nsEffectiveTLDService::GetBaseDomainFromHost(const nsACString &aHostname, + PRUint32 aAdditionalParts, + nsACString &aBaseDomain) +{ + return GetBaseDomainInternal(aHostname, aAdditionalParts + 1, aBaseDomain); +} + +// Finds the base domain for a host, with requested number of additional parts. +// This will fail, generating an error, if the host is an IPv4/IPv6 address, +// if more subdomain parts are requested than are available, or if the hostname +// includes characters that are not valid in a URL. Normalization is performed +// on the host string and the result will be in UTF8. +nsresult +nsEffectiveTLDService::GetBaseDomainInternal(const nsACString &aHostname, + PRUint32 aAdditionalParts, + nsACString &aBaseDomain) +{ + if (aHostname.IsEmpty()) + return NS_ERROR_INVALID_ARG; + + // Create a mutable copy of the hostname and normalize it to UTF8. + // This will fail if the hostname includes invalid characters. nsCAutoString normHostname(aHostname); nsresult rv = NormalizeHostname(normHostname); if (NS_FAILED(rv)) return rv; - // chomp any trailing dot, and remember to add it back later - PRUint32 trailingDot = normHostname.Last() == '.'; + // chomp any trailing dot, and keep track of it for later + PRBool trailingDot = normHostname.Last() == '.'; if (trailingDot) normHostname.Truncate(normHostname.Length() - 1); + // Check if we're dealing with an IPv4/IPv6 hostname, and return + PRNetAddr addr; + PRStatus result = PR_StringToNetAddr(normHostname.get(), &addr); + if (result == PR_SUCCESS) + return NS_ERROR_HOST_IS_IP_ADDRESS; + // walk up the domain tree, most specific to least specific, // looking for matches at each level. note that a given level may // have multiple attributes (e.g. IsWild() and IsNormal()). @@ -140,29 +212,30 @@ nsEffectiveTLDService::GetEffectiveTLDLength(const nsACString &aHostname, const char *currDomain = normHostname.get(); const char *nextDot = strchr(currDomain, '.'); const char *end = currDomain + normHostname.Length(); + const char *eTLD = currDomain; while (1) { nsDomainEntry *entry = mHash.GetEntry(currDomain); if (entry) { if (entry->IsWild() && prevDomain) { // wildcard rules imply an eTLD one level inferior to the match. - *effTLDLength = end - prevDomain; + eTLD = prevDomain; break; } else if (entry->IsNormal() || !nextDot) { // specific match, or we've hit the top domain level - *effTLDLength = end - currDomain; + eTLD = currDomain; break; } else if (entry->IsException()) { // exception rules imply an eTLD one level superior to the match. - *effTLDLength = end - nextDot - 1; + eTLD = nextDot + 1; break; } } if (!nextDot) { - // we've hit the top domain level; return it by default. - *effTLDLength = end - currDomain; + // we've hit the top domain level; use it by default. + eTLD = currDomain; break; } @@ -171,38 +244,52 @@ nsEffectiveTLDService::GetEffectiveTLDLength(const nsACString &aHostname, nextDot = strchr(currDomain, '.'); } + // count off the number of requested domains. + const char *begin = normHostname.get(); + const char *iter = eTLD; + while (1) { + if (iter == begin) + break; + + if (*(--iter) == '.' && aAdditionalParts-- == 0) { + ++iter; + ++aAdditionalParts; + break; + } + } + + if (aAdditionalParts != 0) + return NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS; + + aBaseDomain = Substring(iter, end); // add on the trailing dot, if applicable - *effTLDLength += trailingDot; + if (trailingDot) + aBaseDomain.Append('.'); return NS_OK; } -// NormalizeHostname -// -// Normalizes characters of hostname. ASCII names are lower-cased, and names -// using other characters are normalized with nsIIDNService::Normalize, which -// follows RFC 3454. +// Normalizes characters of hostname. ASCII names are lower-cased, UTF8 names +// are normalized with nsIIDNService::Normalize which follows RFC 3454, and +// ACE names are converted to UTF8 and normalized as above. nsresult nsEffectiveTLDService::NormalizeHostname(nsCString &aHostname) { if (IsASCII(aHostname)) { - ToLowerCase(aHostname); - return NS_OK; - } + PRBool isACE; + if (NS_FAILED(mIDNService->IsACE(aHostname, &isACE)) || !isACE) { + ToLowerCase(aHostname); + return NS_OK; + } - if (!mIDNService) { - nsresult rv; - mIDNService = do_GetService(NS_IDNSERVICE_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); + nsresult rv = mIDNService->ConvertACEtoUTF8(aHostname, aHostname); + if (NS_FAILED(rv)) return rv; } return mIDNService->Normalize(aHostname, aHostname); } -// AddEffectiveTLDEntry -// // Adds the given domain name rule to the effective-TLD hash. -// // CAUTION: As a side effect, the domain name rule will be normalized. // see NormalizeHostname(). nsresult @@ -253,8 +340,6 @@ nsEffectiveTLDService::AddEffectiveTLDEntry(nsCString &aDomainName) return NS_OK; } -// LocateEffectiveTLDFile -// // Locates the effective-TLD file. If aUseProfile is true, uses the file from // the user's profile directory; otherwise uses the one from the system "res" // directory. Places nsnull in foundFile if the desired file was not found. @@ -314,8 +399,6 @@ TruncateAtWhitespace(nsCString &aString) } } -// LoadOneEffectiveTLDFile -// // Loads the contents of the given effective-TLD file, building the tree as it // goes. nsresult @@ -350,8 +433,6 @@ nsEffectiveTLDService::LoadOneEffectiveTLDFile(nsCOMPtr& effTLDFile) return NS_OK; } -// LoadEffectiveTLDFiles -// // Loads the contents of the system and user effective-TLD files. nsresult nsEffectiveTLDService::LoadEffectiveTLDFiles() diff --git a/mozilla/netwerk/dns/src/nsEffectiveTLDService.h b/mozilla/netwerk/dns/src/nsEffectiveTLDService.h index 07b60f78d2b..359099ccb0b 100644 --- a/mozilla/netwerk/dns/src/nsEffectiveTLDService.h +++ b/mozilla/netwerk/dns/src/nsEffectiveTLDService.h @@ -21,6 +21,7 @@ * * Contributor(s): * Pamela Greene (original author) + * Daniel Witte * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -111,6 +112,7 @@ public: nsresult Init(); private: + nsresult GetBaseDomainInternal(const nsACString &aHostname, PRUint32 aAdditionalParts, nsACString &aBaseDomain); nsresult NormalizeHostname(nsCString &aHostname); nsresult AddEffectiveTLDEntry(nsCString &aDomainName); nsresult LoadEffectiveTLDFiles(); diff --git a/mozilla/netwerk/test/unit/test_bug368702.js b/mozilla/netwerk/test/unit/test_bug368702.js index 34d732e1a61..695da97124b 100644 --- a/mozilla/netwerk/test/unit/test_bug368702.js +++ b/mozilla/netwerk/test/unit/test_bug368702.js @@ -1,15 +1,80 @@ const Cc = Components.classes; const Ci = Components.interfaces; +const NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS = 0x804b0050; +const NS_ERROR_HOST_IS_IP_ADDRESS = 0x804b0051; + function run_test() { var tld = Cc["@mozilla.org/network/effective-tld-service;1"]. getService(Ci.nsIEffectiveTLDService); - do_check_eq(tld.getEffectiveTLDLength("localhost"), 9); - do_check_eq(tld.getEffectiveTLDLength("localhost."), 10); - do_check_eq(tld.getEffectiveTLDLength("domain.com"), 3); - do_check_eq(tld.getEffectiveTLDLength("domain.com."), 4); - do_check_eq(tld.getEffectiveTLDLength("domain.co.uk"), 5); - do_check_eq(tld.getEffectiveTLDLength("domain.co.uk."), 6); + var etld; + + do_check_eq(tld.getPublicSuffixFromHost("localhost"), "localhost"); + do_check_eq(tld.getPublicSuffixFromHost("localhost."), "localhost."); + do_check_eq(tld.getPublicSuffixFromHost("domain.com"), "com"); + do_check_eq(tld.getPublicSuffixFromHost("domain.com."), "com."); + do_check_eq(tld.getPublicSuffixFromHost("domain.co.uk"), "co.uk"); + do_check_eq(tld.getPublicSuffixFromHost("domain.co.uk."), "co.uk."); + do_check_eq(tld.getBaseDomainFromHost("domain.co.uk"), "domain.co.uk"); + do_check_eq(tld.getBaseDomainFromHost("domain.co.uk."), "domain.co.uk."); + + try { + etld = tld.getBaseDomainFromHost("domain.co.uk", 1); + do_throw("this should fail"); + } catch(e) { + do_check_eq(e.result, NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS); + } + + try { + etld = tld.getPublicSuffixFromHost("1.2.3.4"); + do_throw("this should fail"); + } catch(e) { + do_check_eq(e.result, NS_ERROR_HOST_IS_IP_ADDRESS); + } + + try { + etld = tld.getPublicSuffixFromHost("2010:836B:4179::836B:4179"); + do_throw("this should fail"); + } catch(e) { + do_check_eq(e.result, NS_ERROR_HOST_IS_IP_ADDRESS); + } + + try { + etld = tld.getPublicSuffixFromHost("3232235878"); + do_throw("this should fail"); + } catch(e) { + do_check_eq(e.result, NS_ERROR_HOST_IS_IP_ADDRESS); + } + + try { + etld = tld.getPublicSuffixFromHost("::ffff:192.9.5.5"); + do_throw("this should fail"); + } catch(e) { + do_check_eq(e.result, NS_ERROR_HOST_IS_IP_ADDRESS); + } + + try { + etld = tld.getPublicSuffixFromHost("::1"); + do_throw("this should fail"); + } catch(e) { + do_check_eq(e.result, NS_ERROR_HOST_IS_IP_ADDRESS); + } + + // Check IP addresses with trailing dot as well, Necko sometimes accepts + // those (depending on operating system, see bug 380543) + try { + etld = tld.getPublicSuffixFromHost("127.0.0.1."); + do_throw("this should fail"); + } catch(e) { + do_check_eq(e.result, NS_ERROR_HOST_IS_IP_ADDRESS); + } + + try { + etld = tld.getPublicSuffixFromHost("::ffff:127.0.0.1."); + do_throw("this should fail"); + } catch(e) { + do_check_eq(e.result, NS_ERROR_HOST_IS_IP_ADDRESS); + } }