Signed-off-by: Alex Henrie alexhenrie24@gmail.com --- dlls/ntdll/ntdll.spec | 6 +- dlls/ntdll/rtl.c | 219 +++++++++++++++++++++++++++++++++++++++--- 2 files changed, 211 insertions(+), 14 deletions(-)
diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index 0ea72e3aef..3aa7d1654c 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -781,10 +781,10 @@ # @ stub RtlIpv6AddressToStringExA # @ stub RtlIpv6AddressToStringExW # @ stub RtlIpv6AddressToStringW -# @ stub RtlIpv6StringToAddressA -# @ stub RtlIpv6StringToAddressExA +@ stdcall RtlIpv6StringToAddressA(str ptr ptr) +@ stdcall RtlIpv6StringToAddressExA(str ptr ptr) @ stdcall RtlIpv6StringToAddressExW(wstr ptr ptr ptr) -# @ stub RtlIpv6StringToAddressW +@ stdcall RtlIpv6StringToAddressW(wstr ptr ptr ptr) @ stdcall RtlIsActivationContextActive(ptr) @ stdcall RtlIsCriticalSectionLocked(ptr) @ stdcall RtlIsCriticalSectionLockedByThread(ptr) diff --git a/dlls/ntdll/rtl.c b/dlls/ntdll/rtl.c index 15ff037fef..11e76ed295 100644 --- a/dlls/ntdll/rtl.c +++ b/dlls/ntdll/rtl.c @@ -98,6 +98,16 @@ static const DWORD CRC_table[256] = 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d };
+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 */ +}; + /* * resource functions */ @@ -886,15 +896,6 @@ void WINAPI RtlCopyLuidAndAttributesArray(
static BOOL parse_ipv4_component(const WCHAR **str, BOOL strict, ULONG *value) { - 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 */ - }; int base = 10, d; WCHAR c; ULONG cur_value, prev_value = 0; @@ -1074,13 +1075,209 @@ NTSTATUS WINAPI RtlIpv4StringToAddressA(const char *str, BOOLEAN strict, const c return ret; }
+static BOOL parse_ipv6_component(const WCHAR **str, int base, ULONG *value) +{ + WCHAR *terminator; + if (**str >= ARRAY_SIZE(hex_table) || hex_table[**str] == -1) return FALSE; + *value = min(strtoulW(*str, &terminator, base), 0x7FFFFFFF); + 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++; + /* Windows bug: a double colon at the beginning is treated as 4 bytes of zeros instead of 2 */ + address->u.Word[0] = 0; + n_bytes = 2; + } + + 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) + { + too_big = TRUE; + } + else + { + if (*str != '.' && (n_ipv4_bytes < 4 || (n_bytes < 15 && gap == -1))) goto error; + address->u.Byte[n_bytes] = ip_component; + n_bytes++; + } + if (n_ipv4_bytes == 4 || *str != '.') break; + n_ipv4_bytes++; + } + else + { + /* 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')) + { + /* 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] = htons(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] = htons(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 (terminator) *terminator = str; + if (too_big) return STATUS_INVALID_PARAMETER; + +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 == ':') + { + str++; + if (!parse_ipv4_component(&str, FALSE, &port_component)) goto error; + if (!port_component || port_component > 0xFFFF || *str) goto error; + port_component = htons(port_component); + } + } + } + + if (!terminator && *str) return STATUS_INVALID_PARAMETER; + + if (scope) *scope = scope_component; + if (port) *port = port_component; + + return STATUS_SUCCESS; + +error: + if (terminator) *terminator = str; + return STATUS_INVALID_PARAMETER; +} + /*********************************************************************** * RtlIpv6StringToAddressExW [NTDLL.@] */ NTSTATUS NTAPI RtlIpv6StringToAddressExW(const WCHAR *str, IN6_ADDR *address, ULONG *scope, USHORT *port) { - FIXME("(%s, %p, %p, %p): stub\n", debugstr_w(str), address, scope, port); - return STATUS_NOT_IMPLEMENTED; + TRACE("(%s, %p, %p, %p)\n", debugstr_w(str), address, scope, port); + return ipv6_string_to_address(str, TRUE, NULL, address, scope, port); +} + +/*********************************************************************** + * RtlIpv6StringToAddressW [NTDLL.@] + */ +NTSTATUS WINAPI RtlIpv6StringToAddressW(const WCHAR *str, const WCHAR **terminator, IN6_ADDR *address) +{ + TRACE("(%s, %p, %p)\n", debugstr_w(str), terminator, address); + return ipv6_string_to_address(str, FALSE, terminator, address, NULL, NULL); +} + +/*********************************************************************** + * RtlIpv6StringToAddressExA [NTDLL.@] + */ +NTSTATUS WINAPI RtlIpv6StringToAddressExA(const char *str, IN6_ADDR *address, ULONG *scope, USHORT *port) +{ + WCHAR wstr[64]; + + TRACE("(%s, %p, %p, %p)\n", debugstr_a(str), address, scope, port); + + if (!str || !address || !scope || !port) + return STATUS_INVALID_PARAMETER; + + RtlMultiByteToUnicodeN(wstr, sizeof(wstr), NULL, str, strlen(str) + 1); + wstr[ARRAY_SIZE(wstr) - 1] = 0; + return ipv6_string_to_address(wstr, TRUE, NULL, address, scope, port); +} + +/*********************************************************************** + * RtlIpv6StringToAddressA [NTDLL.@] + */ +NTSTATUS WINAPI RtlIpv6StringToAddressA(const char *str, const char **terminator, IN6_ADDR *address) +{ + WCHAR wstr[64]; + const WCHAR *wterminator = NULL; + NTSTATUS ret; + + TRACE("(%s, %p, %p)\n", debugstr_a(str), terminator, address); + + RtlMultiByteToUnicodeN(wstr, sizeof(wstr), NULL, str, strlen(str) + 1); + wstr[ARRAY_SIZE(wstr) - 1] = 0; + ret = ipv6_string_to_address(wstr, FALSE, &wterminator, address, NULL, NULL); + if (terminator && wterminator) *terminator = str + (wterminator - wstr); + return ret; }
/***********************************************************************
Signed-off-by: Alex Henrie alexhenrie24@gmail.com --- include/ip2string.h | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/include/ip2string.h b/include/ip2string.h index 6c96c8d76d..0753ee82a9 100644 --- a/include/ip2string.h +++ b/include/ip2string.h @@ -30,6 +30,13 @@ NTSTATUS WINAPI RtlIpv4StringToAddressExA(const char *str, BOOLEAN strict, IN_AD NTSTATUS WINAPI RtlIpv4StringToAddressExW(const WCHAR *str, BOOLEAN strict, IN_ADDR *address, USHORT *port); #define RtlIpv4StringToAddressEx WINELIB_NAME_AW(RtlIpv4StringToAddressEx)
+NTSTATUS WINAPI RtlIpv6StringToAddressA(const char *str, const char **terminator, IN6_ADDR *address); +NTSTATUS WINAPI RtlIpv6StringToAddressW(const WCHAR *str, const WCHAR **terminator, IN6_ADDR *address); +#define RtlIpv6StringToAddress WINELIB_NAME_AW(RtlIpv6StringToAddress) +NTSTATUS WINAPI RtlIpv6StringToAddressExA(const char *str, IN6_ADDR *address, ULONG *scope, USHORT *port); +NTSTATUS WINAPI RtlIpv6StringToAddressExW(const WCHAR *str, IN6_ADDR *address, ULONG *scope, USHORT *port); +#define RtlIpv6StringToAddressEx WINELIB_NAME_AW(RtlIpv6StringToAddressEx) + #ifdef __cplusplus } #endif
Signed-off-by: Alex Henrie alexhenrie24@gmail.com --- dlls/iphlpapi/iphlpapi_main.c | 38 ++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-)
diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c index 9ee85caeb3..cb4ffce408 100644 --- a/dlls/iphlpapi/iphlpapi_main.c +++ b/dlls/iphlpapi/iphlpapi_main.c @@ -3357,6 +3357,8 @@ DWORD WINAPI ParseNetworkString(const WCHAR *str, DWORD type, NET_ADDRESS_INFO *info, USHORT *port, BYTE *prefix_len) { IN_ADDR temp_addr4; + IN6_ADDR temp_addr6; + ULONG temp_scope; USHORT temp_port = 0; NTSTATUS status;
@@ -3397,10 +3399,44 @@ DWORD WINAPI ParseNetworkString(const WCHAR *str, DWORD type, return ERROR_SUCCESS; } } + if (type & NET_STRING_IPV6_ADDRESS) + { + status = RtlIpv6StringToAddressExW(str, &temp_addr6, &temp_scope, &temp_port); + if (SUCCEEDED(status) && !temp_port) + { + if (info) + { + info->Format = NET_ADDRESS_IPV6; + info->u.Ipv6Address.sin6_addr = temp_addr6; + info->u.Ipv6Address.sin6_scope_id = temp_scope; + info->u.Ipv6Address.sin6_port = 0; + } + if (port) *port = 0; + if (prefix_len) *prefix_len = 255; + return ERROR_SUCCESS; + } + } + if (type & NET_STRING_IPV6_SERVICE) + { + status = RtlIpv6StringToAddressExW(str, &temp_addr6, &temp_scope, &temp_port); + if (SUCCEEDED(status) && temp_port) + { + if (info) + { + info->Format = NET_ADDRESS_IPV6; + info->u.Ipv6Address.sin6_addr = temp_addr6; + info->u.Ipv6Address.sin6_scope_id = temp_scope; + info->u.Ipv6Address.sin6_port = temp_port; + } + if (port) *port = ntohs(temp_port); + if (prefix_len) *prefix_len = 255; + return ERROR_SUCCESS; + } + }
if (info) info->Format = NET_ADDRESS_FORMAT_UNSPECIFIED;
- if (type & ~(NET_STRING_IPV4_ADDRESS|NET_STRING_IPV4_SERVICE)) + if (type & ~(NET_STRING_IPV4_ADDRESS|NET_STRING_IPV4_SERVICE|NET_STRING_IPV6_ADDRESS|NET_STRING_IPV6_SERVICE)) { FIXME("Unimplemented type 0x%x\n", type); return ERROR_NOT_SUPPORTED;
On 3/19/20 6:06 AM, Alex Henrie wrote:
- if (type & NET_STRING_IPV6_ADDRESS)
- {
status = RtlIpv6StringToAddressExW(str, &temp_addr6, &temp_scope, &temp_port);
if (SUCCEEDED(status) && !temp_port)
{
NTSTATUS is usually checked for 0 aka STATUS_SUCCESS.
On Wed, Mar 18, 2020 at 11:06 PM Nikolay Sivov nsivov@codeweavers.com wrote:
On 3/19/20 6:06 AM, Alex Henrie wrote:
- if (type & NET_STRING_IPV6_ADDRESS)
- {
status = RtlIpv6StringToAddressExW(str, &temp_addr6, &temp_scope, &temp_port);
if (SUCCEEDED(status) && !temp_port)
{
NTSTATUS is usually checked for 0 aka STATUS_SUCCESS.
Hmm, ParseNetworkString already has code that uses the SUCCEEDED macro instead of checking specifically for STATUS_SUCCESS. If Alexandre rejects the patch over it, I'll resend and correct all four uses of SUCCEEDED at the same time.
-Alex