https://git.reactos.org/?p=reactos.git;a=commitdiff;h=56229b7a06fe24cc084b9…
commit 56229b7a06fe24cc084b939ccdac0415ebe3493c
Author: Alex Henrie <alexhenrie24(a)gmail.com>
AuthorDate: Sat Apr 20 22:20:37 2024 +0300
Commit: Hermès BÉLUSCA - MAÏTO <hermes.belusca-maito(a)reactos.org>
CommitDate: Sat Nov 2 21:57:28 2024 +0100
[RTL] ntdll: Implement RtlIpv6StringToAddress(Ex)[AW]
Signed-off-by: Alex Henrie <alexhenrie24(a)gmail.com>
Signed-off-by: Alexandre Julliard <julliard(a)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 */