ntdll: Reimplement IdnToAscii() using the normalization table and the ntdll helpers.

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
feature/deterministic
Alexandre Julliard 2020-03-02 16:28:41 +01:00
parent cd13557f48
commit 01237d0896
5 changed files with 161 additions and 136 deletions

View File

@ -568,141 +568,12 @@ static inline INT adapt(INT delta, INT numpoints, BOOL firsttime)
/******************************************************************************
* IdnToAscii (KERNEL32.@)
* Implementation of Punycode based on RFC 3492.
*/
INT WINAPI IdnToAscii(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
LPWSTR lpASCIICharStr, INT cchASCIIChar)
INT WINAPI IdnToAscii( DWORD flags, const WCHAR *src, INT srclen, WCHAR *dst, INT dstlen )
{
static const WCHAR prefixW[] = {'x','n','-','-'};
WCHAR *norm_str;
INT i, label_start, label_end, norm_len, out_label, out = 0;
TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
lpASCIICharStr, cchASCIIChar);
norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr, cchUnicodeChar, NULL, 0);
if(!norm_len)
return 0;
norm_str = HeapAlloc(GetProcessHeap(), 0, norm_len*sizeof(WCHAR));
if(!norm_str) {
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return 0;
}
norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr,
cchUnicodeChar, norm_str, norm_len);
if(!norm_len) {
HeapFree(GetProcessHeap(), 0, norm_str);
return 0;
}
for(label_start=0; label_start<norm_len;) {
INT n = INIT_N, bias = INIT_BIAS;
INT delta = 0, b = 0, h;
out_label = out;
for(i=label_start; i<norm_len && norm_str[i]!='.' &&
norm_str[i]!=0x3002 && norm_str[i]!='\0'; i++)
if(norm_str[i] < 0x80)
b++;
label_end = i;
if(b == label_end-label_start) {
if(label_end < norm_len)
b++;
if(!lpASCIICharStr) {
out += b;
}else if(out+b <= cchASCIIChar) {
memcpy(lpASCIICharStr+out, norm_str+label_start, b*sizeof(WCHAR));
out += b;
}else {
HeapFree(GetProcessHeap(), 0, norm_str);
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return 0;
}
label_start = label_end+1;
continue;
}
if(!lpASCIICharStr) {
out += 5+b; /* strlen(xn--...-) */
}else if(out+5+b <= cchASCIIChar) {
memcpy(lpASCIICharStr+out, prefixW, sizeof(prefixW));
out += 4;
for(i=label_start; i<label_end; i++)
if(norm_str[i] < 0x80)
lpASCIICharStr[out++] = norm_str[i];
lpASCIICharStr[out++] = '-';
}else {
HeapFree(GetProcessHeap(), 0, norm_str);
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return 0;
}
if(!b)
out--;
for(h=b; h<label_end-label_start;) {
INT m = 0xffff, q, k;
for(i=label_start; i<label_end; i++) {
if(norm_str[i]>=n && m>norm_str[i])
m = norm_str[i];
}
delta += (m-n)*(h+1);
n = m;
for(i=label_start; i<label_end; i++) {
if(norm_str[i] < n) {
delta++;
}else if(norm_str[i] == n) {
for(q=delta, k=BASE; ; k+=BASE) {
INT t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
INT disp = q<t ? q : t+(q-t)%(BASE-t);
if(!lpASCIICharStr) {
out++;
}else if(out+1 <= cchASCIIChar) {
lpASCIICharStr[out++] = disp<='z'-'a' ?
'a'+disp : '0'+disp-'z'+'a'-1;
}else {
HeapFree(GetProcessHeap(), 0, norm_str);
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return 0;
}
if(q < t)
break;
q = (q-t)/(BASE-t);
}
bias = adapt(delta, h+1, h==b);
delta = 0;
h++;
}
}
delta++;
n++;
}
if(out-out_label > 63) {
HeapFree(GetProcessHeap(), 0, norm_str);
SetLastError(ERROR_INVALID_NAME);
return 0;
}
if(label_end < norm_len) {
if(!lpASCIICharStr) {
out++;
}else if(out+1 <= cchASCIIChar) {
lpASCIICharStr[out++] = norm_str[label_end] ? '.' : 0;
}else {
HeapFree(GetProcessHeap(), 0, norm_str);
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return 0;
}
}
label_start = label_end+1;
}
HeapFree(GetProcessHeap(), 0, norm_str);
return out;
NTSTATUS status = RtlIdnToAscii( flags, src, srclen, dst, &dstlen );
if (!set_ntstatus( status )) return 0;
return dstlen;
}
/******************************************************************************

View File

@ -4572,10 +4572,8 @@ static void test_IdnToAscii(void)
SetLastError(0xdeadbeef);
ret = pIdnToAscii(test_data[i].flags, test_data[i].in, test_data[i].in_len, buf, ARRAY_SIZE(buf));
err = GetLastError();
todo_wine_if (i == 10)
ok(ret == test_data[i].ret || broken(ret == test_data[i].broken_ret), "%d: ret = %d\n", i, ret);
ok(err == ret ? 0xdeadbeef : ERROR_INVALID_NAME, "%d: err = %d\n", i, err);
todo_wine_if (i == 10)
ok(!wcsnicmp(test_data[i].out, buf, ret), "%d: buf = %s\n", i, wine_dbgstr_wn(buf, ret));
}
}
@ -4691,10 +4689,16 @@ static void test_Idn(void)
ret = pIdnToAscii( 0, columns[0], -1, dst, ARRAY_SIZE(dst) );
if (!is_idn_error( error ))
{
ok( ret, "line %u: toAscii failed for %s\n", line, debugstr_w(columns[0]) );
ok( ret, "line %u: toAscii failed for %s expected %s\n", line,
debugstr_w(columns[0]), debugstr_w(expect) );
if (ret) ok( !wcscmp( dst, expect ), "line %u: got %s expected %s\n",
line, debugstr_w(dst), debugstr_w(expect) );
}
else
{
ok( !ret, "line %u: toAscii didn't fail for %s got %s expected error %s\n",
line, debugstr_w(columns[0]), debugstr_w(dst), debugstr_w(error) );
}
expect = columns[1];
if (!*expect) expect = columns[0];

View File

@ -2018,6 +2018,154 @@ NTSTATUS WINAPI RtlNormalizeString( ULONG form, const WCHAR *src, INT src_len, W
}
/* Punycode parameters */
enum { BASE = 36, TMIN = 1, TMAX = 26, SKEW = 38, DAMP = 700 };
static BOOL check_invalid_chars( const struct norm_table *info, DWORD flags,
const unsigned int *buffer, int len )
{
int i;
for (i = 0; i < len; i++)
{
switch (buffer[i])
{
case 0x200c: /* zero-width non-joiner */
case 0x200d: /* zero-width joiner */
if (!i || get_combining_class( info, buffer[i - 1] ) != 9) return TRUE;
break;
case 0x2260: /* not equal to */
case 0x226e: /* not less than */
case 0x226f: /* not greater than */
if (flags & IDN_USE_STD3_ASCII_RULES) return TRUE;
break;
}
switch (get_char_props( info, buffer[i] ))
{
case 0xbf:
return TRUE;
case 0xff:
if (buffer[i] >= HANGUL_SBASE && buffer[i] < HANGUL_SBASE + 0x2c00) break;
return TRUE;
case 0x7f:
if (!(flags & IDN_ALLOW_UNASSIGNED)) return TRUE;
break;
}
}
if ((flags & IDN_USE_STD3_ASCII_RULES) && len && (buffer[0] == '-' || buffer[len - 1] == '-'))
return TRUE;
return FALSE;
}
/******************************************************************************
* RtlIdnToAscii (NTDLL.@)
*/
NTSTATUS WINAPI RtlIdnToAscii( DWORD flags, const WCHAR *src, INT srclen, WCHAR *dst, INT *dstlen )
{
static const WCHAR prefixW[] = {'x','n','-','-'};
const struct norm_table *info;
NTSTATUS status;
WCHAR normstr[256], res[256];
unsigned int ch, buffer[64];
int i, len, start, end, out_label, out = 0, normlen = ARRAY_SIZE(normstr);
TRACE( "%x %s %p %d\n", flags, debugstr_wn(src, srclen), dst, *dstlen );
if ((status = load_norm_table( 13, &info ))) return status;
if ((status = RtlIdnToNameprepUnicode( flags, src, srclen, normstr, &normlen ))) return status;
/* implementation of Punycode based on RFC 3492 */
for (start = 0; start < normlen; start = end + 1)
{
int n = 0x80, bias = 72, delta = 0, b = 0, h, buflen = 0;
out_label = out;
for (i = start; i < normlen; i += len)
{
if (!(len = get_utf16( normstr + i, normlen - i, &ch ))) break;
if (!ch || ch == '.') break;
if (ch < 0x80) b++;
buffer[buflen++] = ch;
}
end = i;
if (b == end - start)
{
if (end < normlen) b++;
if (out + b > ARRAY_SIZE(res)) return STATUS_INVALID_IDN_NORMALIZATION;
memcpy( res + out, normstr + start, b * sizeof(WCHAR) );
out += b;
continue;
}
if (buflen >= 4 && buffer[2] == '-' && buffer[3] == '-') return STATUS_INVALID_IDN_NORMALIZATION;
if (check_invalid_chars( info, flags, buffer, buflen )) return STATUS_INVALID_IDN_NORMALIZATION;
if (out + 5 + b > ARRAY_SIZE(res)) return STATUS_INVALID_IDN_NORMALIZATION;
memcpy( res + out, prefixW, sizeof(prefixW) );
out += ARRAY_SIZE(prefixW);
if (b)
{
for (i = start; i < end; i++) if (normstr[i] < 0x80) res[out++] = normstr[i];
res[out++] = '-';
}
for (h = b; h < buflen; delta++, n++)
{
int m = 0x10ffff, q, k;
for (i = 0; i < buflen; i++) if (buffer[i] >= n && m > buffer[i]) m = buffer[i];
delta += (m - n) * (h + 1);
n = m;
for (i = 0; i < buflen; i++)
{
if (buffer[i] == n)
{
for (q = delta, k = BASE; ; k += BASE)
{
int t = k <= bias ? TMIN : k >= bias + TMAX ? TMAX : k - bias;
int disp = q < t ? q : t + (q - t) % (BASE - t);
if (out + 1 > ARRAY_SIZE(res)) return STATUS_INVALID_IDN_NORMALIZATION;
res[out++] = disp <= 25 ? 'a' + disp : '0' + disp - 26;
if (q < t) break;
q = (q - t) / (BASE - t);
}
delta /= (h == b ? DAMP : 2);
delta += delta / (h + 1);
for (k = 0; delta > ((BASE - TMIN) * TMAX) / 2; k += BASE) delta /= BASE - TMIN;
bias = k + ((BASE - TMIN + 1) * delta) / (delta + SKEW);
delta = 0;
h++;
}
else if (buffer[i] < n) delta++;
}
}
if (out - out_label > 63) return STATUS_INVALID_IDN_NORMALIZATION;
if (end < normlen)
{
if (out + 1 > ARRAY_SIZE(res)) return STATUS_INVALID_IDN_NORMALIZATION;
res[out++] = normstr[end];
}
}
if (*dstlen)
{
if (out <= *dstlen) memcpy( dst, res, out * sizeof(WCHAR) );
else status = STATUS_BUFFER_TOO_SMALL;
}
*dstlen = out;
return status;
}
/******************************************************************************
* RtlIdnToNameprepUnicode (NTDLL.@)
*/

