https://git.reactos.org/?p=reactos.git;a=commitdiff;h=56229b7a06fe24cc084b939ccdac0415ebe3493c
commit 56229b7a06fe24cc084b939ccdac0415ebe3493c Author: Alex Henrie <alexhenri...@gmail.com> AuthorDate: Sat Apr 20 22:20:37 2024 +0300 Commit: Hermès BÉLUSCA - MAÏTO <hermes.belusca-ma...@reactos.org> CommitDate: Sat Nov 2 21:57:28 2024 +0100 [RTL] ntdll: Implement RtlIpv6StringToAddress(Ex)[AW] Signed-off-by: Alex Henrie <alexhenri...@gmail.com> Signed-off-by: Alexandre Julliard <julli...@winehq.org> wine commit id 474d1f0b2daa8583fa82fe203b207807ba29499d --- sdk/lib/rtl/network.c | 410 ++++++++++++++++++++++++++------------------------ 1 file changed, 213 insertions(+), 197 deletions(-) diff --git a/sdk/lib/rtl/network.c b/sdk/lib/rtl/network.c index 44151e42b5e..acf3d9b26bf 100644 --- a/sdk/lib/rtl/network.c +++ b/sdk/lib/rtl/network.c @@ -110,26 +110,6 @@ RtlpStringToUlong( return RtlpStringToUlongBase(String, Base, Terminator, Out); } -/* Tell us what possible base the string could be in, 10 or 16 by looking at the characters. - Invalid characters break the operation */ -static -ULONG -RtlpClassifyChars(PCWSTR S, PULONG Base) -{ - ULONG Len = 0; - *Base = 0; - for (Len = 0; S[Len]; ++Len) - { - if (iswascii(S[Len]) && isdigit(S[Len])) - *Base = max(*Base, 10); - else if (iswascii(S[Len]) && isxdigit(S[Len])) - *Base = 16; - else - break; - } - return Len; -} - /* Worker function to extract the ipv4 part of a string. */ NTSTATUS NTAPI @@ -798,152 +778,239 @@ RtlIpv6StringToAddressExA( return Status; } -/* -* @implemented -*/ -NTSTATUS -NTAPI -RtlIpv6StringToAddressW( - _In_ PCWSTR String, - _Out_ PCWSTR *Terminator, - _Out_ struct in6_addr *Addr) +/* + * RtlIpv6StringToAddress[Ex]W implementation was imported from Wine 9.6 at 20/4/2024 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * For full text of the license see COPYING.LIB in the root of this project. + * + * Copyright 2020 Alex Henrie + */ + +static const int hex_table[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00-0x0F */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10-0x1F */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x20-0x2F */ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, /* 0x30-0x3F */ + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x40-0x4F */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x50-0x5F */ + -1, 10, 11, 12, 13, 14, 15 /* 0x60-0x66 */ +}; + +static BOOL parse_ipv4_component(const WCHAR **str, BOOL strict, ULONG *value) { - INT n, j; - INT StartSkip = -1, Parts = 0; - ULONG Base, Len; - NTSTATUS Status = STATUS_SUCCESS; - enum { None, Number, Colon, DoubleColon } Last = None; - BOOLEAN SkipoutLastHex = FALSE; + int base = 10, d; + WCHAR c; + ULONG cur_value, prev_value = 0; + BOOL success = FALSE; - if (!String || !Terminator || !Addr) - return STATUS_INVALID_PARAMETER; + if (**str == '.') + { + *str += 1; + return FALSE; + } - for (n = 0; n < 8;) + if ((*str)[0] == '0') { - Len = RtlpClassifyChars(String, &Base); - if (Len == 0) + if ((*str)[1] == 'x' || (*str)[1] == 'X') + { + *str += 2; + if (strict) return FALSE; + base = 16; + } + else if ((*str)[1] >= '0' && (*str)[1] <= '9') { - /* not a number, and no ':' or already encountered an ':' */ - if (String[0] != ':' || Last == Colon) - break; + *str += 1; + if (strict) return FALSE; + base = 8; + } + } - /* double colon, 1 or more fields set to 0. mark the position, and move on. */ - if (StartSkip == -1 && String[1] == ':') - { - /* this case was observed in windows, but it does not seem correct. */ - if (!n) - { - Addr->s6_words[n++] = 0; - Addr->s6_words[n++] = 0; - } - StartSkip = n; - String += 2; - Last = DoubleColon; - } - else if (String[1] == ':' || Last != Number) + for (cur_value = 0; **str; *str += 1) + { + c = **str; + if (c >= ARRAYSIZE(hex_table)) break; + d = hex_table[c]; + if (d == -1 || d >= base) break; + cur_value = cur_value * base + d; + success = TRUE; + if (cur_value < prev_value) return FALSE; /* overflow */ + prev_value = cur_value; + } + + if (success) *value = cur_value; + return success; +} + +static BOOL parse_ipv6_component(const WCHAR **str, int base, ULONG *value) +{ + WCHAR *terminator; + if (**str >= ARRAYSIZE(hex_table) || hex_table[**str] == -1) return FALSE; + *value = min(wcstoul(*str, &terminator, base), 0x7FFFFFFF); + if (*terminator == '0') terminator++; /* "0x" but nothing valid after */ + else if (terminator == *str) return FALSE; + *str = terminator; + return TRUE; +} + +static NTSTATUS ipv6_string_to_address(const WCHAR *str, BOOL ex, + const WCHAR **terminator, IN6_ADDR *address, ULONG *scope, USHORT *port) +{ + BOOL expecting_port = FALSE, has_0x = FALSE, too_big = FALSE; + int n_bytes = 0, n_ipv4_bytes = 0, gap = -1; + ULONG ip_component, scope_component = 0, port_component = 0; + const WCHAR *prev_str; + + if (str[0] == '[') + { + if (!ex) goto error; + expecting_port = TRUE; + str++; + } + + if (str[0] == ':') + { + if (str[1] != ':') goto error; + str++; + address->u.Word[0] = 0; + } + + for (;;) + { + if (!n_ipv4_bytes && *str == ':') + { + /* double colon */ + if (gap != -1) goto error; + str++; + prev_str = str; + gap = n_bytes; + if (n_bytes == 14 || !parse_ipv6_component(&str, 16, &ip_component)) break; + str = prev_str; + } + else + { + prev_str = str; + } + + if (!n_ipv4_bytes && n_bytes <= (gap != -1 ? 10 : 12)) + { + if (parse_ipv6_component(&str, 10, &ip_component) && *str == '.') + n_ipv4_bytes = 1; + str = prev_str; + } + + if (n_ipv4_bytes) + { + /* IPv4 component */ + if (!parse_ipv6_component(&str, 10, &ip_component)) goto error; + if (str - prev_str > 3 || ip_component > 255) { - /* a double colon after we already encountered one, or a the last parsed item was not a number. */ - break; + too_big = TRUE; } else { - ++String; - Last = Colon; + if (*str != '.' && (n_ipv4_bytes < 4 || (n_bytes < 15 && gap == -1))) goto error; + address->u.Byte[n_bytes] = ip_component; + n_bytes++; } - } - else if (Len > 4) - { - /* it seems that when encountering more than 4 chars, the terminator is not updated, - unless the previously encountered item is a double colon.... */ - Status = STATUS_INVALID_PARAMETER; - if (Last != DoubleColon) - return Status; - String += Len; - break; + if (n_ipv4_bytes == 4 || *str != '.') break; + n_ipv4_bytes++; } else { - ULONG Value; - if (String[Len] == '.' && n <= 6) + /* IPv6 component */ + if (!parse_ipv6_component(&str, 16, &ip_component)) goto error; + if (prev_str[0] == '0' && (prev_str[1] == 'x' || prev_str[1] == 'X')) { - ULONG Values[4]; - INT PartsV4 = 0; - /* this could be an ipv4 address inside an ipv6 address http://tools.ietf.org/html/rfc2765 */ - Last = Number; - Status = RtlpIpv4StringToAddressParserW(String, TRUE, &String, Values, &PartsV4); - for(j = 0; j < PartsV4; ++j) - { - if (Values[j] > 255) - { - Status = STATUS_INVALID_PARAMETER; - if (j != 3) - return Status; - break; - } - if ((j == PartsV4 - 1) && - (j < 3 || - (*String == ':' && StartSkip == -1) || - (StartSkip == -1 && n < 6) || - Status == STATUS_INVALID_PARAMETER)) - { - Status = STATUS_INVALID_PARAMETER; - break; - } - Addr->s6_bytes[n * 2 + j] = Values[j] & 0xff; - } - /* mark enough parts as converted in case we are the last item to be converted */ - n += 2; - /* mark 2 parts as converted in case we are not the last item, and we encountered a double colon. */ - Parts+=2; - break; + /* Windows "feature": the last IPv6 component can start with "0x" and be longer than 4 digits */ + if (terminator) *terminator = prev_str + 1; /* Windows says that the "x" is the terminator */ + if (n_bytes < 14 && gap == -1) return STATUS_INVALID_PARAMETER; + address->u.Word[n_bytes/2] = RtlUshortByteSwap(ip_component); + n_bytes += 2; + has_0x = TRUE; + goto fill_gap; } + if (*str != ':' && n_bytes < 14 && gap == -1) goto error; + if (str - prev_str > 4) + too_big = TRUE; + else + address->u.Word[n_bytes/2] = RtlUshortByteSwap(ip_component); + n_bytes += 2; + if (*str != ':' || (gap != -1 && str[1] == ':')) break; + } + if (n_bytes == (gap != -1 ? 14 : 16)) break; + if (too_big) return STATUS_INVALID_PARAMETER; + str++; + } - if (String[Len] != ':' && n < 7 && StartSkip == -1) - { - /* if we decoded atleast some numbers, update the terminator to point to the first invalid char */ - if (Base) - String += Len; - Status = STATUS_INVALID_PARAMETER; - break; - } + if (terminator) *terminator = str; + if (too_big) return STATUS_INVALID_PARAMETER; - if (Len == 1 && towlower(String[Len]) == 'x' && String[0] == '0') +fill_gap: + if (gap == -1) + { + if (n_bytes < 16) goto error; + } + else + { + memmove(address->u.Byte + 16 - (n_bytes - gap), address->u.Byte + gap, n_bytes - gap); + memset(address->u.Byte + gap, 0, 16 - n_bytes); + } + + if (ex) + { + if (has_0x) goto error; + + if (*str == '%') + { + str++; + if (!parse_ipv4_component(&str, TRUE, &scope_component)) goto error; + } + + if (expecting_port) + { + if (*str != ']') goto error; + str++; + if (*str == ':') { - Len = RtlpClassifyChars(String + 2, &Base); - if (Len > 0 && Len <= 4) - { - *Terminator = String + 1; - String += 2; - SkipoutLastHex = TRUE; - } + str++; + if (!parse_ipv4_component(&str, FALSE, &port_component)) goto error; + if (!port_component || port_component > 0xFFFF || *str) goto error; + port_component = RtlUshortByteSwap(port_component); } - - Status = RtlpStringToUlongBase(String, 16, &String, &Value); - if (!NT_SUCCESS(Status)) - break; - - if (StartSkip != -1) - Parts++; - Addr->s6_words[n++] = WN2H(Value); - Last = Number; - if (SkipoutLastHex) - break; } } - if (StartSkip != -1 && Status != STATUS_INVALID_PARAMETER && Last != Colon) - { - /* we found a '::' somewhere, so expand that. */ - memmove(&Addr->s6_words[8-Parts], &Addr->s6_words[StartSkip], Parts * sizeof(Addr->s6_words[0])); - memset(&Addr->s6_words[StartSkip], 0, (8-StartSkip-Parts) * sizeof(Addr->s6_words[0])); - n = 8; - } + if (!terminator && *str) return STATUS_INVALID_PARAMETER; - /* we have already set the terminator */ - if (SkipoutLastHex) - return n < 8 ? STATUS_INVALID_PARAMETER : Status; - *Terminator = String; - return n < 8 ? STATUS_INVALID_PARAMETER : Status; + if (scope) *scope = scope_component; + if (port) *port = port_component; + + return STATUS_SUCCESS; + +error: + if (terminator) *terminator = str; + return STATUS_INVALID_PARAMETER; +} + +/* +* @implemented +*/ +NTSTATUS +NTAPI +RtlIpv6StringToAddressW( + _In_ PCWSTR String, + _Out_ PCWSTR *Terminator, + _Out_ struct in6_addr *Addr) +{ + if (!String || !Terminator || !Addr) + return STATUS_INVALID_PARAMETER; + + return ipv6_string_to_address(String, FALSE, Terminator, Addr, NULL, NULL); } /* @@ -957,63 +1024,12 @@ RtlIpv6StringToAddressExW( _Out_ PULONG ScopeId, _Out_ PUSHORT Port) { - NTSTATUS Status; - ULONG ConvertedPort = 0, ConvertedScope = 0; - if (!AddressString || !Address || !ScopeId || !Port) - return STATUS_INVALID_PARAMETER; - - if (*AddressString == '[') - { - ConvertedPort = 1; - ++AddressString; - } - Status = RtlIpv6StringToAddressW(AddressString, &AddressString, Address); - if (!NT_SUCCESS(Status)) + if (!AddressString || !Address || !ScopeId || !Port) return STATUS_INVALID_PARAMETER; - if (*AddressString == '%') - { - ++AddressString; - Status = RtlpStringToUlongBase(AddressString, 10, &AddressString, &ConvertedScope); - if (!NT_SUCCESS(Status)) - return STATUS_INVALID_PARAMETER; - } - else if (*AddressString && !(ConvertedPort && *AddressString == ']')) - { - return STATUS_INVALID_PARAMETER; - } - - if (ConvertedPort) - { - if (*AddressString++ !=']') - return STATUS_INVALID_PARAMETER; - if (*AddressString ==':') - { - ULONG Base = 10; - if (*++AddressString == '0') - { - if (towlower(*++AddressString) != 'x') - return STATUS_INVALID_PARAMETER; - ++AddressString; - Base = 16; - } - Status = RtlpStringToUlongBase(AddressString, Base, &AddressString, &ConvertedPort); - if (!NT_SUCCESS(Status) || ConvertedPort > 0xffff) - return STATUS_INVALID_PARAMETER; - } - else - { - ConvertedPort = 0; - } - } - - if (*AddressString == 0) - { - *ScopeId = ConvertedScope; - *Port = WN2H(ConvertedPort); - return STATUS_SUCCESS; - } - return STATUS_INVALID_PARAMETER; + return ipv6_string_to_address(AddressString, TRUE, NULL, Address, ScopeId, Port); } +/* End of Wine implementation */ + /* EOF */