From 816aa308b6a808839760e69679f38498cdd0f4df Mon Sep 17 00:00:00 2001 From: Alex Henrie Date: Mon, 27 Jan 2020 23:08:43 -0700 Subject: [PATCH 1/5] ntdll: Implement RtlIpv6StringToAddress(Ex)[AW] Signed-off-by: Alex Henrie --- dlls/ntdll/ntdll.spec | 6 +- dlls/ntdll/rtl.c | 245 +++++++++++++++++++++++++++++++++++++----- 2 files changed, 222 insertions(+), 29 deletions(-) diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index fe36235bda..bcd9950a76 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -778,10 +778,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..12c0ed430d 100644 --- a/dlls/ntdll/rtl.c +++ b/dlls/ntdll/rtl.c @@ -98,6 +98,21 @@ 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 */ +}; + +static BOOL is_hex(WCHAR c) +{ + return c < ARRAY_SIZE(hex_table) && hex_table[c] != -1; +} + /* * resource functions */ @@ -884,18 +899,9 @@ void WINAPI RtlCopyLuidAndAttributesArray( for (i = 0; i < Count; i++) Dest[i] = Src[i]; } -static BOOL parse_ipv4_component(const WCHAR **str, BOOL strict, ULONG *value) +static BOOL parse_ip_component(const WCHAR **str, int base, 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; + int d; WCHAR c; ULONG cur_value, prev_value = 0; BOOL success = FALSE; @@ -906,19 +912,23 @@ static BOOL parse_ipv4_component(const WCHAR **str, BOOL strict, ULONG *value) return FALSE; } - if ((*str)[0] == '0') + if (!base) { - if ((*str)[1] == 'x' || (*str)[1] == 'X') + base = 10; + if ((*str)[0] == '0') { - *str += 2; - if (strict) return FALSE; - base = 16; - } - else if ((*str)[1] >= '0' && (*str)[1] <= '9') - { - *str += 1; - if (strict) return FALSE; - base = 8; + if ((*str)[1] == 'x' || (*str)[1] == 'X') + { + *str += 2; + if (strict) return FALSE; + base = 16; + } + else if ((*str)[1] >= '0' && (*str)[1] <= '9') + { + *str += 1; + if (strict) return FALSE; + base = 8; + } } } @@ -946,7 +956,7 @@ static NTSTATUS ipv4_string_to_address(const WCHAR *str, BOOL strict, for (;;) { - if (!parse_ipv4_component(&str, strict, &fields[n])) + if (!parse_ip_component(&str, 0, strict, &fields[n])) goto error; n++; if (*str != '.') @@ -1000,7 +1010,7 @@ static NTSTATUS ipv4_string_to_address(const WCHAR *str, BOOL strict, if (*str == ':') { str++; - if (!parse_ipv4_component(&str, FALSE, &fields[0])) + if (!parse_ip_component(&str, 0, FALSE, &fields[0])) goto error; if (!fields[0] || fields[0] > 0xFFFF || *str) goto error; @@ -1074,13 +1084,196 @@ NTSTATUS WINAPI RtlIpv4StringToAddressA(const char *str, BOOLEAN strict, const c return ret; } +static NTSTATUS ipv6_string_to_address(const WCHAR *str, BOOL ex, + const WCHAR **terminator, IN6_ADDR *address, ULONG *scope, USHORT *port) +{ + static const WCHAR hex_digits[] = { + '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f','A','B','C','D','E','F',0 + }; + BOOL expecting_port = FALSE, ipv4_in_hex = FALSE; + int n_bytes = 0, n_ipv4_bytes = 0, gap = -1; + ULONG ip_component, scope_component = 0, port_component = 0; + const WCHAR *component_terminator; + + size_t component_len; + WCHAR delimiter; + + if (str[0] == '[') + { + if (!ex) goto error; + expecting_port = TRUE; + str++; + } + + if (str[0] == ':') + { + if (str[1] != ':') goto error; + /* Windows bug: a double colon at the beginning is treated as 4 bytes of zeros instead of 2 */ + address->u.Word[0] = 0; + address->u.Word[1] = 0; + n_bytes += 4; + str++; + } + + for (;;) + { + component_len = strspnW(str, hex_digits); + delimiter = str[component_len]; + if (terminator && delimiter != ':' && delimiter != '.') *terminator = str + component_len; + if (!n_ipv4_bytes && str[0] == '0' && (str[1] == 'x' || str[1] == 'X') && is_hex(str[2])) + { + /* IPv4 address as single hex value */ + if (n_bytes > 12) goto error; + component_terminator = str + 2; + if (!parse_ip_component(&component_terminator, 16, TRUE, &ip_component)) goto error; + if (terminator) *terminator = str + 1; /* Windows bug: terminator is off by 1 */ + if (gap == -1 && *component_terminator == ':') return STATUS_INVALID_PARAMETER; + address->u.Byte[n_bytes++] = (ip_component & 0xFF000000) >> 24; + address->u.Byte[n_bytes++] = (ip_component & 0x00FF0000) >> 16; + address->u.Byte[n_bytes++] = (ip_component & 0x0000FF00) >> 8; + address->u.Byte[n_bytes++] = (ip_component & 0x000000FF); + str = component_terminator; + ipv4_in_hex = TRUE; + goto fill_gap; + } + if (!n_ipv4_bytes && delimiter == '.' && n_bytes <= 12) n_ipv4_bytes = 1; + if (n_ipv4_bytes) + { + /* dotted IPv4 address */ + if (!parse_ip_component(&str, 0, TRUE, &ip_component)) goto error; + if (ip_component > 255) return STATUS_INVALID_PARAMETER; + if (delimiter != '.' && (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) break; + n_ipv4_bytes++; + } + else + { + /* IPv6 */ + if (*str == ':') + { + gap = n_bytes; + if (!is_hex(str[1])) + { + str++; + break; + } + } + else + { + if (!parse_ip_component(&str, 16, TRUE, &ip_component)) goto error; + if (component_len > 4) return STATUS_INVALID_PARAMETER; + if (delimiter != ':' && n_bytes < 14 && gap == -1) goto error; + address->u.Word[n_bytes/2] = htons(ip_component); + n_bytes += 2; + } + } + if (n_bytes == 16 || *str != delimiter || (delimiter != ':' && delimiter != '.')) break; + if (gap != -1 && str[0] == ':' && str[1] == ':') break; + str++; + } + + if (terminator) *terminator = str; + +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 (ipv4_in_hex) goto error; + + if (*str == '%') + { + str++; + if (!parse_ip_component(&str, 10, TRUE, &scope_component)) goto error; + } + + if (expecting_port) + { + if (*str != ']') goto error; + str++; + if (*str == ':') + { + str++; + if (!parse_ip_component(&str, 0, 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; } /*********************************************************************** -- 2.24.1