View File

@ -722,6 +722,7 @@
@ stub RtlGuidToPropertySetName
@ stdcall RtlHashUnicodeString(ptr long long ptr)
@ stdcall RtlIdentifierAuthoritySid(ptr)
@ stdcall RtlIdnToAscii(long wstr long ptr ptr)
@ stdcall RtlIdnToNameprepUnicode(long wstr long ptr ptr)
@ stdcall RtlImageDirectoryEntryToData(long long long ptr)
@ stdcall RtlImageNtHeader(long)

View File

@ -2797,6 +2797,7 @@ NTSYSAPI DWORD WINAPI RtlGetThreadErrorMode(void);
NTSYSAPI NTSTATUS WINAPI RtlGetVersion(RTL_OSVERSIONINFOEXW*);
NTSYSAPI NTSTATUS WINAPI RtlGUIDFromString(PUNICODE_STRING,GUID*);
NTSYSAPI PSID_IDENTIFIER_AUTHORITY WINAPI RtlIdentifierAuthoritySid(PSID);
NTSYSAPI NTSTATUS WINAPI RtlIdnToAscii(DWORD,const WCHAR*,INT,WCHAR*,INT*);
NTSYSAPI NTSTATUS WINAPI RtlIdnToNameprepUnicode(DWORD,const WCHAR*,INT,WCHAR*,INT*);
NTSYSAPI PVOID WINAPI RtlImageDirectoryEntryToData(HMODULE,BOOL,WORD,ULONG *);
NTSYSAPI PIMAGE_NT_HEADERS WINAPI RtlImageNtHeader(HMODULE);