diff --git a/build/autoconf/icu.m4 b/build/autoconf/icu.m4 index e0eb6234697..a03132fd93b 100644 --- a/build/autoconf/icu.m4 +++ b/build/autoconf/icu.m4 @@ -142,7 +142,6 @@ if test -z "$BUILDING_JS" -o -n "$JS_STANDALONE"; then ICU_CPPFLAGS="$ICU_CPPFLAGS -DUCONFIG_NO_TRANSLITERATION" ICU_CPPFLAGS="$ICU_CPPFLAGS -DUCONFIG_NO_REGULAR_EXPRESSIONS" ICU_CPPFLAGS="$ICU_CPPFLAGS -DUCONFIG_NO_BREAK_ITERATION" - ICU_CPPFLAGS="$ICU_CPPFLAGS -DUCONFIG_NO_IDNA" # we don't need to pass data to and from legacy char* APIs ICU_CPPFLAGS="$ICU_CPPFLAGS -DU_CHARSET_IS_UTF8" # make sure to not accidentally pick up system-icu headers diff --git a/netwerk/dns/moz.build b/netwerk/dns/moz.build index 7b49d6caa1a..d1d259e1218 100644 --- a/netwerk/dns/moz.build +++ b/netwerk/dns/moz.build @@ -40,7 +40,6 @@ UNIFIED_SOURCES += [ 'DNSRequestChild.cpp', 'DNSRequestParent.cpp', 'GetAddrInfo.cpp', - 'nameprep.c', 'nsDNSService2.cpp', 'nsIDNService.cpp', 'punycode.c', @@ -69,3 +68,13 @@ LOCAL_INCLUDES += [ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk' and CONFIG['ANDROID_VERSION'] > '19': CXXFLAGS += ['-I%s/bionic/libc/dns/include' % CONFIG['ANDROID_SOURCE']] + +if CONFIG['ENABLE_INTL_API']: + DEFINES['IDNA2008'] = True + CXXFLAGS += CONFIG['MOZ_ICU_CFLAGS'] + CFLAGS += CONFIG['MOZ_ICU_CFLAGS'] + USE_LIBS += ['icu'] +else: + UNIFIED_SOURCES += [ + 'nameprep.c', + ] diff --git a/netwerk/dns/nsIDNService.cpp b/netwerk/dns/nsIDNService.cpp index 28a6db24867..e2c1b8a2ca7 100644 --- a/netwerk/dns/nsIDNService.cpp +++ b/netwerk/dns/nsIDNService.cpp @@ -17,6 +17,16 @@ #include "nsISupportsPrimitives.h" #include "punycode.h" +#ifdef IDNA2008 +// Currently we use the transitional processing option -- see +// http://unicode.org/reports/tr46/ +// To switch to non-transitional processing, change the value of this flag +// and kTransitionalProcessing in netwerk/test/unit/test_idna2008.js to false +// (patch in bug 1218179). +const bool kIDNA2008_TransitionalProcessing = true; + +#include "ICUUtils.h" +#endif using namespace mozilla::unicode; @@ -123,18 +133,91 @@ void nsIDNService::prefsChanged(nsIPrefBranch *prefBranch, const char16_t *pref) nsIDNService::nsIDNService() { +#ifdef IDNA2008 + uint32_t IDNAOptions = UIDNA_CHECK_BIDI | UIDNA_CHECK_CONTEXTJ; + if (!kIDNA2008_TransitionalProcessing) { + IDNAOptions |= UIDNA_NONTRANSITIONAL_TO_UNICODE; + } + UErrorCode errorCode = U_ZERO_ERROR; + mIDNA = uidna_openUTS46(IDNAOptions, &errorCode); +#else if (idn_success != idn_nameprep_create(nullptr, &mNamePrepHandle)) mNamePrepHandle = nullptr; mNormalizer = do_GetService(NS_UNICODE_NORMALIZER_CONTRACTID); /* member initializers and constructor code */ +#endif } nsIDNService::~nsIDNService() { +#ifdef IDNA2008 + uidna_close(mIDNA); +#else idn_nameprep_destroy(mNamePrepHandle); +#endif } +#ifdef IDNA2008 +nsresult +nsIDNService::IDNA2008ToUnicode(const nsACString& input, nsAString& output) +{ + NS_ConvertUTF8toUTF16 inputStr(input); + UIDNAInfo info = UIDNA_INFO_INITIALIZER; + UErrorCode errorCode = U_ZERO_ERROR; + int32_t inLen = inputStr.Length(); + int32_t outMaxLen = inLen - kACEPrefixLen + 1; + UChar outputBuffer[kMaxDNSNodeLen + 1]; + + int32_t outLen = uidna_labelToUnicode(mIDNA, (const UChar*)inputStr.get(), + inLen, outputBuffer, outMaxLen, + &info, &errorCode); + if (info.errors != 0) { + return NS_ERROR_FAILURE; + } + + if (U_SUCCESS(errorCode)) { + ICUUtils::AssignUCharArrayToString(outputBuffer, outLen, output); + } + + return ICUUtils::UErrorToNsResult(errorCode); +} + +nsresult +nsIDNService::IDNA2008StringPrep(const nsAString& input, + nsAString& output, + stringPrepFlag flag) +{ + UIDNAInfo info = UIDNA_INFO_INITIALIZER; + UErrorCode errorCode = U_ZERO_ERROR; + int32_t inLen = input.Length(); + int32_t outMaxLen = kMaxDNSNodeLen + 1; + UChar outputBuffer[kMaxDNSNodeLen + 1]; + + int32_t outLen = + uidna_labelToUnicode(mIDNA, (const UChar*)PromiseFlatString(input).get(), + inLen, outputBuffer, outMaxLen, &info, &errorCode); + nsresult rv = ICUUtils::UErrorToNsResult(errorCode); + NS_ENSURE_SUCCESS(rv, rv); + + // Output the result of nameToUnicode even if there were errors + ICUUtils::AssignUCharArrayToString(outputBuffer, outLen, output); + + if (flag == eStringPrepIgnoreErrors) { + return NS_OK; + } + + if (info.errors != 0) { + if (flag == eStringPrepForDNS) { + output.Truncate(); + } + rv = NS_ERROR_FAILURE; + } + + return rv; +} +#endif + NS_IMETHODIMP nsIDNService::ConvertUTF8toACE(const nsACString & input, nsACString & ace) { return UTF8toACE(input, ace, eStringPrepForDNS); @@ -396,6 +479,7 @@ static nsresult utf16ToUcs4(const nsAString& in, return NS_OK; } +#ifndef IDNA2008 static void ucs4toUtf16(const uint32_t *in, nsAString& out) { while (*in) { @@ -408,6 +492,7 @@ static void ucs4toUtf16(const uint32_t *in, nsAString& out) in++; } } +#endif static nsresult punycode(const nsAString& in, nsACString& out) { @@ -462,6 +547,9 @@ static nsresult punycode(const nsAString& in, nsACString& out) nsresult nsIDNService::stringPrep(const nsAString& in, nsAString& out, stringPrepFlag flag) { +#ifdef IDNA2008 + return IDNA2008StringPrep(in, out, flag); +#else if (!mNamePrepHandle || !mNormalizer) return NS_ERROR_FAILURE; @@ -523,6 +611,7 @@ nsresult nsIDNService::stringPrep(const nsAString& in, nsAString& out, } return rv; +#endif } nsresult nsIDNService::stringPrepAndACE(const nsAString& in, nsACString& out, @@ -612,6 +701,11 @@ nsresult nsIDNService::decodeACE(const nsACString& in, nsACString& out, return NS_OK; } + nsAutoString utf16; +#ifdef IDNA2008 + nsresult result = IDNA2008ToUnicode(in, utf16); + NS_ENSURE_SUCCESS(result, result); +#else // RFC 3490 - 4.2 ToUnicode // The ToUnicode output never contains more code points than its input. punycode_uint output_length = in.Length() - kACEPrefixLen + 1; @@ -630,9 +724,9 @@ nsresult nsIDNService::decodeACE(const nsACString& in, nsACString& out, // UCS4 -> UTF8 output[output_length] = 0; - nsAutoString utf16; ucs4toUtf16(output, utf16); delete [] output; +#endif if (flag != eStringPrepForUI || isLabelSafe(utf16)) { CopyUTF16toUTF8(utf16, out); } else { diff --git a/netwerk/dns/nsIDNService.h b/netwerk/dns/nsIDNService.h index c97b9d67b61..b0fa5101e89 100644 --- a/netwerk/dns/nsIDNService.h +++ b/netwerk/dns/nsIDNService.h @@ -10,8 +10,14 @@ #include "nsCOMPtr.h" #include "nsIObserver.h" #include "nsWeakReference.h" + +#ifdef IDNA2008 +#include "unicode/uidna.h" +#else #include "nsIUnicodeNormalizer.h" #include "nsIDNKitInterface.h" +#endif + #include "nsString.h" class nsIPrefBranch; @@ -143,8 +149,23 @@ private: */ bool illegalScriptCombo(int32_t script, int32_t& savedScript); +#ifdef IDNA2008 + /** + * Convert a DNS label from ASCII to Unicode using IDNA2008 + */ + nsresult IDNA2008ToUnicode(const nsACString& input, nsAString& output); + + /** + * Convert a DNS label to a normalized form conforming to IDNA2008 + */ + nsresult IDNA2008StringPrep(const nsAString& input, nsAString& output, + stringPrepFlag flag); + + UIDNA* mIDNA; +#else idn_nameprep_t mNamePrepHandle; nsCOMPtr mNormalizer; +#endif nsXPIDLString mIDNBlacklist; /** diff --git a/netwerk/standalone/moz.build b/netwerk/standalone/moz.build index dcc13e07c30..60a16aa2678 100644 --- a/netwerk/standalone/moz.build +++ b/netwerk/standalone/moz.build @@ -43,6 +43,7 @@ LOCAL_INCLUDES = [ '../dns', ] +DEFINES['IDNA2008'] = False DEFINES['MOZILLA_INTERNAL_API'] = True DEFINES['MOZILLA_XPCOMRT_API'] = True DEFINES['MOZILLA_EXTERNAL_LINKAGE'] = True