LCOV - code coverage report
Current view: directory - netwerk/dns - nsIDNService.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 319 267 83.7 %
Date: 2012-06-02 Functions: 27 26 96.3 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       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.org code.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is
      18                 :  * Netscape Communications Corporation.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 2002
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *   Naoki Hotta <nhotta@netscape.com> (original author)
      24                 :  *
      25                 :  * Alternatively, the contents of this file may be used under the terms of
      26                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      27                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      28                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      29                 :  * of those above. If you wish to allow use of your version of this file only
      30                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      31                 :  * use your version of this file under the terms of the MPL, indicate your
      32                 :  * decision by deleting the provisions above and replace them with the notice
      33                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      34                 :  * the provisions above, a recipient may use your version of this file under
      35                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      36                 :  *
      37                 :  * ***** END LICENSE BLOCK ***** */
      38                 : 
      39                 : #include "nsIDNService.h"
      40                 : #include "nsReadableUtils.h"
      41                 : #include "nsCRT.h"
      42                 : #include "nsUnicharUtils.h"
      43                 : #include "nsIServiceManager.h"
      44                 : #include "nsIPrefService.h"
      45                 : #include "nsIPrefBranch.h"
      46                 : #include "nsIObserverService.h"
      47                 : #include "nsISupportsPrimitives.h"
      48                 : #include "punycode.h"
      49                 : 
      50                 : #include "mozilla/FunctionTimer.h"
      51                 : 
      52                 : //-----------------------------------------------------------------------------
      53                 : // RFC 1034 - 3.1. Name space specifications and terminology
      54                 : static const PRUint32 kMaxDNSNodeLen = 63;
      55                 : 
      56                 : //-----------------------------------------------------------------------------
      57                 : 
      58                 : #define NS_NET_PREF_IDNTESTBED      "network.IDN_testbed"
      59                 : #define NS_NET_PREF_IDNPREFIX       "network.IDN_prefix"
      60                 : #define NS_NET_PREF_IDNBLACKLIST    "network.IDN.blacklist_chars"
      61                 : #define NS_NET_PREF_SHOWPUNYCODE    "network.IDN_show_punycode"
      62                 : #define NS_NET_PREF_IDNWHITELIST    "network.IDN.whitelist."
      63                 : 
      64              17 : inline bool isOnlySafeChars(const nsAFlatString& in,
      65                 :                               const nsAFlatString& blacklist)
      66                 : {
      67              17 :   return (blacklist.IsEmpty() ||
      68              17 :           in.FindCharInSet(blacklist) == kNotFound);
      69                 : }
      70                 : 
      71                 : //-----------------------------------------------------------------------------
      72                 : // nsIDNService
      73                 : //-----------------------------------------------------------------------------
      74                 : 
      75                 : /* Implementation file */
      76          103658 : NS_IMPL_THREADSAFE_ISUPPORTS3(nsIDNService,
      77                 :                               nsIIDNService,
      78                 :                               nsIObserver,
      79                 :                               nsISupportsWeakReference)
      80                 : 
      81            1419 : nsresult nsIDNService::Init()
      82                 : {
      83                 :   NS_TIME_FUNCTION;
      84                 : 
      85            2838 :   nsCOMPtr<nsIPrefService> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
      86            1419 :   if (prefs)
      87            1419 :     prefs->GetBranch(NS_NET_PREF_IDNWHITELIST, getter_AddRefs(mIDNWhitelistPrefBranch));
      88                 : 
      89            2838 :   nsCOMPtr<nsIPrefBranch> prefInternal(do_QueryInterface(prefs));
      90            1419 :   if (prefInternal) {
      91            1419 :     prefInternal->AddObserver(NS_NET_PREF_IDNTESTBED, this, true); 
      92            1419 :     prefInternal->AddObserver(NS_NET_PREF_IDNPREFIX, this, true); 
      93            1419 :     prefInternal->AddObserver(NS_NET_PREF_IDNBLACKLIST, this, true);
      94            1419 :     prefInternal->AddObserver(NS_NET_PREF_SHOWPUNYCODE, this, true);
      95            1419 :     prefsChanged(prefInternal, nsnull);
      96                 :   }
      97                 : 
      98            1419 :   return NS_OK;
      99                 : }
     100                 : 
     101               2 : NS_IMETHODIMP nsIDNService::Observe(nsISupports *aSubject,
     102                 :                                     const char *aTopic,
     103                 :                                     const PRUnichar *aData)
     104                 : {
     105               2 :   if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
     106               4 :     nsCOMPtr<nsIPrefBranch> prefBranch( do_QueryInterface(aSubject) );
     107               2 :     if (prefBranch)
     108               2 :       prefsChanged(prefBranch, aData);
     109                 :   }
     110               2 :   return NS_OK;
     111                 : }
     112                 : 
     113            1421 : void nsIDNService::prefsChanged(nsIPrefBranch *prefBranch, const PRUnichar *pref)
     114                 : {
     115            1421 :   if (!pref || NS_LITERAL_STRING(NS_NET_PREF_IDNTESTBED).Equals(pref)) {
     116                 :     bool val;
     117            1419 :     if (NS_SUCCEEDED(prefBranch->GetBoolPref(NS_NET_PREF_IDNTESTBED, &val)))
     118               0 :       mMultilingualTestBed = val;
     119                 :   }
     120            1421 :   if (!pref || NS_LITERAL_STRING(NS_NET_PREF_IDNPREFIX).Equals(pref)) {
     121            2838 :     nsXPIDLCString prefix;
     122            1419 :     nsresult rv = prefBranch->GetCharPref(NS_NET_PREF_IDNPREFIX, getter_Copies(prefix));
     123            1419 :     if (NS_SUCCEEDED(rv) && prefix.Length() <= kACEPrefixLen)
     124               0 :       PL_strncpyz(nsIDNService::mACEPrefix, prefix.get(), kACEPrefixLen + 1);
     125                 :   }
     126            1421 :   if (!pref || NS_LITERAL_STRING(NS_NET_PREF_IDNBLACKLIST).Equals(pref)) {
     127            2840 :     nsCOMPtr<nsISupportsString> blacklist;
     128                 :     nsresult rv = prefBranch->GetComplexValue(NS_NET_PREF_IDNBLACKLIST,
     129                 :                                               NS_GET_IID(nsISupportsString),
     130            1420 :                                               getter_AddRefs(blacklist));
     131            1420 :     if (NS_SUCCEEDED(rv))
     132            1420 :       blacklist->ToString(getter_Copies(mIDNBlacklist));
     133                 :     else
     134               0 :       mIDNBlacklist.Truncate();
     135                 :   }
     136            1421 :   if (!pref || NS_LITERAL_STRING(NS_NET_PREF_SHOWPUNYCODE).Equals(pref)) {
     137                 :     bool val;
     138            1420 :     if (NS_SUCCEEDED(prefBranch->GetBoolPref(NS_NET_PREF_SHOWPUNYCODE, &val)))
     139            1420 :       mShowPunycode = val;
     140                 :   }
     141            1421 : }
     142                 : 
     143            1419 : nsIDNService::nsIDNService()
     144                 : {
     145                 :   // initialize to the official prefix (RFC 3490 "5. ACE prefix")
     146            1419 :   const char kIDNSPrefix[] = "xn--";
     147            1419 :   strcpy(mACEPrefix, kIDNSPrefix);
     148                 : 
     149            1419 :   mMultilingualTestBed = false;
     150                 : 
     151            1419 :   if (idn_success != idn_nameprep_create(NULL, &mNamePrepHandle))
     152               0 :     mNamePrepHandle = nsnull;
     153                 : 
     154            1419 :   mNormalizer = do_GetService(NS_UNICODE_NORMALIZER_CONTRACTID);
     155                 :   /* member initializers and constructor code */
     156            1419 : }
     157                 : 
     158            4248 : nsIDNService::~nsIDNService()
     159                 : {
     160            1416 :   idn_nameprep_destroy(mNamePrepHandle);
     161            5664 : }
     162                 : 
     163                 : /* ACString ConvertUTF8toACE (in AUTF8String input); */
     164            4620 : NS_IMETHODIMP nsIDNService::ConvertUTF8toACE(const nsACString & input, nsACString & ace)
     165                 : {
     166            4620 :   return UTF8toACE(input, ace, true);
     167                 : }
     168                 : 
     169            4637 : nsresult nsIDNService::UTF8toACE(const nsACString & input, nsACString & ace, bool allowUnassigned)
     170                 : {
     171                 :   nsresult rv;
     172            9274 :   NS_ConvertUTF8toUTF16 ustr(input);
     173                 : 
     174                 :   // map ideographic period to ASCII period etc.
     175            4637 :   normalizeFullStops(ustr);
     176                 : 
     177                 : 
     178                 :   PRUint32 len, offset;
     179            4637 :   len = 0;
     180            4637 :   offset = 0;
     181            9274 :   nsCAutoString encodedBuf;
     182                 : 
     183            4637 :   nsAString::const_iterator start, end;
     184            4637 :   ustr.BeginReading(start); 
     185            4637 :   ustr.EndReading(end); 
     186            4637 :   ace.Truncate();
     187                 : 
     188                 :   // encode nodes if non ASCII
     189           60901 :   while (start != end) {
     190           51629 :     len++;
     191           51629 :     if (*start++ == (PRUnichar)'.') {
     192            4848 :       rv = stringPrepAndACE(Substring(ustr, offset, len - 1), encodedBuf,
     193            4848 :                             allowUnassigned);
     194            4848 :       NS_ENSURE_SUCCESS(rv, rv);
     195                 : 
     196            4846 :       ace.Append(encodedBuf);
     197            4846 :       ace.Append('.');
     198            4846 :       offset += len;
     199            4846 :       len = 0;
     200                 :     }
     201                 :   }
     202                 : 
     203                 :   // add extra node for multilingual test bed
     204            4635 :   if (mMultilingualTestBed)
     205               0 :     ace.AppendLiteral("mltbd.");
     206                 :   // encode the last node if non ASCII
     207            4635 :   if (len) {
     208            4635 :     rv = stringPrepAndACE(Substring(ustr, offset, len), encodedBuf,
     209            4635 :                           allowUnassigned);
     210            4635 :     NS_ENSURE_SUCCESS(rv, rv);
     211                 : 
     212            4634 :     ace.Append(encodedBuf);
     213                 :   }
     214                 : 
     215            4634 :   return NS_OK;
     216                 : }
     217                 : 
     218                 : /* AUTF8String convertACEtoUTF8(in ACString input); */
     219               2 : NS_IMETHODIMP nsIDNService::ConvertACEtoUTF8(const nsACString & input, nsACString & _retval)
     220                 : {
     221               2 :   return ACEtoUTF8(input, _retval, true);
     222                 : }
     223                 : 
     224               7 : nsresult nsIDNService::ACEtoUTF8(const nsACString & input, nsACString & _retval,
     225                 :                                  bool allowUnassigned)
     226                 : {
     227                 :   // RFC 3490 - 4.2 ToUnicode
     228                 :   // ToUnicode never fails.  If any step fails, then the original input
     229                 :   // sequence is returned immediately in that step.
     230                 : 
     231               7 :   if (!IsASCII(input)) {
     232               0 :     _retval.Assign(input);
     233               0 :     return NS_OK;
     234                 :   }
     235                 :   
     236               7 :   PRUint32 len = 0, offset = 0;
     237              14 :   nsCAutoString decodedBuf;
     238                 : 
     239               7 :   nsACString::const_iterator start, end;
     240               7 :   input.BeginReading(start); 
     241               7 :   input.EndReading(end); 
     242               7 :   _retval.Truncate();
     243                 : 
     244                 :   // loop and decode nodes
     245             125 :   while (start != end) {
     246             112 :     len++;
     247             112 :     if (*start++ == '.') {
     248               7 :       if (NS_FAILED(decodeACE(Substring(input, offset, len - 1), decodedBuf,
     249                 :                               allowUnassigned))) {
     250               1 :         _retval.Assign(input);
     251               1 :         return NS_OK;
     252                 :       }
     253                 : 
     254               6 :       _retval.Append(decodedBuf);
     255               6 :       _retval.Append('.');
     256               6 :       offset += len;
     257               6 :       len = 0;
     258                 :     }
     259                 :   }
     260                 :   // decode the last node
     261               6 :   if (len) {
     262               6 :     if (NS_FAILED(decodeACE(Substring(input, offset, len), decodedBuf,
     263                 :                             allowUnassigned)))
     264               0 :       _retval.Assign(input);
     265                 :     else
     266               6 :       _retval.Append(decodedBuf);
     267                 :   }
     268                 : 
     269               6 :   return NS_OK;
     270                 : }
     271                 : 
     272                 : /* boolean isACE(in ACString input); */
     273          131454 : NS_IMETHODIMP nsIDNService::IsACE(const nsACString & input, bool *_retval)
     274                 : {
     275          131454 :   nsACString::const_iterator begin;
     276          131454 :   input.BeginReading(begin);
     277                 : 
     278          131454 :   const char *data = begin.get();
     279          131454 :   PRUint32 dataLen = begin.size_forward();
     280                 : 
     281                 :   // look for the ACE prefix in the input string.  it may occur
     282                 :   // at the beginning of any segment in the domain name.  for
     283                 :   // example: "www.xn--ENCODED.com"
     284                 : 
     285          131454 :   const char *p = PL_strncasestr(data, mACEPrefix, dataLen);
     286                 : 
     287          131454 :   *_retval = p && (p == data || *(p - 1) == '.');
     288          131454 :   return NS_OK;
     289                 : }
     290                 : 
     291                 : /* AUTF8String normalize(in AUTF8String input); */
     292              29 : NS_IMETHODIMP nsIDNService::Normalize(const nsACString & input, nsACString & output)
     293                 : {
     294                 :   // protect against bogus input
     295              29 :   NS_ENSURE_TRUE(IsUTF8(input), NS_ERROR_UNEXPECTED);
     296                 : 
     297              58 :   NS_ConvertUTF8toUTF16 inUTF16(input);
     298              29 :   normalizeFullStops(inUTF16);
     299                 : 
     300                 :   // pass the domain name to stringprep label by label
     301              58 :   nsAutoString outUTF16, outLabel;
     302                 : 
     303              29 :   PRUint32 len = 0, offset = 0;
     304                 :   nsresult rv;
     305              29 :   nsAString::const_iterator start, end;
     306              29 :   inUTF16.BeginReading(start);
     307              29 :   inUTF16.EndReading(end);
     308                 : 
     309             312 :   while (start != end) {
     310             272 :     len++;
     311             272 :     if (*start++ == PRUnichar('.')) {
     312              36 :       rv = stringPrep(Substring(inUTF16, offset, len - 1), outLabel, true);
     313              36 :       NS_ENSURE_SUCCESS(rv, rv);
     314                 :    
     315              18 :       outUTF16.Append(outLabel);
     316              18 :       outUTF16.Append(PRUnichar('.'));
     317              18 :       offset += len;
     318              18 :       len = 0;
     319                 :     }
     320                 :   }
     321              11 :   if (len) {
     322              11 :     rv = stringPrep(Substring(inUTF16, offset, len), outLabel, true);
     323              11 :     NS_ENSURE_SUCCESS(rv, rv);
     324                 : 
     325              11 :     outUTF16.Append(outLabel);
     326                 :   }
     327                 : 
     328              11 :   CopyUTF16toUTF8(outUTF16, output);
     329              11 :   if (!isOnlySafeChars(outUTF16, mIDNBlacklist))
     330               0 :     return ConvertUTF8toACE(output, output);
     331                 : 
     332              11 :   return NS_OK;
     333                 : }
     334                 : 
     335          131468 : NS_IMETHODIMP nsIDNService::ConvertToDisplayIDN(const nsACString & input, bool * _isASCII, nsACString & _retval)
     336                 : {
     337                 :   // If host is ACE, then convert to UTF-8 if the host is in the IDN whitelist.
     338                 :   // Else, if host is already UTF-8, then make sure it is normalized per IDN.
     339                 : 
     340                 :   nsresult rv;
     341                 : 
     342          131468 :   if (IsASCII(input)) {
     343                 :     // first, canonicalize the host to lowercase, for whitelist lookup
     344          131439 :     _retval = input;
     345          131439 :     ToLowerCase(_retval);
     346                 : 
     347                 :     bool isACE;
     348          131439 :     IsACE(_retval, &isACE);
     349                 : 
     350          131439 :     if (isACE && !mShowPunycode && isInWhitelist(_retval)) {
     351                 :       // ACEtoUTF8() can't fail, but might return the original ACE string
     352              10 :       nsCAutoString temp(_retval);
     353               5 :       ACEtoUTF8(temp, _retval, false);
     354               5 :       *_isASCII = IsASCII(_retval);
     355                 :     } else {
     356          131434 :       *_isASCII = true;
     357                 :     }
     358                 :   } else {
     359                 :     // We have to normalize the hostname before testing against the domain
     360                 :     // whitelist (see bug 315411), and to ensure the entire string gets
     361                 :     // normalized.
     362              29 :     rv = Normalize(input, _retval);
     363              29 :     if (NS_FAILED(rv)) return rv;
     364                 : 
     365              11 :     if (mShowPunycode && NS_SUCCEEDED(ConvertUTF8toACE(_retval, _retval))) {
     366               0 :       *_isASCII = true;
     367               0 :       return NS_OK;
     368                 :     }
     369                 : 
     370                 :     // normalization could result in an ASCII-only hostname. alternatively, if
     371                 :     // the host is converted to ACE by the normalizer, then the host may contain
     372                 :     // unsafe characters, so leave it ACE encoded. see bug 283016, bug 301694, and bug 309311.
     373              11 :     *_isASCII = IsASCII(_retval);
     374              11 :     if (!*_isASCII && !isInWhitelist(_retval)) {
     375               4 :       *_isASCII = true;
     376               4 :       return ConvertUTF8toACE(_retval, _retval);
     377                 :     }
     378                 :   }
     379                 : 
     380          131446 :   return NS_OK;
     381                 : }
     382                 : 
     383                 : //-----------------------------------------------------------------------------
     384                 : 
     385            1084 : static void utf16ToUcs4(const nsAString& in, PRUint32 *out, PRUint32 outBufLen, PRUint32 *outLen)
     386                 : {
     387            1084 :   PRUint32 i = 0;
     388            1084 :   nsAString::const_iterator start, end;
     389            1084 :   in.BeginReading(start); 
     390            1084 :   in.EndReading(end); 
     391                 : 
     392            1084 :   while (start != end) {
     393                 :     PRUnichar curChar;
     394                 : 
     395            6671 :     curChar= *start++;
     396                 : 
     397            6671 :     if (start != end &&
     398                 :         NS_IS_HIGH_SURROGATE(curChar) && 
     399               0 :         NS_IS_LOW_SURROGATE(*start)) {
     400               0 :       out[i] = SURROGATE_TO_UCS4(curChar, *start);
     401               0 :       ++start;
     402                 :     }
     403                 :     else
     404            6671 :       out[i] = curChar;
     405                 : 
     406            6671 :     i++;
     407            6671 :     if (i >= outBufLen) {
     408               0 :       NS_ERROR("input too big, the result truncated");
     409               0 :       out[outBufLen-1] = (PRUint32)'\0';
     410               0 :       *outLen = outBufLen-1;
     411               0 :       return;
     412                 :     }
     413                 :   }
     414            1084 :   out[i] = (PRUint32)'\0';
     415            1084 :   *outLen = i;
     416                 : }
     417                 : 
     418             573 : static void ucs4toUtf16(const PRUint32 *in, nsAString& out)
     419                 : {
     420            4642 :   while (*in) {
     421            3496 :     if (!IS_IN_BMP(*in)) {
     422               0 :       out.Append((PRUnichar) H_SURROGATE(*in));
     423               0 :       out.Append((PRUnichar) L_SURROGATE(*in));
     424                 :     }
     425                 :     else
     426            3496 :       out.Append((PRUnichar) *in);
     427            3496 :     in++;
     428                 :   }
     429             573 : }
     430                 : 
     431             517 : static nsresult punycode(const char* prefix, const nsAString& in, nsACString& out)
     432                 : {
     433                 :   PRUint32 ucs4Buf[kMaxDNSNodeLen + 1];
     434                 :   PRUint32 ucs4Len;
     435             517 :   utf16ToUcs4(in, ucs4Buf, kMaxDNSNodeLen, &ucs4Len);
     436                 : 
     437                 :   // need maximum 20 bits to encode 16 bit Unicode character
     438                 :   // (include null terminator)
     439             517 :   const PRUint32 kEncodedBufSize = kMaxDNSNodeLen * 20 / 8 + 1 + 1;  
     440                 :   char encodedBuf[kEncodedBufSize];
     441             517 :   punycode_uint encodedLength = kEncodedBufSize;
     442                 : 
     443                 :   enum punycode_status status = punycode_encode(ucs4Len,
     444                 :                                                 ucs4Buf,
     445                 :                                                 nsnull,
     446                 :                                                 &encodedLength,
     447             517 :                                                 encodedBuf);
     448                 : 
     449             517 :   if (punycode_success != status ||
     450                 :       encodedLength >= kEncodedBufSize)
     451               0 :     return NS_ERROR_FAILURE;
     452                 : 
     453             517 :   encodedBuf[encodedLength] = '\0';
     454             517 :   out.Assign(nsDependentCString(prefix) + nsDependentCString(encodedBuf));
     455                 : 
     456             517 :   return NS_OK;
     457                 : }
     458                 : 
     459               0 : static nsresult encodeToRACE(const char* prefix, const nsAString& in, nsACString& out)
     460                 : {
     461                 :   // need maximum 20 bits to encode 16 bit Unicode character
     462                 :   // (include null terminator)
     463               0 :   const PRUint32 kEncodedBufSize = kMaxDNSNodeLen * 20 / 8 + 1 + 1;  
     464                 : 
     465                 :   // set up a work buffer for RACE encoder
     466                 :   PRUnichar temp[kMaxDNSNodeLen + 2];
     467               0 :   temp[0] = 0xFFFF;   // set a place holder (to be filled by get_compress_mode)
     468               0 :   temp[in.Length() + 1] = (PRUnichar)'\0';
     469                 : 
     470               0 :   nsAString::const_iterator start, end;
     471               0 :   in.BeginReading(start); 
     472               0 :   in.EndReading(end);
     473                 :   
     474               0 :   for (PRUint32 i = 1; start != end; i++)
     475               0 :     temp[i] = *start++;
     476                 : 
     477                 :   // encode nodes if non ASCII
     478                 : 
     479                 :   char encodedBuf[kEncodedBufSize];
     480                 :   idn_result_t result = race_compress_encode((const unsigned short *) temp, 
     481                 :                                              get_compress_mode((unsigned short *) temp + 1), 
     482               0 :                                              encodedBuf, kEncodedBufSize);
     483               0 :   if (idn_success != result)
     484               0 :     return NS_ERROR_FAILURE;
     485                 : 
     486               0 :   out.Assign(prefix);
     487               0 :   out.Append(encodedBuf);
     488                 : 
     489               0 :   return NS_OK;
     490                 : }
     491                 : 
     492                 : // RFC 3454
     493                 : //
     494                 : // 1) Map -- For each character in the input, check if it has a mapping
     495                 : // and, if so, replace it with its mapping. This is described in section 3.
     496                 : //
     497                 : // 2) Normalize -- Possibly normalize the result of step 1 using Unicode
     498                 : // normalization. This is described in section 4.
     499                 : //
     500                 : // 3) Prohibit -- Check for any characters that are not allowed in the
     501                 : // output. If any are found, return an error. This is described in section
     502                 : // 5.
     503                 : //
     504                 : // 4) Check bidi -- Possibly check for right-to-left characters, and if any
     505                 : // are found, make sure that the whole string satisfies the requirements
     506                 : // for bidirectional strings. If the string does not satisfy the requirements
     507                 : // for bidirectional strings, return an error. This is described in section 6.
     508                 : //
     509                 : // 5) Check unassigned code points -- If allowUnassigned is false, check for
     510                 : // any unassigned Unicode points and if any are found return an error.
     511                 : // This is described in section 7.
     512                 : //
     513             567 : nsresult nsIDNService::stringPrep(const nsAString& in, nsAString& out,
     514                 :                                   bool allowUnassigned)
     515                 : {
     516             567 :   if (!mNamePrepHandle || !mNormalizer)
     517               0 :     return NS_ERROR_FAILURE;
     518                 : 
     519             567 :   nsresult rv = NS_OK;
     520                 :   PRUint32 ucs4Buf[kMaxDNSNodeLen + 1];
     521                 :   PRUint32 ucs4Len;
     522             567 :   utf16ToUcs4(in, ucs4Buf, kMaxDNSNodeLen, &ucs4Len);
     523                 : 
     524                 :   // map
     525                 :   idn_result_t idn_err;
     526                 : 
     527                 :   PRUint32 namePrepBuf[kMaxDNSNodeLen * 3];   // map up to three characters
     528                 :   idn_err = idn_nameprep_map(mNamePrepHandle, (const PRUint32 *) ucs4Buf,
     529             567 :                                      (PRUint32 *) namePrepBuf, kMaxDNSNodeLen * 3);
     530             567 :   NS_ENSURE_TRUE(idn_err == idn_success, NS_ERROR_FAILURE);
     531                 : 
     532            1134 :   nsAutoString namePrepStr;
     533             567 :   ucs4toUtf16(namePrepBuf, namePrepStr);
     534             567 :   if (namePrepStr.Length() >= kMaxDNSNodeLen)
     535               0 :     return NS_ERROR_FAILURE;
     536                 : 
     537                 :   // normalize
     538            1134 :   nsAutoString normlizedStr;
     539             567 :   rv = mNormalizer->NormalizeUnicodeNFKC(namePrepStr, normlizedStr);
     540             567 :   if (normlizedStr.Length() >= kMaxDNSNodeLen)
     541               0 :     return NS_ERROR_FAILURE;
     542                 : 
     543                 :   // prohibit
     544             567 :   const PRUint32 *found = nsnull;
     545                 :   idn_err = idn_nameprep_isprohibited(mNamePrepHandle, 
     546             567 :                                       (const PRUint32 *) ucs4Buf, &found);
     547             567 :   if (idn_err != idn_success || found)
     548              15 :     return NS_ERROR_FAILURE;
     549                 : 
     550                 :   // check bidi
     551                 :   idn_err = idn_nameprep_isvalidbidi(mNamePrepHandle, 
     552             552 :                                      (const PRUint32 *) ucs4Buf, &found);
     553             552 :   if (idn_err != idn_success || found)
     554               3 :     return NS_ERROR_FAILURE;
     555                 : 
     556             549 :   if (!allowUnassigned) {
     557                 :     // check unassigned code points
     558                 :     idn_err = idn_nameprep_isunassigned(mNamePrepHandle,
     559              16 :                                         (const PRUint32 *) ucs4Buf, &found);
     560              16 :     if (idn_err != idn_success || found)
     561               3 :       return NS_ERROR_FAILURE;
     562                 :   }
     563                 : 
     564                 :   // set the result string
     565             546 :   out.Assign(normlizedStr);
     566                 : 
     567             546 :   return rv;
     568                 : }
     569                 : 
     570             517 : nsresult nsIDNService::encodeToACE(const nsAString& in, nsACString& out)
     571                 : {
     572                 :   // RACE encode is supported for existing testing environment
     573             517 :   if (!strcmp("bq--", mACEPrefix))
     574               0 :     return encodeToRACE(mACEPrefix, in, out);
     575                 :   
     576                 :   // use punycoce
     577             517 :   return punycode(mACEPrefix, in, out);
     578                 : }
     579                 : 
     580            9483 : nsresult nsIDNService::stringPrepAndACE(const nsAString& in, nsACString& out,
     581                 :                                         bool allowUnassigned)
     582                 : {
     583            9483 :   nsresult rv = NS_OK;
     584                 : 
     585            9483 :   out.Truncate();
     586                 : 
     587            9483 :   if (in.Length() > kMaxDNSNodeLen) {
     588               0 :     NS_WARNING("IDN node too large");
     589               0 :     return NS_ERROR_FAILURE;
     590                 :   }
     591                 : 
     592            9483 :   if (IsASCII(in))
     593            8963 :     LossyCopyUTF16toASCII(in, out);
     594                 :   else {
     595            1040 :     nsAutoString strPrep;
     596             520 :     rv = stringPrep(in, strPrep, allowUnassigned);
     597             520 :     if (NS_SUCCEEDED(rv)) {
     598             517 :       if (IsASCII(strPrep))
     599               0 :         LossyCopyUTF16toASCII(strPrep, out);
     600                 :       else
     601             517 :         rv = encodeToACE(strPrep, out);
     602                 :     }
     603                 :   }
     604                 : 
     605            9483 :   if (out.Length() > kMaxDNSNodeLen) {
     606               0 :     NS_WARNING("IDN node too large");
     607               0 :     return NS_ERROR_FAILURE;
     608                 :   }
     609                 : 
     610            9483 :   return rv;
     611                 : }
     612                 : 
     613                 : // RFC 3490
     614                 : // 1) Whenever dots are used as label separators, the following characters
     615                 : //    MUST be recognized as dots: U+002E (full stop), U+3002 (ideographic full
     616                 : //    stop), U+FF0E (fullwidth full stop), U+FF61 (halfwidth ideographic full
     617                 : //    stop).
     618                 : 
     619            4666 : void nsIDNService::normalizeFullStops(nsAString& s)
     620                 : {
     621            4666 :   nsAString::const_iterator start, end;
     622            4666 :   s.BeginReading(start); 
     623            4666 :   s.EndReading(end); 
     624            4666 :   PRInt32 index = 0;
     625                 : 
     626           61297 :   while (start != end) {
     627           51965 :     switch (*start) {
     628                 :       case 0x3002:
     629                 :       case 0xFF0E:
     630                 :       case 0xFF61:
     631               0 :         s.Replace(index, 1, NS_LITERAL_STRING("."));
     632               0 :         break;
     633                 :       default:
     634           51965 :         break;
     635                 :     }
     636           51965 :     start++;
     637           51965 :     index++;
     638                 :   }
     639            4666 : }
     640                 : 
     641              13 : nsresult nsIDNService::decodeACE(const nsACString& in, nsACString& out,
     642                 :                                  bool allowUnassigned)
     643                 : {
     644                 :   bool isAce;
     645              13 :   IsACE(in, &isAce);
     646              13 :   if (!isAce) {
     647               7 :     out.Assign(in);
     648               7 :     return NS_OK;
     649                 :   }
     650                 : 
     651                 :   // RFC 3490 - 4.2 ToUnicode
     652                 :   // The ToUnicode output never contains more code points than its input.
     653               6 :   punycode_uint output_length = in.Length() - kACEPrefixLen + 1;
     654              12 :   punycode_uint *output = new punycode_uint[output_length];
     655               6 :   NS_ENSURE_TRUE(output, NS_ERROR_OUT_OF_MEMORY);
     656                 : 
     657               6 :   enum punycode_status status = punycode_decode(in.Length() - kACEPrefixLen,
     658              12 :                                                 PromiseFlatCString(in).get() + kACEPrefixLen,
     659                 :                                                 &output_length,
     660                 :                                                 output,
     661              12 :                                                 nsnull);
     662               6 :   if (status != punycode_success) {
     663               0 :     delete [] output;
     664               0 :     return NS_ERROR_FAILURE;
     665                 :   }
     666                 : 
     667                 :   // UCS4 -> UTF8
     668               6 :   output[output_length] = 0;
     669              12 :   nsAutoString utf16;
     670               6 :   ucs4toUtf16(output, utf16);
     671               6 :   delete [] output;
     672               6 :   if (!isOnlySafeChars(utf16, mIDNBlacklist))
     673               0 :     return NS_ERROR_FAILURE;
     674               6 :   CopyUTF16toUTF8(utf16, out);
     675                 : 
     676                 :   // Validation: encode back to ACE and compare the strings
     677              12 :   nsCAutoString ace;
     678               6 :   nsresult rv = UTF8toACE(out, ace, allowUnassigned);
     679               6 :   NS_ENSURE_SUCCESS(rv, rv);
     680                 : 
     681               5 :   if (!ace.Equals(in, nsCaseInsensitiveCStringComparator()))
     682               0 :     return NS_ERROR_FAILURE;
     683                 : 
     684               5 :   return NS_OK;
     685                 : }
     686                 : 
     687              17 : bool nsIDNService::isInWhitelist(const nsACString &host)
     688                 : {
     689              17 :   if (mIDNWhitelistPrefBranch) {
     690              34 :     nsCAutoString tld(host);
     691                 :     // make sure the host is ACE for lookup and check that there are no
     692                 :     // unassigned codepoints
     693              17 :     if (!IsASCII(tld) && NS_FAILED(UTF8toACE(tld, tld, false))) {
     694               2 :       return false;
     695                 :     }
     696                 : 
     697                 :     // truncate trailing dots first
     698              15 :     tld.Trim(".");
     699              15 :     PRInt32 pos = tld.RFind(".");
     700              15 :     if (pos == kNotFound)
     701               0 :       return false;
     702                 : 
     703              15 :     tld.Cut(0, pos + 1);
     704                 : 
     705                 :     bool safe;
     706              15 :     if (NS_SUCCEEDED(mIDNWhitelistPrefBranch->GetBoolPref(tld.get(), &safe)))
     707              12 :       return safe;
     708                 :   }
     709                 : 
     710               3 :   return false;
     711                 : }
     712                 : 

Generated by: LCOV version 1.7