Index: include/ndk/rtlfuncs.h =================================================================== --- include/ndk/rtlfuncs.h (revision 66924) +++ include/ndk/rtlfuncs.h (working copy) @@ -3994,8 +3994,43 @@ // Network Functions // NTSYSAPI +LPSTR +NTAPI +RtlIpv4AddressToStringA( + _In_ const struct in_addr *Addr, + _Out_writes_(16) PCHAR S +); + +NTSYSAPI +LPWSTR +NTAPI +RtlIpv4AddressToStringW( + _In_ const struct in_addr *Addr, + _Out_writes_(16) PWCHAR S +); + +NTSYSAPI NTSTATUS NTAPI +RtlIpv4AddressToStringExA( + _In_ const struct in_addr *Address, + _In_ USHORT Port, + _Out_writes_to_(*AddressStringLength, *AddressStringLength) PCHAR AddressString, + _Inout_ PULONG AddressStringLength +); + +NTSTATUS +NTAPI +RtlIpv4AddressToStringExW( + _In_ const struct in_addr *Address, + _In_ USHORT Port, + _Out_writes_to_(*AddressStringLength, *AddressStringLength) PWCHAR AddressString, + _Inout_ PULONG AddressStringLength +); + +NTSYSAPI +NTSTATUS +NTAPI RtlIpv4StringToAddressA( _In_ PCSTR String, _In_ BOOLEAN Strict, @@ -4034,11 +4069,49 @@ ); NTSYSAPI +LPSTR +NTAPI +RtlIpv6AddressToStringA( + _In_ const struct in6_addr *Addr, + _Out_writes_(46) PSTR S +); + +NTSYSAPI +LPWSTR +NTAPI +RtlIpv6AddressToStringW( + _In_ const struct in6_addr *Addr, + _Out_writes_(46) PWSTR S +); + +NTSYSAPI NTSTATUS NTAPI +RtlIpv6AddressToStringExA( + _In_ const struct in6_addr *Address, + _In_ ULONG ScopeId, + _In_ USHORT Port, + _Out_writes_to_(*AddressStringLength, *AddressStringLength) PSTR AddressString, + _Inout_ PULONG AddressStringLength +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlIpv6AddressToStringExW( + _In_ const struct in6_addr *Address, + _In_ ULONG ScopeId, + _In_ USHORT Port, + _Out_writes_to_(*AddressStringLength, *AddressStringLength) PWCHAR AddressString, + _Inout_ PULONG AddressStringLength +); + +NTSYSAPI +NTSTATUS +NTAPI RtlIpv6StringToAddressA( - _In_ PCHAR Name, - _Out_ PCHAR *Terminator, + _In_ PCSTR Name, + _Out_ PCSTR *Terminator, _Out_ struct in6_addr *Addr ); @@ -4046,8 +4119,8 @@ NTSTATUS NTAPI RtlIpv6StringToAddressW( - _In_ PWCHAR Name, - _Out_ PCHAR *Terminator, + _In_ PCWSTR Name, + _Out_ PCWSTR *Terminator, _Out_ struct in6_addr *Addr ); @@ -4055,7 +4128,7 @@ NTSTATUS NTAPI RtlIpv6StringToAddressExA( - _In_ PCHAR AddressString, + _In_ PCSTR AddressString, _In_ struct in6_addr *Address, _In_ PULONG ScopeId, _In_ PUSHORT Port @@ -4065,7 +4138,7 @@ NTSTATUS NTAPI RtlIpv6StringToAddressExW( - _In_ PWCHAR AddressName, + _In_ PCWSTR AddressName, _In_ struct in6_addr *Address, _In_ PULONG ScopeId, _In_ PUSHORT Port Index: lib/rtl/network.c =================================================================== --- lib/rtl/network.c (revision 66924) +++ lib/rtl/network.c (working copy) @@ -9,7 +9,7 @@ /* INCLUDES *****************************************************************/ #include - +#include #define NDEBUG #include @@ -19,25 +19,85 @@ /* maximum length of an ipv4 port expressed as a string */ #define IPV4_PORT_STRING_MAX_LEN 7 /* with the leading ':' */ +/* maximum length of an ipv6 string for RtlIpv6AddressToString */ +#define RTLIPV6A2S_MAX_LEN 46 + +/* maximum length of an ipv6 string with scope and port for RtlIpv6AddressToStringEx */ +#define RTLIPV6A2SEX_MAX_LEN 65 + /* network to host order conversion for little endian machines */ #define WN2H(w) (((w & 0xFF00) >> 8) | ((w & 0x00FF) << 8)) +/* in the case of the address ::0:a:b:c:d:e:f windows incorrectly cuts off the f. + define FOLLOW_INCORRECT_FIRSTZERO to 1 to follow this behavior, to 0 to disable this. */ +#if !defined(IPV6_FOLLOW_INCORRECT_FIRSTZERO) +#define IPV6_FOLLOW_INCORRECT_FIRSTZERO 1 +#endif + +/* windows does accept a group of the form 0xffff as the last group in an ipv6 address.. + the terminator points to the x in this case. */ +#if !defined(IPV6_ACCEPT_HEXNOTATION_LAST_DIGIT) +#define IPV6_ACCEPT_HEXNOTATION_LAST_DIGIT 1 +#endif + + /* PRIVATE FUNCTIONS **********************************************************/ +/* decode a string with given Base (8, 10 or 16) */ static NTSTATUS -RtlpStringToUlong( +RtlpStringToUlongBase( _In_ PCWSTR String, - _In_ BOOLEAN Strict, + _In_ ULONG Base, _Out_ PCWSTR *Terminator, _Out_ PULONG Out) { - /* If we never see any digits, we'll return this */ NTSTATUS Status = STATUS_INVALID_PARAMETER; ULONG Result = 0; - ULONG Base = 10; ULONG Digit; + while (1) + { + Digit = towlower(*String); + if ((Digit >= L'0' && Digit <= L'9' && Base >= 10) || (Digit >= L'0' && Digit <= L'7')) + Digit -= L'0'; + else if (Digit >= L'a' && Digit <= L'f' && Base >= 16) + Digit -= (L'a' - 10); + else + break; + + Status = RtlULongMult(Result, Base, &Result); + if (!NT_SUCCESS(Status)) + { + Status = STATUS_INVALID_PARAMETER; + break; + } + + Status = RtlULongAdd(Result, Digit, &Result); + if (!NT_SUCCESS(Status)) + { + Status = STATUS_INVALID_PARAMETER; + break; + } + String++; + } + + *Terminator = String; + *Out = Result; + return Status; +} + + +static +NTSTATUS +RtlpStringToUlong( + _In_ PCWSTR String, + _In_ BOOLEAN Strict, + _Out_ PCWSTR *Terminator, + _Out_ PULONG Out) +{ + ULONG Base = 10; + if (String[0] == L'0') { if (String[1] == L'x' || String[1] == L'X') @@ -57,46 +117,66 @@ /* Strict forbids anything but decimal */ if (Strict && Base != 10) { - Status = STATUS_INVALID_PARAMETER; - goto Done; + *Terminator = String; + return STATUS_INVALID_PARAMETER; } + return RtlpStringToUlongBase(String, Base, Terminator, Out); +} - while (1) - { - if (*String >= L'0' && *String <= L'7') - Digit = *String - L'0'; - else if (*String >= L'0' && *String <= L'9' && Base >= 10) - Digit = *String - L'0'; - else if (*String >= L'A' && *String <= L'F' && Base >= 16) - Digit = 10 + (*String - L'A'); - else if (*String >= L'a' && *String <= L'f' && Base >= 16) - Digit = 10 + (*String - L'a'); +/* 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; +} - Status = RtlULongMult(Result, Base, &Result); - if (!NT_SUCCESS(Status)) - { - Status = STATUS_INVALID_PARAMETER; +/* Worker function to extract the ipv4 part of a string. */ +NTSTATUS +NTAPI +RtlpIpv4StringToAddressParserW( + _In_ PCWSTR String, + _In_ BOOLEAN Strict, + _Out_ PCWSTR *Terminator, + _Out_ ULONG Values[4], + _Out_ INT *Parts) +{ + NTSTATUS Status; + *Parts = 0; + do + { + Status = RtlpStringToUlong(String, Strict, &String, &Values[*Parts]); + (*Parts)++; + + if (*String != L'.') break; - } - Status = RtlULongAdd(Result, Digit, &Result); - if (!NT_SUCCESS(Status)) + /* Already four parts, but a dot follows? */ + if (*Parts == 4) { Status = STATUS_INVALID_PARAMETER; break; } - + /* Skip the dot */ String++; - } + } while (NT_SUCCESS(Status)); -Done: *Terminator = String; - *Out = Result; return Status; } + /* PUBLIC FUNCTIONS ***********************************************************/ /* @@ -104,19 +184,19 @@ */ LPSTR NTAPI -RtlIpv4AddressToStringA(IN struct in_addr *Addr, - OUT PCHAR S) +RtlIpv4AddressToStringA(_In_ const struct in_addr *Addr, + _Out_writes_(IPV4_ADDR_STRING_MAX_LEN) PCHAR S) { - INT Length; + if (!S) + return (LPSTR)~0; - if (!S) return (LPSTR)~0; - - Length = sprintf(S, "%u.%u.%u.%u", Addr->S_un.S_un_b.s_b1, + if (!NT_SUCCESS(RtlStringCchPrintfA(S, IPV4_ADDR_STRING_MAX_LEN, "%u.%u.%u.%u", Addr->S_un.S_un_b.s_b1, Addr->S_un.S_un_b.s_b2, Addr->S_un.S_un_b.s_b3, - Addr->S_un.S_un_b.s_b4); + Addr->S_un.S_un_b.s_b4))) + return (LPSTR)~0; - return S + Length; + return S + strlen(S); } /* @@ -124,28 +204,39 @@ */ NTSTATUS NTAPI -RtlIpv4AddressToStringExA(IN struct in_addr *Address, - IN USHORT Port, - OUT PCHAR AddressString, - IN OUT PULONG AddressStringLength) +RtlIpv4AddressToStringExA(_In_ const struct in_addr *Address, + _In_ USHORT Port, + _Out_writes_to_(*AddressStringLength, *AddressStringLength) PCHAR AddressString, + _Inout_ PULONG AddressStringLength) { - CHAR Buffer[IPV4_ADDR_STRING_MAX_LEN+IPV4_PORT_STRING_MAX_LEN]; + CHAR Buffer[IPV4_ADDR_STRING_MAX_LEN + IPV4_PORT_STRING_MAX_LEN] = { 0 }; + NTSTATUS Result; ULONG Length; if (!Address || !AddressString || !AddressStringLength) return STATUS_INVALID_PARAMETER; - Length = sprintf(Buffer, "%u.%u.%u.%u", Address->S_un.S_un_b.s_b1, + Result = RtlStringCchPrintfA(Buffer, _countof(Buffer), "%u.%u.%u.%u", Address->S_un.S_un_b.s_b1, Address->S_un.S_un_b.s_b2, Address->S_un.S_un_b.s_b3, Address->S_un.S_un_b.s_b4); - if (Port) Length += sprintf(Buffer + Length, ":%u", WN2H(Port)); + if (!NT_SUCCESS(Result)) + return STATUS_INVALID_PARAMETER; + Length = strlen(Buffer); + if (Port) + { + Result = RtlStringCchPrintfA(Buffer + Length, _countof(Buffer) - Length, ":%u", WN2H(Port)); + if (!NT_SUCCESS(Result)) + return STATUS_INVALID_PARAMETER; + Length += strlen(Buffer+Length); + } + if (*AddressStringLength > Length) { *AddressStringLength = Length + 1; - strcpy(AddressString, Buffer); + RtlStringCchCopyA(AddressString, IPV4_ADDR_STRING_MAX_LEN+IPV4_PORT_STRING_MAX_LEN, Buffer); return STATUS_SUCCESS; } @@ -158,18 +249,18 @@ */ LPWSTR NTAPI -RtlIpv4AddressToStringW(IN struct in_addr *Addr, - OUT PWCHAR S) +RtlIpv4AddressToStringW(_In_ const struct in_addr *Addr, + _Out_writes_(IPV4_ADDR_STRING_MAX_LEN) PWCHAR S) { - INT Length; + if (!S) + return (LPWSTR)~0; - if (!S) return (LPWSTR)~0; - - Length = swprintf(S, L"%u.%u.%u.%u", Addr->S_un.S_un_b.s_b1, + if(!NT_SUCCESS(RtlStringCchPrintfW(S, IPV4_ADDR_STRING_MAX_LEN, L"%u.%u.%u.%u", Addr->S_un.S_un_b.s_b1, Addr->S_un.S_un_b.s_b2, Addr->S_un.S_un_b.s_b3, - Addr->S_un.S_un_b.s_b4); - return S + Length; + Addr->S_un.S_un_b.s_b4))) + return (LPWSTR)~0; + return S + wcslen(S); } /* @@ -177,28 +268,39 @@ */ NTSTATUS NTAPI -RtlIpv4AddressToStringExW(IN struct in_addr *Address, - IN USHORT Port, - OUT PWCHAR AddressString, - IN OUT PULONG AddressStringLength) +RtlIpv4AddressToStringExW(_In_ const struct in_addr *Address, + _In_ USHORT Port, + _Out_writes_to_(*AddressStringLength, *AddressStringLength) PWCHAR AddressString, + _Inout_ PULONG AddressStringLength) { - WCHAR Buffer[IPV4_ADDR_STRING_MAX_LEN+IPV4_PORT_STRING_MAX_LEN]; + WCHAR Buffer[IPV4_ADDR_STRING_MAX_LEN + IPV4_PORT_STRING_MAX_LEN] = { 0 }; + NTSTATUS Result; ULONG Length; if (!Address || !AddressString || !AddressStringLength) return STATUS_INVALID_PARAMETER; - Length = swprintf(Buffer, L"%u.%u.%u.%u", Address->S_un.S_un_b.s_b1, + Result = RtlStringCchPrintfW(Buffer, _countof(Buffer), L"%u.%u.%u.%u", Address->S_un.S_un_b.s_b1, Address->S_un.S_un_b.s_b2, Address->S_un.S_un_b.s_b3, Address->S_un.S_un_b.s_b4); + if (!NT_SUCCESS(Result)) + return STATUS_INVALID_PARAMETER; - if (Port) Length += swprintf(Buffer + Length, L":%u", WN2H(Port)); + Length = wcslen(Buffer); + if (Port) + { + Result = RtlStringCchPrintfW(Buffer + Length, _countof(Buffer) - Length, L":%u", WN2H(Port)); + if (!NT_SUCCESS(Result)) + return STATUS_INVALID_PARAMETER; + Length += wcslen(Buffer + Length); + } + if (*AddressStringLength > Length) { *AddressStringLength = Length + 1; - wcscpy(AddressString, Buffer); + RtlStringCchCopyW(AddressString, IPV4_ADDR_STRING_MAX_LEN+IPV4_PORT_STRING_MAX_LEN, Buffer); return STATUS_SUCCESS; } @@ -290,30 +392,13 @@ INT Parts = 0; INT i; - do - { - Status = RtlpStringToUlong(String, Strict, &String, &Values[Parts]); - Parts++; + Status = RtlpIpv4StringToAddressParserW(String, Strict, Terminator, Values, &Parts); - if (*String != L'.') - break; - - /* Already four parts, but a dot follows? */ - if (Parts == 4) - { - Status = STATUS_INVALID_PARAMETER; - goto Done; - } - - /* Skip the dot */ - String++; - } while (NT_SUCCESS(Status)); - if (Strict && Parts < 4) Status = STATUS_INVALID_PARAMETER; if (!NT_SUCCESS(Status)) - goto Done; + return Status; /* Combine the parts */ Result = Values[Parts - 1]; @@ -323,16 +408,12 @@ if (Values[i] > 0xFF || (Result & (0xFF << Shift)) != 0) { - Status = STATUS_INVALID_PARAMETER; - goto Done; + return STATUS_INVALID_PARAMETER; } Result |= Values[i] << Shift; } Addr->S_un.S_addr = RtlUlongByteSwap(Result); - -Done: - *Terminator = String; return Status; } @@ -386,111 +467,483 @@ } /* -* @unimplemented +* @implemented */ -NTSTATUS +LPSTR NTAPI -RtlIpv6AddressToStringA(IN struct in6_addr *Addr, - OUT PCHAR S) +RtlIpv6AddressToStringA(_In_ const struct in6_addr *Addr, + _Out_writes_(RTLIPV6A2S_MINLEN) PSTR S) { - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; + WCHAR Buffer[RTLIPV6A2S_MAX_LEN] = { 0 }; + PWSTR Result; + NTSTATUS Status; + + /* Win2k3 returns length offset from the nullptr.. */ + if (!S) return + (LPSTR)~0; + + Buffer[0] = 0; + Result = RtlIpv6AddressToStringW(Addr, Buffer); + + if (!(Result >= Buffer && Result <= Buffer + _countof(Buffer))) + return (LPSTR)~0; + + Status = RtlUnicodeToMultiByteN(S, RTLIPV6A2S_MAX_LEN, NULL, Buffer, (wcslen(Buffer)+1) * sizeof(Buffer[0])); + if (!NT_SUCCESS(Status)) + return (LPSTR)~0; + + return S + strlen(S); } + /* -* @unimplemented +* @implemented */ NTSTATUS NTAPI -RtlIpv6AddressToStringExA(IN struct in6_addr *Address, - IN ULONG ScopeId, - IN ULONG Port, - OUT PCHAR AddressString, - IN OUT PULONG AddressStringLength) +RtlIpv6AddressToStringExA(_In_ const struct in6_addr *Address, + _In_ ULONG ScopeId, + _In_ USHORT Port, + _Out_writes_to_(*AddressStringLength, *AddressStringLength) PSTR AddressString, + _Inout_ PULONG AddressStringLength) { - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; + WCHAR Buffer[RTLIPV6A2SEX_MAX_LEN] = { 0 }; + NTSTATUS Status; + + if (!Address || !AddressString || !AddressStringLength) + return STATUS_INVALID_PARAMETER; + + Status = RtlIpv6AddressToStringExW(Address, ScopeId, Port, Buffer, AddressStringLength); + if (!NT_SUCCESS(Status)) + return Status; + + Status = RtlUnicodeToMultiByteN(AddressString, RTLIPV6A2SEX_MAX_LEN, NULL, Buffer, (wcslen(Buffer)+1) * sizeof(Buffer[0])); + if (!NT_SUCCESS(Status)) + return STATUS_INVALID_PARAMETER; + + return STATUS_SUCCESS; } + /* -* @unimplemented +* @implemented */ -NTSTATUS +LPWSTR NTAPI -RtlIpv6AddressToStringW(IN struct in6_addr *Addr, - OUT PWCHAR S) +RtlIpv6AddressToStringW(_In_ const struct in6_addr *Addr, + _Out_writes_(RTLIPV6A2S_MAX_LEN) PWSTR S) { - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; + NTSTATUS Status; + INT Length = -1; + UINT Parts = 8; + UINT SkipOnce = 1, n; + + /* Win2k3 returns length offset from the nullptr.. */ + if (!S) return + (LPWSTR)~0; + + /* does it look like an ipv4 address contained in an ipv6? http://tools.ietf.org/html/rfc2765 */ + if (!Addr->s6_words[0] && !Addr->s6_words[1] && !Addr->s6_words[2] && !Addr->s6_words[3] && Addr->s6_words[6]) + { + PWSTR Prefix = NULL; + if (Addr->s6_words[4] == 0xffff && !Addr->s6_words[5]) + Prefix = L"ffff:0:"; + else if (!Addr->s6_words[4] && Addr->s6_words[5] == 0xffff) + Prefix = L"ffff:"; + else if (!Addr->s6_words[4] && !Addr->s6_words[5]) + Prefix = L""; + if (Prefix != NULL) + { + Status = RtlStringCchPrintfW(S, RTLIPV6A2S_MAX_LEN, L"::%s%u.%u.%u.%u", Prefix, + Addr->s6_bytes[12], + Addr->s6_bytes[13], + Addr->s6_bytes[14], + Addr->s6_bytes[15]); + if (!NT_SUCCESS(Status)) + return (LPWSTR)~0; + return S + wcslen(S); + } + } + + /* does it look like an ISATAP address? http://tools.ietf.org/html/rfc5214 */ + if (!(Addr->s6_words[4] & 0xfffd) && Addr->s6_words[5] == 0xfe5e) + Parts = 6; + + for (n = 0; n < Parts; ++n) + { + if (SkipOnce && ((n + 1) < Parts) && !Addr->s6_words[n] && !Addr->s6_words[n + 1]) + { + SkipOnce = 0; + while (!Addr->s6_words[n+1] && (n+1) < Parts) + ++n; + *S++ = ':'; + if ((n+1) >= Parts) + *S++ = ':'; + } + else + { + if (n) + *S++ = ':'; + Status = RtlStringCchPrintfW(S, RTLIPV6A2S_MAX_LEN, L"%x", WN2H(Addr->s6_words[n])); + if (!NT_SUCCESS(Status)) + return (LPWSTR)~0; + S += wcslen(S); + } + } + if (Parts < 8) + { + Status = RtlStringCchPrintfW(S, RTLIPV6A2S_MAX_LEN, L":%u.%u.%u.%u", + Addr->s6_bytes[12], + Addr->s6_bytes[13], + Addr->s6_bytes[14], + Addr->s6_bytes[15]); + + if (!NT_SUCCESS(Status)) + return (LPWSTR)~0; + + return S + wcslen(S); + } + *S = 0; /* make sure we are null terminated */ + return S; } + /* -* @unimplemented +* @implemented */ NTSTATUS NTAPI -RtlIpv6AddressToStringExW(IN struct in6_addr *Address, - IN ULONG ScopeId, - IN USHORT Port, - IN OUT PWCHAR AddressString, - IN OUT PULONG AddressStringLength) +RtlIpv6AddressToStringExW(_In_ const struct in6_addr *Address, + _In_ ULONG ScopeId, + _In_ USHORT Port, + _Out_writes_to_(*AddressStringLength, *AddressStringLength) PWCHAR AddressString, + _Inout_ PULONG AddressStringLength) { - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; + WCHAR Buffer[RTLIPV6A2SEX_MAX_LEN] = { 0 }; + PWCHAR S = Buffer; + NTSTATUS Result; + ULONG Len; + + if (!Address || !AddressString || !AddressStringLength) + return STATUS_INVALID_PARAMETER; + + if (Port) + *S++ = '['; + + S = RtlIpv6AddressToStringW(Address, S); + if ((LPCWSTR)~0 == S) + return STATUS_INVALID_PARAMETER; /* this should not be possible, but better check... */ + + if (ScopeId) + { + Result = RtlStringCchPrintfW(S, RTLIPV6A2SEX_MAX_LEN - (S-Buffer), L"%%%u", ScopeId); + if (!NT_SUCCESS(Result)) + return STATUS_INVALID_PARAMETER; + S += wcslen(S); + } + + if (Port) + { + Result = RtlStringCchPrintfW(S, RTLIPV6A2SEX_MAX_LEN - (S-Buffer), L"]:%u", WN2H(Port)); + if (!NT_SUCCESS(Result)) + return STATUS_INVALID_PARAMETER; + S += wcslen(S); + } + + Len = (S - Buffer) + 1; + + if (Len < *AddressStringLength) + { + *AddressStringLength = Len; + memcpy(AddressString, Buffer, sizeof(Buffer[0])* Len); + return STATUS_SUCCESS; + } + + *AddressStringLength = Len; + return STATUS_INVALID_PARAMETER; } /* -* @unimplemented +* @implemented */ NTSTATUS NTAPI -RtlIpv6StringToAddressA(IN PCHAR Name, - OUT PCHAR *Terminator, - OUT struct in6_addr *Addr) +RtlIpv6StringToAddressA(_In_ PCSTR Name, + _Out_ PCSTR *Terminator, + _Out_ struct in6_addr *Addr) { - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; + NTSTATUS Status; + ANSI_STRING NameA; + UNICODE_STRING NameW; + PWCHAR TerminatorW = NULL; + + Status = RtlInitAnsiStringEx(&NameA, Name); + if (!NT_SUCCESS(Status)) + return Status; + + Status = RtlAnsiStringToUnicodeString(&NameW, &NameA, TRUE); + if (!NT_SUCCESS(Status)) + return Status; + + Status = RtlIpv6StringToAddressW(NameW.Buffer, &TerminatorW, Addr); + /* on windows the terminator is not always written, so we mimic that behavior. */ + if (TerminatorW) + *Terminator = Name + (TerminatorW - NameW.Buffer); + + RtlFreeUnicodeString(&NameW); + return Status; } /* -* @unimplemented +* @implemented */ NTSTATUS NTAPI -RtlIpv6StringToAddressExA(IN PCHAR AddressString, - OUT struct in6_addr *Address, - OUT PULONG ScopeId, - OUT PUSHORT Port) +RtlIpv6StringToAddressExA(_In_ PCSTR AddressString, + _Out_ struct in6_addr *Address, + _Out_ PULONG ScopeId, + _Out_ PUSHORT Port) { - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; + NTSTATUS Status; + ANSI_STRING AddressA; + UNICODE_STRING AddressW; + + Status = RtlInitAnsiStringEx(&AddressA, AddressString); + if (!NT_SUCCESS(Status)) + return Status; + + Status = RtlAnsiStringToUnicodeString(&AddressW, &AddressA, TRUE); + if (!NT_SUCCESS(Status)) + return Status; + + Status = RtlIpv6StringToAddressExW(AddressW.Buffer, Address, ScopeId, Port); + + RtlFreeUnicodeString(&AddressW); + return Status; } + /* -* @unimplemented +* @implemented */ NTSTATUS NTAPI -RtlIpv6StringToAddressW(IN PWCHAR Name, - OUT PCHAR *Terminator, - OUT struct in6_addr *Addr) +RtlIpv6StringToAddressW(_In_ PCWSTR Name, + _Out_ PCWSTR *Terminator, + _Out_ struct in6_addr *Addr) { - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; + INT n, j; + INT StartSkip = -1, Parts = 0; + ULONG Base, Len; + NTSTATUS Status = STATUS_SUCCESS; + enum { None, Number, Colon, DoubleColon } Last = None; +#if IPV6_ACCEPT_HEXNOTATION_LAST_DIGIT + BOOLEAN SkipoutLastHex = FALSE; +#endif + + if (!Name || !Terminator || !Addr) + return STATUS_INVALID_PARAMETER; + + for (n = 0; n < 8;) + { + Len = RtlpClassifyChars(Name, &Base); + if (Len == 0) + { + /* not a number, and no ':' or already encountered an ':' */ + if (Name[0] != ':' || Last == Colon) + break; + + /* double colon, 1 or more fields set to 0. mark the position, and move on. */ + if (StartSkip == -1 && Name[1] == ':') + { +#if IPV6_FOLLOW_INCORRECT_FIRSTZERO + /* this case was observed in windows, but it does not seem correct. */ + if (!n) + { + Addr->s6_words[n++] = 0; + Addr->s6_words[n++] = 0; + } +#endif /* IPV6_FOLLOW_INCORRECT_FIRSTZERO */ + StartSkip = n; + Name += 2; + Last = DoubleColon; + } + else if (Name[1] == ':' || Last != Number) + { + /* a double colon after we already encountered one, or a the last parsed item was not a number. */ + break; + } + else + { + ++Name; + Last = Colon; + } + } + 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; + Name += Len; + break; + } + else + { + ULONG Value; + if (Name[Len] == '.' && n <= 6) + { + 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(Name, TRUE, &Name, 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 || (*Name == ':' && 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; + } + + if (Name[Len] != ':' && n < 7 && StartSkip == -1) + { + /* if we decoded atleast some numbers, update the terminator to point to the first invalid char */ + if (Base) + Name += Len; + Status = STATUS_INVALID_PARAMETER; + break; + } + +#if IPV6_ACCEPT_HEXNOTATION_LAST_DIGIT + if (Len == 1 && towlower(Name[Len]) == 'x' && Name[0] == '0') + { + Len = RtlpClassifyChars(Name + 2, &Base); + if (Len > 0 && Len <= 4) + { + *Terminator = Name + 1; + Name += 2; + SkipoutLastHex = TRUE; + } + } +#endif + + Status = RtlpStringToUlongBase(Name, 16, &Name, &Value); + if (!NT_SUCCESS(Status)) + break; + + if (StartSkip != -1) + Parts++; + Addr->s6_words[n++] = WN2H(Value); + Last = Number; +#if IPV6_ACCEPT_HEXNOTATION_LAST_DIGIT + if (SkipoutLastHex) + break; +#endif + } + } + + 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 IPV6_ACCEPT_HEXNOTATION_LAST_DIGIT + /* we have already set the terminator */ + if (SkipoutLastHex) + return n < 8 ? STATUS_INVALID_PARAMETER : Status; +#endif + *Terminator = Name; + return n < 8 ? STATUS_INVALID_PARAMETER : Status; } /* -* @unimplemented +* @implemented */ NTSTATUS NTAPI -RtlIpv6StringToAddressExW(IN PWCHAR AddressName, - OUT struct in6_addr *Address, - OUT PULONG ScopeId, - OUT PUSHORT Port) +RtlIpv6StringToAddressExW(_In_ PCWSTR AddressName, + _Out_ struct in6_addr *Address, + _Out_ PULONG ScopeId, + _Out_ PUSHORT Port) { - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; + NTSTATUS Status; + ULONG ConvertedPort = 0, ConvertedScope = 0; + if (!AddressName || !Address || !ScopeId || !Port) + return STATUS_INVALID_PARAMETER; + + if (*AddressName == '[') + { + ConvertedPort = 1; + ++AddressName; + } + Status = RtlIpv6StringToAddressW(AddressName, &AddressName, Address); + if (!NT_SUCCESS(Status)) + return STATUS_INVALID_PARAMETER; + + if (*AddressName == '%') + { + ++AddressName; + Status = RtlpStringToUlongBase(AddressName, 10, &AddressName, &ConvertedScope); + if (!NT_SUCCESS(Status)) + return STATUS_INVALID_PARAMETER; + } + else if (*AddressName && !(ConvertedPort && *AddressName == ']')) + { + return STATUS_INVALID_PARAMETER; + } + + if (ConvertedPort) + { + if ( *AddressName++ !=']' ) + return STATUS_INVALID_PARAMETER; + if (*AddressName ==':') + { + ULONG Base = 10; + if (*++AddressName == '0') + { + if (towlower(*++AddressName) != 'x') + return STATUS_INVALID_PARAMETER; + ++AddressName; + Base = 16; + } + Status = RtlpStringToUlongBase(AddressName, Base, &AddressName, &ConvertedPort); + if (!NT_SUCCESS(Status) || ConvertedPort > 0xffff) + return STATUS_INVALID_PARAMETER; + } + else + { + ConvertedPort = 0; + } + } + + if (*AddressName == 0) + { + *ScopeId = ConvertedScope; + *Port = WN2H(ConvertedPort); + return STATUS_SUCCESS; + } + return STATUS_INVALID_PARAMETER; } /* EOF */