1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /*
4 **********************************************************************
5 * Copyright (C) 2008-2016, International Business Machines
6 * Corporation and others. All Rights Reserved.
7 **********************************************************************
8 */
9
10 #include "unicode/utypes.h"
11 #include "unicode/uspoof.h"
12 #include "unicode/uchar.h"
13 #include "unicode/uniset.h"
14 #include "unicode/utf16.h"
15 #include "utrie2.h"
16 #include "cmemory.h"
17 #include "cstring.h"
18 #include "scriptset.h"
19 #include "umutex.h"
20 #include "udataswp.h"
21 #include "uassert.h"
22 #include "ucln_in.h"
23 #include "uspoof_impl.h"
24
25 #if !UCONFIG_NO_NORMALIZATION
26
27
28 U_NAMESPACE_BEGIN
29
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SpoofImpl)30 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SpoofImpl)
31
32 SpoofImpl::SpoofImpl(SpoofData *data, UErrorCode& status) {
33 construct(status);
34 fSpoofData = data;
35 }
36
SpoofImpl(UErrorCode & status)37 SpoofImpl::SpoofImpl(UErrorCode& status) {
38 construct(status);
39
40 // TODO: Call this method where it is actually needed, instead of in the
41 // constructor, to allow for lazy data loading. See #12696.
42 fSpoofData = SpoofData::getDefault(status);
43 }
44
SpoofImpl()45 SpoofImpl::SpoofImpl() {
46 UErrorCode status = U_ZERO_ERROR;
47 construct(status);
48
49 // TODO: Call this method where it is actually needed, instead of in the
50 // constructor, to allow for lazy data loading. See #12696.
51 fSpoofData = SpoofData::getDefault(status);
52 }
53
construct(UErrorCode & status)54 void SpoofImpl::construct(UErrorCode& status) {
55 fMagic = USPOOF_MAGIC;
56 fChecks = USPOOF_ALL_CHECKS;
57 fSpoofData = NULL;
58 fAllowedCharsSet = NULL;
59 fAllowedLocales = NULL;
60 fRestrictionLevel = USPOOF_HIGHLY_RESTRICTIVE;
61
62 if (U_FAILURE(status)) { return; }
63
64 UnicodeSet *allowedCharsSet = new UnicodeSet(0, 0x10ffff);
65 fAllowedCharsSet = allowedCharsSet;
66 fAllowedLocales = uprv_strdup("");
67 if (fAllowedCharsSet == NULL || fAllowedLocales == NULL) {
68 status = U_MEMORY_ALLOCATION_ERROR;
69 return;
70 }
71 allowedCharsSet->freeze();
72 }
73
74
75 // Copy Constructor, used by the user level clone() function.
SpoofImpl(const SpoofImpl & src,UErrorCode & status)76 SpoofImpl::SpoofImpl(const SpoofImpl &src, UErrorCode &status) :
77 fMagic(0), fChecks(USPOOF_ALL_CHECKS), fSpoofData(NULL), fAllowedCharsSet(NULL) ,
78 fAllowedLocales(NULL) {
79 if (U_FAILURE(status)) {
80 return;
81 }
82 fMagic = src.fMagic;
83 fChecks = src.fChecks;
84 if (src.fSpoofData != NULL) {
85 fSpoofData = src.fSpoofData->addReference();
86 }
87 fAllowedCharsSet = static_cast<const UnicodeSet *>(src.fAllowedCharsSet->clone());
88 fAllowedLocales = uprv_strdup(src.fAllowedLocales);
89 if (fAllowedCharsSet == NULL || fAllowedLocales == NULL) {
90 status = U_MEMORY_ALLOCATION_ERROR;
91 }
92 fRestrictionLevel = src.fRestrictionLevel;
93 }
94
~SpoofImpl()95 SpoofImpl::~SpoofImpl() {
96 fMagic = 0; // head off application errors by preventing use of
97 // of deleted objects.
98 if (fSpoofData != NULL) {
99 fSpoofData->removeReference(); // Will delete if refCount goes to zero.
100 }
101 delete fAllowedCharsSet;
102 uprv_free((void *)fAllowedLocales);
103 }
104
105 // Cast this instance as a USpoofChecker for the C API.
asUSpoofChecker()106 USpoofChecker *SpoofImpl::asUSpoofChecker() {
107 return reinterpret_cast<USpoofChecker*>(this);
108 }
109
110 //
111 // Incoming parameter check on Status and the SpoofChecker object
112 // received from the C API.
113 //
validateThis(const USpoofChecker * sc,UErrorCode & status)114 const SpoofImpl *SpoofImpl::validateThis(const USpoofChecker *sc, UErrorCode &status) {
115 if (U_FAILURE(status)) {
116 return NULL;
117 }
118 if (sc == NULL) {
119 status = U_ILLEGAL_ARGUMENT_ERROR;
120 return NULL;
121 }
122 SpoofImpl *This = (SpoofImpl *)sc;
123 if (This->fMagic != USPOOF_MAGIC) {
124 status = U_INVALID_FORMAT_ERROR;
125 return NULL;
126 }
127 if (This->fSpoofData != NULL && !This->fSpoofData->validateDataVersion(status)) {
128 return NULL;
129 }
130 return This;
131 }
132
validateThis(USpoofChecker * sc,UErrorCode & status)133 SpoofImpl *SpoofImpl::validateThis(USpoofChecker *sc, UErrorCode &status) {
134 return const_cast<SpoofImpl *>
135 (SpoofImpl::validateThis(const_cast<const USpoofChecker *>(sc), status));
136 }
137
138
setAllowedLocales(const char * localesList,UErrorCode & status)139 void SpoofImpl::setAllowedLocales(const char *localesList, UErrorCode &status) {
140 UnicodeSet allowedChars;
141 UnicodeSet *tmpSet = NULL;
142 const char *locStart = localesList;
143 const char *locEnd = NULL;
144 const char *localesListEnd = localesList + uprv_strlen(localesList);
145 int32_t localeListCount = 0; // Number of locales provided by caller.
146
147 // Loop runs once per locale from the localesList, a comma separated list of locales.
148 do {
149 locEnd = uprv_strchr(locStart, ',');
150 if (locEnd == NULL) {
151 locEnd = localesListEnd;
152 }
153 while (*locStart == ' ') {
154 locStart++;
155 }
156 const char *trimmedEnd = locEnd-1;
157 while (trimmedEnd > locStart && *trimmedEnd == ' ') {
158 trimmedEnd--;
159 }
160 if (trimmedEnd <= locStart) {
161 break;
162 }
163 const char *locale = uprv_strndup(locStart, (int32_t)(trimmedEnd + 1 - locStart));
164 localeListCount++;
165
166 // We have one locale from the locales list.
167 // Add the script chars for this locale to the accumulating set of allowed chars.
168 // If the locale is no good, we will be notified back via status.
169 addScriptChars(locale, &allowedChars, status);
170 uprv_free((void *)locale);
171 if (U_FAILURE(status)) {
172 break;
173 }
174 locStart = locEnd + 1;
175 } while (locStart < localesListEnd);
176
177 // If our caller provided an empty list of locales, we disable the allowed characters checking
178 if (localeListCount == 0) {
179 uprv_free((void *)fAllowedLocales);
180 fAllowedLocales = uprv_strdup("");
181 tmpSet = new UnicodeSet(0, 0x10ffff);
182 if (fAllowedLocales == NULL || tmpSet == NULL) {
183 status = U_MEMORY_ALLOCATION_ERROR;
184 return;
185 }
186 tmpSet->freeze();
187 delete fAllowedCharsSet;
188 fAllowedCharsSet = tmpSet;
189 fChecks &= ~USPOOF_CHAR_LIMIT;
190 return;
191 }
192
193
194 // Add all common and inherited characters to the set of allowed chars.
195 UnicodeSet tempSet;
196 tempSet.applyIntPropertyValue(UCHAR_SCRIPT, USCRIPT_COMMON, status);
197 allowedChars.addAll(tempSet);
198 tempSet.applyIntPropertyValue(UCHAR_SCRIPT, USCRIPT_INHERITED, status);
199 allowedChars.addAll(tempSet);
200
201 // If anything went wrong, we bail out without changing
202 // the state of the spoof checker.
203 if (U_FAILURE(status)) {
204 return;
205 }
206
207 // Store the updated spoof checker state.
208 tmpSet = static_cast<UnicodeSet *>(allowedChars.clone());
209 const char *tmpLocalesList = uprv_strdup(localesList);
210 if (tmpSet == NULL || tmpLocalesList == NULL) {
211 status = U_MEMORY_ALLOCATION_ERROR;
212 return;
213 }
214 uprv_free((void *)fAllowedLocales);
215 fAllowedLocales = tmpLocalesList;
216 tmpSet->freeze();
217 delete fAllowedCharsSet;
218 fAllowedCharsSet = tmpSet;
219 fChecks |= USPOOF_CHAR_LIMIT;
220 }
221
222
getAllowedLocales(UErrorCode &)223 const char * SpoofImpl::getAllowedLocales(UErrorCode &/*status*/) {
224 return fAllowedLocales;
225 }
226
227
228 // Given a locale (a language), add all the characters from all of the scripts used with that language
229 // to the allowedChars UnicodeSet
230
addScriptChars(const char * locale,UnicodeSet * allowedChars,UErrorCode & status)231 void SpoofImpl::addScriptChars(const char *locale, UnicodeSet *allowedChars, UErrorCode &status) {
232 UScriptCode scripts[30];
233
234 int32_t numScripts = uscript_getCode(locale, scripts, UPRV_LENGTHOF(scripts), &status);
235 if (U_FAILURE(status)) {
236 return;
237 }
238 if (status == U_USING_DEFAULT_WARNING) {
239 status = U_ILLEGAL_ARGUMENT_ERROR;
240 return;
241 }
242 UnicodeSet tmpSet;
243 int32_t i;
244 for (i=0; i<numScripts; i++) {
245 tmpSet.applyIntPropertyValue(UCHAR_SCRIPT, scripts[i], status);
246 allowedChars->addAll(tmpSet);
247 }
248 }
249
250 // Computes the augmented script set for a code point, according to UTS 39 section 5.1.
getAugmentedScriptSet(UChar32 codePoint,ScriptSet & result,UErrorCode & status)251 void SpoofImpl::getAugmentedScriptSet(UChar32 codePoint, ScriptSet& result, UErrorCode& status) {
252 result.resetAll();
253 result.setScriptExtensions(codePoint, status);
254 if (U_FAILURE(status)) { return; }
255
256 // Section 5.1 step 1
257 if (result.test(USCRIPT_HAN, status)) {
258 result.set(USCRIPT_HAN_WITH_BOPOMOFO, status);
259 result.set(USCRIPT_JAPANESE, status);
260 result.set(USCRIPT_KOREAN, status);
261 }
262 if (result.test(USCRIPT_HIRAGANA, status)) {
263 result.set(USCRIPT_JAPANESE, status);
264 }
265 if (result.test(USCRIPT_KATAKANA, status)) {
266 result.set(USCRIPT_JAPANESE, status);
267 }
268 if (result.test(USCRIPT_HANGUL, status)) {
269 result.set(USCRIPT_KOREAN, status);
270 }
271 if (result.test(USCRIPT_BOPOMOFO, status)) {
272 result.set(USCRIPT_HAN_WITH_BOPOMOFO, status);
273 }
274
275 // Section 5.1 step 2
276 if (result.test(USCRIPT_COMMON, status) || result.test(USCRIPT_INHERITED, status)) {
277 result.setAll();
278 }
279 }
280
281 // Computes the resolved script set for a string, according to UTS 39 section 5.1.
getResolvedScriptSet(const UnicodeString & input,ScriptSet & result,UErrorCode & status) const282 void SpoofImpl::getResolvedScriptSet(const UnicodeString& input, ScriptSet& result, UErrorCode& status) const {
283 getResolvedScriptSetWithout(input, USCRIPT_CODE_LIMIT, result, status);
284 }
285
286 // Computes the resolved script set for a string, omitting characters having the specified script.
287 // If USCRIPT_CODE_LIMIT is passed as the second argument, all characters are included.
getResolvedScriptSetWithout(const UnicodeString & input,UScriptCode script,ScriptSet & result,UErrorCode & status) const288 void SpoofImpl::getResolvedScriptSetWithout(const UnicodeString& input, UScriptCode script, ScriptSet& result, UErrorCode& status) const {
289 result.setAll();
290
291 ScriptSet temp;
292 UChar32 codePoint;
293 for (int32_t i = 0; i < input.length(); i += U16_LENGTH(codePoint)) {
294 codePoint = input.char32At(i);
295
296 // Compute the augmented script set for the character
297 getAugmentedScriptSet(codePoint, temp, status);
298 if (U_FAILURE(status)) { return; }
299
300 // Intersect the augmented script set with the resolved script set, but only if the character doesn't
301 // have the script specified in the function call
302 if (script == USCRIPT_CODE_LIMIT || !temp.test(script, status)) {
303 result.intersect(temp);
304 }
305 }
306 }
307
308 // Computes the set of numerics for a string, according to UTS 39 section 5.3.
getNumerics(const UnicodeString & input,UnicodeSet & result,UErrorCode &) const309 void SpoofImpl::getNumerics(const UnicodeString& input, UnicodeSet& result, UErrorCode& /*status*/) const {
310 result.clear();
311
312 UChar32 codePoint;
313 for (int32_t i = 0; i < input.length(); i += U16_LENGTH(codePoint)) {
314 codePoint = input.char32At(i);
315
316 // Store a representative character for each kind of decimal digit
317 if (u_charType(codePoint) == U_DECIMAL_DIGIT_NUMBER) {
318 // Store the zero character as a representative for comparison.
319 // Unicode guarantees it is codePoint - value
320 result.add(codePoint - (UChar32)u_getNumericValue(codePoint));
321 }
322 }
323 }
324
325 // Computes the restriction level of a string, according to UTS 39 section 5.2.
getRestrictionLevel(const UnicodeString & input,UErrorCode & status) const326 URestrictionLevel SpoofImpl::getRestrictionLevel(const UnicodeString& input, UErrorCode& status) const {
327 // Section 5.2 step 1:
328 if (!fAllowedCharsSet->containsAll(input)) {
329 return USPOOF_UNRESTRICTIVE;
330 }
331
332 // Section 5.2 step 2
333 // Java use a static UnicodeSet for this test. In C++, avoid the static variable
334 // and just do a simple for loop.
335 UBool allASCII = TRUE;
336 for (int32_t i=0, length=input.length(); i<length; i++) {
337 if (input.charAt(i) > 0x7f) {
338 allASCII = FALSE;
339 break;
340 }
341 }
342 if (allASCII) {
343 return USPOOF_ASCII;
344 }
345
346 // Section 5.2 steps 3:
347 ScriptSet resolvedScriptSet;
348 getResolvedScriptSet(input, resolvedScriptSet, status);
349 if (U_FAILURE(status)) { return USPOOF_UNRESTRICTIVE; }
350
351 // Section 5.2 step 4:
352 if (!resolvedScriptSet.isEmpty()) {
353 return USPOOF_SINGLE_SCRIPT_RESTRICTIVE;
354 }
355
356 // Section 5.2 step 5:
357 ScriptSet resolvedNoLatn;
358 getResolvedScriptSetWithout(input, USCRIPT_LATIN, resolvedNoLatn, status);
359 if (U_FAILURE(status)) { return USPOOF_UNRESTRICTIVE; }
360
361 // Section 5.2 step 6:
362 if (resolvedNoLatn.test(USCRIPT_HAN_WITH_BOPOMOFO, status)
363 || resolvedNoLatn.test(USCRIPT_JAPANESE, status)
364 || resolvedNoLatn.test(USCRIPT_KOREAN, status)) {
365 return USPOOF_HIGHLY_RESTRICTIVE;
366 }
367
368 // Section 5.2 step 7:
369 if (!resolvedNoLatn.isEmpty()
370 && !resolvedNoLatn.test(USCRIPT_CYRILLIC, status)
371 && !resolvedNoLatn.test(USCRIPT_GREEK, status)
372 && !resolvedNoLatn.test(USCRIPT_CHEROKEE, status)) {
373 return USPOOF_MODERATELY_RESTRICTIVE;
374 }
375
376 // Section 5.2 step 8:
377 return USPOOF_MINIMALLY_RESTRICTIVE;
378 }
379
findHiddenOverlay(const UnicodeString & input,UErrorCode &) const380 int32_t SpoofImpl::findHiddenOverlay(const UnicodeString& input, UErrorCode&) const {
381 bool sawLeadCharacter = false;
382 for (int32_t i=0; i<input.length();) {
383 UChar32 cp = input.char32At(i);
384 if (sawLeadCharacter && cp == 0x0307) {
385 return i;
386 }
387 uint8_t combiningClass = u_getCombiningClass(cp);
388 // Skip over characters except for those with combining class 0 (non-combining characters) or with
389 // combining class 230 (same class as U+0307)
390 U_ASSERT(u_getCombiningClass(0x0307) == 230);
391 if (combiningClass == 0 || combiningClass == 230) {
392 sawLeadCharacter = isIllegalCombiningDotLeadCharacter(cp);
393 }
394 i += U16_LENGTH(cp);
395 }
396 return -1;
397 }
398
isIllegalCombiningDotLeadCharacterNoLookup(UChar32 cp)399 static inline bool isIllegalCombiningDotLeadCharacterNoLookup(UChar32 cp) {
400 return cp == u'i' || cp == u'j' || cp == u'ı' || cp == u'ȷ' || cp == u'l' ||
401 u_hasBinaryProperty(cp, UCHAR_SOFT_DOTTED);
402 }
403
isIllegalCombiningDotLeadCharacter(UChar32 cp) const404 bool SpoofImpl::isIllegalCombiningDotLeadCharacter(UChar32 cp) const {
405 if (isIllegalCombiningDotLeadCharacterNoLookup(cp)) {
406 return true;
407 }
408 UnicodeString skelStr;
409 fSpoofData->confusableLookup(cp, skelStr);
410 UChar32 finalCp = skelStr.char32At(skelStr.moveIndex32(skelStr.length(), -1));
411 if (finalCp != cp && isIllegalCombiningDotLeadCharacterNoLookup(finalCp)) {
412 return true;
413 }
414 return false;
415 }
416
417
418
419 // Convert a text format hex number. Utility function used by builder code. Static.
420 // Input: UChar *string text. Output: a UChar32
421 // Input has been pre-checked, and will have no non-hex chars.
422 // The number must fall in the code point range of 0..0x10ffff
423 // Static Function.
ScanHex(const UChar * s,int32_t start,int32_t limit,UErrorCode & status)424 UChar32 SpoofImpl::ScanHex(const UChar *s, int32_t start, int32_t limit, UErrorCode &status) {
425 if (U_FAILURE(status)) {
426 return 0;
427 }
428 U_ASSERT(limit-start > 0);
429 uint32_t val = 0;
430 int i;
431 for (i=start; i<limit; i++) {
432 int digitVal = s[i] - 0x30;
433 if (digitVal>9) {
434 digitVal = 0xa + (s[i] - 0x41); // Upper Case 'A'
435 }
436 if (digitVal>15) {
437 digitVal = 0xa + (s[i] - 0x61); // Lower Case 'a'
438 }
439 U_ASSERT(digitVal <= 0xf);
440 val <<= 4;
441 val += digitVal;
442 }
443 if (val > 0x10ffff) {
444 status = U_PARSE_ERROR;
445 val = 0;
446 }
447 return (UChar32)val;
448 }
449
450
451 //-----------------------------------------
452 //
453 // class CheckResult Implementation
454 //
455 //-----------------------------------------
456
CheckResult()457 CheckResult::CheckResult() : fMagic(USPOOF_CHECK_MAGIC) {
458 clear();
459 }
460
asUSpoofCheckResult()461 USpoofCheckResult* CheckResult::asUSpoofCheckResult() {
462 return reinterpret_cast<USpoofCheckResult*>(this);
463 }
464
465 //
466 // Incoming parameter check on Status and the CheckResult object
467 // received from the C API.
468 //
validateThis(const USpoofCheckResult * ptr,UErrorCode & status)469 const CheckResult* CheckResult::validateThis(const USpoofCheckResult *ptr, UErrorCode &status) {
470 if (U_FAILURE(status)) { return NULL; }
471 if (ptr == NULL) {
472 status = U_ILLEGAL_ARGUMENT_ERROR;
473 return NULL;
474 }
475 CheckResult *This = (CheckResult*) ptr;
476 if (This->fMagic != USPOOF_CHECK_MAGIC) {
477 status = U_INVALID_FORMAT_ERROR;
478 return NULL;
479 }
480 return This;
481 }
482
validateThis(USpoofCheckResult * ptr,UErrorCode & status)483 CheckResult* CheckResult::validateThis(USpoofCheckResult *ptr, UErrorCode &status) {
484 return const_cast<CheckResult *>
485 (CheckResult::validateThis(const_cast<const USpoofCheckResult*>(ptr), status));
486 }
487
clear()488 void CheckResult::clear() {
489 fChecks = 0;
490 fNumerics.clear();
491 fRestrictionLevel = USPOOF_UNDEFINED_RESTRICTIVE;
492 }
493
toCombinedBitmask(int32_t enabledChecks)494 int32_t CheckResult::toCombinedBitmask(int32_t enabledChecks) {
495 if ((enabledChecks & USPOOF_AUX_INFO) != 0 && fRestrictionLevel != USPOOF_UNDEFINED_RESTRICTIVE) {
496 return fChecks | fRestrictionLevel;
497 } else {
498 return fChecks;
499 }
500 }
501
~CheckResult()502 CheckResult::~CheckResult() {
503 }
504
505 //----------------------------------------------------------------------------------------------
506 //
507 // class SpoofData Implementation
508 //
509 //----------------------------------------------------------------------------------------------
510
511
validateDataVersion(UErrorCode & status) const512 UBool SpoofData::validateDataVersion(UErrorCode &status) const {
513 if (U_FAILURE(status) ||
514 fRawData == NULL ||
515 fRawData->fMagic != USPOOF_MAGIC ||
516 fRawData->fFormatVersion[0] != USPOOF_CONFUSABLE_DATA_FORMAT_VERSION ||
517 fRawData->fFormatVersion[1] != 0 ||
518 fRawData->fFormatVersion[2] != 0 ||
519 fRawData->fFormatVersion[3] != 0) {
520 status = U_INVALID_FORMAT_ERROR;
521 return FALSE;
522 }
523 return TRUE;
524 }
525
526 static UBool U_CALLCONV
spoofDataIsAcceptable(void * context,const char *,const char *,const UDataInfo * pInfo)527 spoofDataIsAcceptable(void *context,
528 const char * /* type */, const char * /*name*/,
529 const UDataInfo *pInfo) {
530 if(
531 pInfo->size >= 20 &&
532 pInfo->isBigEndian == U_IS_BIG_ENDIAN &&
533 pInfo->charsetFamily == U_CHARSET_FAMILY &&
534 pInfo->dataFormat[0] == 0x43 && // dataFormat="Cfu "
535 pInfo->dataFormat[1] == 0x66 &&
536 pInfo->dataFormat[2] == 0x75 &&
537 pInfo->dataFormat[3] == 0x20 &&
538 pInfo->formatVersion[0] == USPOOF_CONFUSABLE_DATA_FORMAT_VERSION
539 ) {
540 UVersionInfo *version = static_cast<UVersionInfo *>(context);
541 if(version != NULL) {
542 uprv_memcpy(version, pInfo->dataVersion, 4);
543 }
544 return TRUE;
545 } else {
546 return FALSE;
547 }
548 }
549
550 // Methods for the loading of the default confusables data file. The confusable
551 // data is loaded only when it is needed.
552 //
553 // SpoofData::getDefault() - Return the default confusables data, and call the
554 // initOnce() if it is not available. Adds a reference
555 // to the SpoofData that the caller is responsible for
556 // decrementing when they are done with the data.
557 //
558 // uspoof_loadDefaultData - Called once, from initOnce(). The resulting SpoofData
559 // is shared by all spoof checkers using the default data.
560 //
561 // uspoof_cleanupDefaultData - Called during cleanup.
562 //
563
564 static UInitOnce gSpoofInitDefaultOnce = U_INITONCE_INITIALIZER;
565 static SpoofData* gDefaultSpoofData;
566
567 static UBool U_CALLCONV
uspoof_cleanupDefaultData(void)568 uspoof_cleanupDefaultData(void) {
569 if (gDefaultSpoofData) {
570 // Will delete, assuming all user-level spoof checkers were closed.
571 gDefaultSpoofData->removeReference();
572 gDefaultSpoofData = nullptr;
573 gSpoofInitDefaultOnce.reset();
574 }
575 return TRUE;
576 }
577
uspoof_loadDefaultData(UErrorCode & status)578 static void U_CALLCONV uspoof_loadDefaultData(UErrorCode& status) {
579 UDataMemory *udm = udata_openChoice(nullptr, "cfu", "confusables",
580 spoofDataIsAcceptable,
581 nullptr, // context, would receive dataVersion if supplied.
582 &status);
583 if (U_FAILURE(status)) { return; }
584 gDefaultSpoofData = new SpoofData(udm, status);
585 if (U_FAILURE(status)) {
586 delete gDefaultSpoofData;
587 gDefaultSpoofData = nullptr;
588 return;
589 }
590 if (gDefaultSpoofData == nullptr) {
591 status = U_MEMORY_ALLOCATION_ERROR;
592 return;
593 }
594 ucln_i18n_registerCleanup(UCLN_I18N_SPOOFDATA, uspoof_cleanupDefaultData);
595 }
596
getDefault(UErrorCode & status)597 SpoofData* SpoofData::getDefault(UErrorCode& status) {
598 umtx_initOnce(gSpoofInitDefaultOnce, &uspoof_loadDefaultData, status);
599 if (U_FAILURE(status)) { return NULL; }
600 gDefaultSpoofData->addReference();
601 return gDefaultSpoofData;
602 }
603
604
605
SpoofData(UDataMemory * udm,UErrorCode & status)606 SpoofData::SpoofData(UDataMemory *udm, UErrorCode &status)
607 {
608 reset();
609 if (U_FAILURE(status)) {
610 return;
611 }
612 fUDM = udm;
613 // fRawData is non-const because it may be constructed by the data builder.
614 fRawData = reinterpret_cast<SpoofDataHeader *>(
615 const_cast<void *>(udata_getMemory(udm)));
616 validateDataVersion(status);
617 initPtrs(status);
618 }
619
620
SpoofData(const void * data,int32_t length,UErrorCode & status)621 SpoofData::SpoofData(const void *data, int32_t length, UErrorCode &status)
622 {
623 reset();
624 if (U_FAILURE(status)) {
625 return;
626 }
627 if ((size_t)length < sizeof(SpoofDataHeader)) {
628 status = U_INVALID_FORMAT_ERROR;
629 return;
630 }
631 if (data == NULL) {
632 status = U_ILLEGAL_ARGUMENT_ERROR;
633 return;
634 }
635 void *ncData = const_cast<void *>(data);
636 fRawData = static_cast<SpoofDataHeader *>(ncData);
637 if (length < fRawData->fLength) {
638 status = U_INVALID_FORMAT_ERROR;
639 return;
640 }
641 validateDataVersion(status);
642 initPtrs(status);
643 }
644
645
646 // Spoof Data constructor for use from data builder.
647 // Initializes a new, empty data area that will be populated later.
SpoofData(UErrorCode & status)648 SpoofData::SpoofData(UErrorCode &status) {
649 reset();
650 if (U_FAILURE(status)) {
651 return;
652 }
653 fDataOwned = true;
654
655 // The spoof header should already be sized to be a multiple of 16 bytes.
656 // Just in case it's not, round it up.
657 uint32_t initialSize = (sizeof(SpoofDataHeader) + 15) & ~15;
658 U_ASSERT(initialSize == sizeof(SpoofDataHeader));
659
660 fRawData = static_cast<SpoofDataHeader *>(uprv_malloc(initialSize));
661 fMemLimit = initialSize;
662 if (fRawData == NULL) {
663 status = U_MEMORY_ALLOCATION_ERROR;
664 return;
665 }
666 uprv_memset(fRawData, 0, initialSize);
667
668 fRawData->fMagic = USPOOF_MAGIC;
669 fRawData->fFormatVersion[0] = USPOOF_CONFUSABLE_DATA_FORMAT_VERSION;
670 fRawData->fFormatVersion[1] = 0;
671 fRawData->fFormatVersion[2] = 0;
672 fRawData->fFormatVersion[3] = 0;
673 initPtrs(status);
674 }
675
676 // reset() - initialize all fields.
677 // Should be updated if any new fields are added.
678 // Called by constructors to put things in a known initial state.
reset()679 void SpoofData::reset() {
680 fRawData = NULL;
681 fDataOwned = FALSE;
682 fUDM = NULL;
683 fMemLimit = 0;
684 fRefCount = 1;
685 fCFUKeys = NULL;
686 fCFUValues = NULL;
687 fCFUStrings = NULL;
688 }
689
690
691 // SpoofData::initPtrs()
692 // Initialize the pointers to the various sections of the raw data.
693 //
694 // This function is used both during the Trie building process (multiple
695 // times, as the individual data sections are added), and
696 // during the opening of a Spoof Checker from prebuilt data.
697 //
698 // The pointers for non-existent data sections (identified by an offset of 0)
699 // are set to NULL.
700 //
701 // Note: During building the data, adding each new data section
702 // reallocs the raw data area, which likely relocates it, which
703 // in turn requires reinitializing all of the pointers into it, hence
704 // multiple calls to this function during building.
705 //
initPtrs(UErrorCode & status)706 void SpoofData::initPtrs(UErrorCode &status) {
707 fCFUKeys = NULL;
708 fCFUValues = NULL;
709 fCFUStrings = NULL;
710 if (U_FAILURE(status)) {
711 return;
712 }
713 if (fRawData->fCFUKeys != 0) {
714 fCFUKeys = (int32_t *)((char *)fRawData + fRawData->fCFUKeys);
715 }
716 if (fRawData->fCFUStringIndex != 0) {
717 fCFUValues = (uint16_t *)((char *)fRawData + fRawData->fCFUStringIndex);
718 }
719 if (fRawData->fCFUStringTable != 0) {
720 fCFUStrings = (UChar *)((char *)fRawData + fRawData->fCFUStringTable);
721 }
722 }
723
724
~SpoofData()725 SpoofData::~SpoofData() {
726 if (fDataOwned) {
727 uprv_free(fRawData);
728 }
729 fRawData = NULL;
730 if (fUDM != NULL) {
731 udata_close(fUDM);
732 }
733 fUDM = NULL;
734 }
735
736
removeReference()737 void SpoofData::removeReference() {
738 if (umtx_atomic_dec(&fRefCount) == 0) {
739 delete this;
740 }
741 }
742
743
addReference()744 SpoofData *SpoofData::addReference() {
745 umtx_atomic_inc(&fRefCount);
746 return this;
747 }
748
749
reserveSpace(int32_t numBytes,UErrorCode & status)750 void *SpoofData::reserveSpace(int32_t numBytes, UErrorCode &status) {
751 if (U_FAILURE(status)) {
752 return NULL;
753 }
754 if (!fDataOwned) {
755 U_ASSERT(FALSE);
756 status = U_INTERNAL_PROGRAM_ERROR;
757 return NULL;
758 }
759
760 numBytes = (numBytes + 15) & ~15; // Round up to a multiple of 16
761 uint32_t returnOffset = fMemLimit;
762 fMemLimit += numBytes;
763 fRawData = static_cast<SpoofDataHeader *>(uprv_realloc(fRawData, fMemLimit));
764 fRawData->fLength = fMemLimit;
765 uprv_memset((char *)fRawData + returnOffset, 0, numBytes);
766 initPtrs(status);
767 return (char *)fRawData + returnOffset;
768 }
769
serialize(void * buf,int32_t capacity,UErrorCode & status) const770 int32_t SpoofData::serialize(void *buf, int32_t capacity, UErrorCode &status) const {
771 int32_t dataSize = fRawData->fLength;
772 if (capacity < dataSize) {
773 status = U_BUFFER_OVERFLOW_ERROR;
774 return dataSize;
775 }
776 uprv_memcpy(buf, fRawData, dataSize);
777 return dataSize;
778 }
779
size() const780 int32_t SpoofData::size() const {
781 return fRawData->fLength;
782 }
783
784 //-------------------------------
785 //
786 // Front-end APIs for SpoofData
787 //
788 //-------------------------------
789
confusableLookup(UChar32 inChar,UnicodeString & dest) const790 int32_t SpoofData::confusableLookup(UChar32 inChar, UnicodeString &dest) const {
791 // Perform a binary search.
792 // [lo, hi), i.e lo is inclusive, hi is exclusive.
793 // The result after the loop will be in lo.
794 int32_t lo = 0;
795 int32_t hi = length();
796 do {
797 int32_t mid = (lo + hi) / 2;
798 if (codePointAt(mid) > inChar) {
799 hi = mid;
800 } else if (codePointAt(mid) < inChar) {
801 lo = mid;
802 } else {
803 // Found result. Break early.
804 lo = mid;
805 break;
806 }
807 } while (hi - lo > 1);
808
809 // Did we find an entry? If not, the char maps to itself.
810 if (codePointAt(lo) != inChar) {
811 dest.append(inChar);
812 return 1;
813 }
814
815 // Add the element to the string builder and return.
816 return appendValueTo(lo, dest);
817 }
818
length() const819 int32_t SpoofData::length() const {
820 return fRawData->fCFUKeysSize;
821 }
822
codePointAt(int32_t index) const823 UChar32 SpoofData::codePointAt(int32_t index) const {
824 return ConfusableDataUtils::keyToCodePoint(fCFUKeys[index]);
825 }
826
appendValueTo(int32_t index,UnicodeString & dest) const827 int32_t SpoofData::appendValueTo(int32_t index, UnicodeString& dest) const {
828 int32_t stringLength = ConfusableDataUtils::keyToLength(fCFUKeys[index]);
829
830 // Value is either a char (for strings of length 1) or
831 // an index into the string table (for longer strings)
832 uint16_t value = fCFUValues[index];
833 if (stringLength == 1) {
834 dest.append((UChar)value);
835 } else {
836 dest.append(fCFUStrings + value, stringLength);
837 }
838
839 return stringLength;
840 }
841
842
843 U_NAMESPACE_END
844
845 U_NAMESPACE_USE
846
847 //-----------------------------------------------------------------------------
848 //
849 // uspoof_swap - byte swap and char encoding swap of spoof data
850 //
851 //-----------------------------------------------------------------------------
852 U_CAPI int32_t U_EXPORT2
uspoof_swap(const UDataSwapper * ds,const void * inData,int32_t length,void * outData,UErrorCode * status)853 uspoof_swap(const UDataSwapper *ds, const void *inData, int32_t length, void *outData,
854 UErrorCode *status) {
855
856 if (status == NULL || U_FAILURE(*status)) {
857 return 0;
858 }
859 if(ds==NULL || inData==NULL || length<-1 || (length>0 && outData==NULL)) {
860 *status=U_ILLEGAL_ARGUMENT_ERROR;
861 return 0;
862 }
863
864 //
865 // Check that the data header is for spoof data.
866 // (Header contents are defined in gencfu.cpp)
867 //
868 const UDataInfo *pInfo = (const UDataInfo *)((const char *)inData+4);
869 if(!( pInfo->dataFormat[0]==0x43 && /* dataFormat="Cfu " */
870 pInfo->dataFormat[1]==0x66 &&
871 pInfo->dataFormat[2]==0x75 &&
872 pInfo->dataFormat[3]==0x20 &&
873 pInfo->formatVersion[0]==USPOOF_CONFUSABLE_DATA_FORMAT_VERSION &&
874 pInfo->formatVersion[1]==0 &&
875 pInfo->formatVersion[2]==0 &&
876 pInfo->formatVersion[3]==0 )) {
877 udata_printError(ds, "uspoof_swap(): data format %02x.%02x.%02x.%02x "
878 "(format version %02x %02x %02x %02x) is not recognized\n",
879 pInfo->dataFormat[0], pInfo->dataFormat[1],
880 pInfo->dataFormat[2], pInfo->dataFormat[3],
881 pInfo->formatVersion[0], pInfo->formatVersion[1],
882 pInfo->formatVersion[2], pInfo->formatVersion[3]);
883 *status=U_UNSUPPORTED_ERROR;
884 return 0;
885 }
886
887 //
888 // Swap the data header. (This is the generic ICU Data Header, not the uspoof Specific
889 // header). This swap also conveniently gets us
890 // the size of the ICU d.h., which lets us locate the start
891 // of the uspoof specific data.
892 //
893 int32_t headerSize=udata_swapDataHeader(ds, inData, length, outData, status);
894
895
896 //
897 // Get the Spoof Data Header, and check that it appears to be OK.
898 //
899 //
900 const uint8_t *inBytes =(const uint8_t *)inData+headerSize;
901 SpoofDataHeader *spoofDH = (SpoofDataHeader *)inBytes;
902 if (ds->readUInt32(spoofDH->fMagic) != USPOOF_MAGIC ||
903 ds->readUInt32(spoofDH->fLength) < sizeof(SpoofDataHeader))
904 {
905 udata_printError(ds, "uspoof_swap(): Spoof Data header is invalid.\n");
906 *status=U_UNSUPPORTED_ERROR;
907 return 0;
908 }
909
910 //
911 // Prefight operation? Just return the size
912 //
913 int32_t spoofDataLength = ds->readUInt32(spoofDH->fLength);
914 int32_t totalSize = headerSize + spoofDataLength;
915 if (length < 0) {
916 return totalSize;
917 }
918
919 //
920 // Check that length passed in is consistent with length from Spoof data header.
921 //
922 if (length < totalSize) {
923 udata_printError(ds, "uspoof_swap(): too few bytes (%d after ICU Data header) for spoof data.\n",
924 spoofDataLength);
925 *status=U_INDEX_OUTOFBOUNDS_ERROR;
926 return 0;
927 }
928
929
930 //
931 // Swap the Data. Do the data itself first, then the Spoof Data Header, because
932 // we need to reference the header to locate the data, and an
933 // inplace swap of the header leaves it unusable.
934 //
935 uint8_t *outBytes = (uint8_t *)outData + headerSize;
936 SpoofDataHeader *outputDH = (SpoofDataHeader *)outBytes;
937
938 int32_t sectionStart;
939 int32_t sectionLength;
940
941 //
942 // If not swapping in place, zero out the output buffer before starting.
943 // Gaps may exist between the individual sections, and these must be zeroed in
944 // the output buffer. The simplest way to do that is to just zero the whole thing.
945 //
946 if (inBytes != outBytes) {
947 uprv_memset(outBytes, 0, spoofDataLength);
948 }
949
950 // Confusables Keys Section (fCFUKeys)
951 sectionStart = ds->readUInt32(spoofDH->fCFUKeys);
952 sectionLength = ds->readUInt32(spoofDH->fCFUKeysSize) * 4;
953 ds->swapArray32(ds, inBytes+sectionStart, sectionLength, outBytes+sectionStart, status);
954
955 // String Index Section
956 sectionStart = ds->readUInt32(spoofDH->fCFUStringIndex);
957 sectionLength = ds->readUInt32(spoofDH->fCFUStringIndexSize) * 2;
958 ds->swapArray16(ds, inBytes+sectionStart, sectionLength, outBytes+sectionStart, status);
959
960 // String Table Section
961 sectionStart = ds->readUInt32(spoofDH->fCFUStringTable);
962 sectionLength = ds->readUInt32(spoofDH->fCFUStringTableLen) * 2;
963 ds->swapArray16(ds, inBytes+sectionStart, sectionLength, outBytes+sectionStart, status);
964
965 // And, last, swap the header itself.
966 // int32_t fMagic // swap this
967 // uint8_t fFormatVersion[4] // Do not swap this, just copy
968 // int32_t fLength and all the rest // Swap the rest, all is 32 bit stuff.
969 //
970 uint32_t magic = ds->readUInt32(spoofDH->fMagic);
971 ds->writeUInt32((uint32_t *)&outputDH->fMagic, magic);
972
973 if (outputDH->fFormatVersion != spoofDH->fFormatVersion) {
974 uprv_memcpy(outputDH->fFormatVersion, spoofDH->fFormatVersion, sizeof(spoofDH->fFormatVersion));
975 }
976 // swap starting at fLength
977 ds->swapArray32(ds, &spoofDH->fLength, sizeof(SpoofDataHeader)-8 /* minus magic and fFormatVersion[4] */, &outputDH->fLength, status);
978
979 return totalSize;
980 }
981
982 #endif
983
984
985