These patches have been in Wine Staging for 3 months and no problems have been reported. At minimum, the implementation of RtlIpv6StringToAddress in ntdll is needed to enable further work to reimplement inet_pton and WSAStringToAddress in ws2_32 without depending on the host system's inet_pton function. That would make those Wine functions behave consistently like their corresponding Windows functions regardless of the particular Unix platform that Wine is running on, and furthermore would remove an obstacle to converting ws2_32 to a PE library.
Alex Henrie (3): ntdll: Implement RtlIpv6StringToAddress(Ex)[AW] include: Add RtlIpv6StringToAddress(Ex)[AW] iphlpapi: Implement ParseNetworkString for IPv6 addresses and services
dlls/iphlpapi/iphlpapi_main.c | 38 ++++- dlls/ntdll/ntdll.spec | 6 +- dlls/ntdll/rtl.c | 220 ++++++++++++++++++++++++++-- dlls/ntoskrnl.exe/ntoskrnl.exe.spec | 6 +- include/ip2string.h | 7 + 5 files changed, 259 insertions(+), 18 deletions(-)
Signed-off-by: Alex Henrie alexhenrie24@gmail.com --- dlls/ntdll/ntdll.spec | 6 +- dlls/ntdll/rtl.c | 220 ++++++++++++++++++++++++++-- dlls/ntoskrnl.exe/ntoskrnl.exe.spec | 6 +- 3 files changed, 215 insertions(+), 17 deletions(-)
diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index 46597317f5..530821d601 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -783,10 +783,10 @@ # @ stub RtlIpv6AddressToStringExA # @ stub RtlIpv6AddressToStringExW # @ stub RtlIpv6AddressToStringW -# @ stub RtlIpv6StringToAddressA -# @ stub RtlIpv6StringToAddressExA +@ stdcall RtlIpv6StringToAddressA(str ptr ptr) +@ stdcall RtlIpv6StringToAddressExA(str ptr ptr ptr) @ stdcall RtlIpv6StringToAddressExW(wstr ptr ptr ptr) -# @ stub RtlIpv6StringToAddressW +@ stdcall RtlIpv6StringToAddressW(wstr ptr ptr) @ stdcall RtlIsActivationContextActive(ptr) @ stdcall RtlIsCriticalSectionLocked(ptr) @ stdcall RtlIsCriticalSectionLockedByThread(ptr) diff --git a/dlls/ntdll/rtl.c b/dlls/ntdll/rtl.c index ee33e3d82b..3018c1e88f 100644 --- a/dlls/ntdll/rtl.c +++ b/dlls/ntdll/rtl.c @@ -97,6 +97,15 @@ 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 */ +};
#if defined(_WIN64) && !defined(_MSC_VER) static inline unsigned char _InterlockedCompareExchange128(__int64 *dest, __int64 xchg_high, __int64 xchg_low, __int64 *compare) @@ -904,15 +913,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; @@ -1092,13 +1092,211 @@ 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(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++; + /* 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); + if (!str || !address || !scope || !port) return STATUS_INVALID_PARAMETER; + 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; }
/*********************************************************************** diff --git a/dlls/ntoskrnl.exe/ntoskrnl.exe.spec b/dlls/ntoskrnl.exe/ntoskrnl.exe.spec index 76c4afb281..921741872c 100644 --- a/dlls/ntoskrnl.exe/ntoskrnl.exe.spec +++ b/dlls/ntoskrnl.exe/ntoskrnl.exe.spec @@ -1135,10 +1135,10 @@ @ stub RtlIpv6AddressToStringExA @ stub RtlIpv6AddressToStringExW @ stub RtlIpv6AddressToStringW -@ stub RtlIpv6StringToAddressA -@ stub RtlIpv6StringToAddressExA +@ stdcall RtlIpv6StringToAddressA(str ptr ptr) ntdll.RtlIpv6StringToAddressA +@ stdcall RtlIpv6StringToAddressExA(str ptr ptr ptr) ntdll.RtlIpv6StringToAddressExA @ stdcall RtlIpv6StringToAddressExW(wstr ptr ptr ptr) -@ stub RtlIpv6StringToAddressW +@ stdcall RtlIpv6StringToAddressW(wstr ptr ptr) ntdll.RtlIpv6StringToAddressW @ stub RtlIsGenericTableEmpty @ stub RtlIsGenericTableEmptyAvl @ stdcall RtlIsNameLegalDOS8Dot3(ptr ptr ptr)
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 e65eff82e2..ff9d0d3a53 100644 --- a/dlls/iphlpapi/iphlpapi_main.c +++ b/dlls/iphlpapi/iphlpapi_main.c @@ -3415,6 +3415,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;
@@ -3455,10 +3457,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;