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 :
|