Author: tfaber Date: Fri Apr 10 08:38:05 2015 New Revision: 67125
URL: http://svn.reactos.org/svn/reactos?rev=67125&view=rev Log: [RTL] - Implement RtlIpv6StringToAddress*. Patch by Mark Jansen. CORE-6490
Modified: trunk/reactos/lib/rtl/network.c
Modified: trunk/reactos/lib/rtl/network.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/lib/rtl/network.c?rev=67125... ============================================================================== --- trunk/reactos/lib/rtl/network.c [iso-8859-1] (original) +++ trunk/reactos/lib/rtl/network.c [iso-8859-1] Fri Apr 10 08:38:05 2015 @@ -30,6 +30,51 @@
/* PRIVATE FUNCTIONS **********************************************************/
+/* decode a string with given Base (8, 10 or 16) */ +static +NTSTATUS +RtlpStringToUlongBase( + _In_ PCWSTR String, + _In_ ULONG Base, + _Out_ PCWSTR *Terminator, + _Out_ PULONG Out) +{ + NTSTATUS Status = STATUS_INVALID_PARAMETER; + ULONG Result = 0; + ULONG Digit; + + while (1) + { + Digit = towlower(*String); + if (isdigit(Digit) && (Base >= 10 || 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( @@ -38,11 +83,7 @@ _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;
if (String[0] == L'0') { @@ -63,43 +104,63 @@ /* Strict forbids anything but decimal */ if (Strict && Base != 10) { - Status = STATUS_INVALID_PARAMETER; - goto Done; - } - - 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'); + *Terminator = String; + return STATUS_INVALID_PARAMETER; + } + 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; - - Status = RtlULongMult(Result, Base, &Result); - if (!NT_SUCCESS(Status)) + } + return Len; +} + +/* Worker function to extract the ipv4 part of a string. */ +NTSTATUS +NTAPI +RtlpIpv4StringToAddressParserW( + _In_ PCWSTR String, + _In_ BOOLEAN Strict, + _Out_ PCWSTR *Terminator, + _Out_writes_(4) ULONG *Values, + _Out_ INT *Parts) +{ + NTSTATUS Status; + *Parts = 0; + do + { + Status = RtlpStringToUlong(String, Strict, &String, &Values[*Parts]); + (*Parts)++; + + if (*String != L'.') + break; + + /* Already four parts, but a dot follows? */ + if (*Parts == 4) { Status = STATUS_INVALID_PARAMETER; break; } - - Status = RtlULongAdd(Result, Digit, &Result); - if (!NT_SUCCESS(Status)) - { - Status = STATUS_INVALID_PARAMETER; - break; - } - + /* Skip the dot */ String++; - } - -Done: + } while (NT_SUCCESS(Status)); + *Terminator = String; - *Out = Result; return Status; }
@@ -353,30 +414,16 @@ INT Parts = 0; INT i;
- do - { - Status = RtlpStringToUlong(String, Strict, &String, &Values[Parts]); - 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)); - + Status = RtlpIpv4StringToAddressParserW(String, + Strict, + Terminator, + Values, + &Parts); if (Strict && Parts < 4) Status = STATUS_INVALID_PARAMETER;
if (!NT_SUCCESS(Status)) - goto Done; + return Status;
/* Combine the parts */ Result = Values[Parts - 1]; @@ -386,16 +433,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; }
@@ -695,7 +738,7 @@ }
/* -* @unimplemented +* @implemented */ NTSTATUS NTAPI @@ -704,12 +747,30 @@ _Out_ PCSTR *Terminator, _Out_ struct in6_addr *Addr) { - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; -} - -/* -* @unimplemented + NTSTATUS Status; + ANSI_STRING StringA; + UNICODE_STRING StringW; + PCWSTR TerminatorW = NULL; + + Status = RtlInitAnsiStringEx(&StringA, String); + if (!NT_SUCCESS(Status)) + return Status; + + Status = RtlAnsiStringToUnicodeString(&StringW, &StringA, TRUE); + if (!NT_SUCCESS(Status)) + return Status; + + Status = RtlIpv6StringToAddressW(StringW.Buffer, &TerminatorW, Addr); + /* on windows the terminator is not always written, so we mimic that behavior. */ + if (TerminatorW) + *Terminator = String + (TerminatorW - StringW.Buffer); + + RtlFreeUnicodeString(&StringW); + return Status; +} + +/* +* @implemented */ NTSTATUS NTAPI @@ -719,12 +780,26 @@ _Out_ PULONG ScopeId, _Out_ PUSHORT Port) { - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; -} - -/* -* @unimplemented + 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; +} + +/* +* @implemented */ NTSTATUS NTAPI @@ -733,12 +808,146 @@ _Out_ PCWSTR *Terminator, _Out_ struct in6_addr *Addr) { - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; -} - -/* -* @unimplemented + 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; + + if (!String || !Terminator || !Addr) + return STATUS_INVALID_PARAMETER; + + for (n = 0; n < 8;) + { + Len = RtlpClassifyChars(String, &Base); + if (Len == 0) + { + /* not a number, and no ':' or already encountered an ':' */ + if (String[0] != ':' || Last == Colon) + break; + + /* 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) + { + /* a double colon after we already encountered one, or a the last parsed item was not a number. */ + break; + } + else + { + ++String; + 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; + String += Len; + break; + } + else + { + ULONG Value; + if (String[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(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; + } + + 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 (Len == 1 && towlower(String[Len]) == 'x' && String[0] == '0') + { + Len = RtlpClassifyChars(String + 2, &Base); + if (Len > 0 && Len <= 4) + { + *Terminator = String + 1; + String += 2; + SkipoutLastHex = TRUE; + } + } + + 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; + } + + /* we have already set the terminator */ + if (SkipoutLastHex) + return n < 8 ? STATUS_INVALID_PARAMETER : Status; + *Terminator = String; + return n < 8 ? STATUS_INVALID_PARAMETER : Status; +} + +/* +* @implemented */ NTSTATUS NTAPI @@ -748,8 +957,63 @@ _Out_ PULONG ScopeId, _Out_ PUSHORT Port) { - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; + 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)) + 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; }
/* EOF */