reactos/sdk/lib/rtl/network.c
2019-08-15 14:20:00 +02:00

1020 lines
28 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS Runtime Library
* PURPOSE: Network Address Translation implementation
* PROGRAMMER: Alex Ionescu (alexi@tinykrnl.org)
* Thomas Faber (thomas.faber@reactos.org)
*/
/* INCLUDES *****************************************************************/
#include <rtl.h>
#include <ntstrsafe.h>
#define NDEBUG
#include <debug.h>
/* maximum length of an ipv4 address expressed as a string */
#define IPV4_ADDR_STRING_MAX_LEN 16
/* 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))
/* 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(
_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')
{
/* 0x/0X prefix -- hex */
String += 2;
Base = 16;
}
else if (String[1] >= L'0' && String[1] <= L'9')
{
/* 0 prefix -- octal */
String++;
Base = 8;
}
}
/* Strict forbids anything but decimal */
if (Strict && Base != 10)
{
*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;
}
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;
}
/* Skip the dot */
String++;
} while (NT_SUCCESS(Status));
*Terminator = String;
return Status;
}
/* PUBLIC FUNCTIONS ***********************************************************/
/*
* @implemented
*/
PSTR
NTAPI
RtlIpv4AddressToStringA(
_In_ const struct in_addr *Addr,
_Out_writes_(IPV4_ADDR_STRING_MAX_LEN) PCHAR S)
{
NTSTATUS Status;
PSTR End;
if (!S)
return (PSTR)~0;
Status = RtlStringCchPrintfExA(S,
IPV4_ADDR_STRING_MAX_LEN,
&End,
NULL,
0,
"%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);
ASSERT(Status == STATUS_SUCCESS);
if (!NT_SUCCESS(Status))
return (PSTR)~0;
return End;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
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];
NTSTATUS Status;
ULONG Length;
PSTR End;
if (!Address || !AddressString || !AddressStringLength)
return STATUS_INVALID_PARAMETER;
Status = RtlStringCchPrintfExA(Buffer,
RTL_NUMBER_OF(Buffer),
&End,
NULL,
0,
Port ? "%u.%u.%u.%u:%u"
: "%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,
WN2H(Port));
ASSERT(Status == STATUS_SUCCESS);
if (!NT_SUCCESS(Status))
return STATUS_INVALID_PARAMETER;
Length = End - Buffer;
if (*AddressStringLength > Length)
{
Status = RtlStringCchCopyA(AddressString,
*AddressStringLength,
Buffer);
ASSERT(Status == STATUS_SUCCESS);
*AddressStringLength = Length + 1;
return STATUS_SUCCESS;
}
*AddressStringLength = Length + 1;
return STATUS_INVALID_PARAMETER;
}
/*
* @implemented
*/
PWSTR
NTAPI
RtlIpv4AddressToStringW(
_In_ const struct in_addr *Addr,
_Out_writes_(IPV4_ADDR_STRING_MAX_LEN) PWCHAR S)
{
NTSTATUS Status;
PWSTR End;
if (!S)
return (PWSTR)~0;
Status = RtlStringCchPrintfExW(S,
IPV4_ADDR_STRING_MAX_LEN,
&End,
NULL,
0,
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);
ASSERT(Status == STATUS_SUCCESS);
if (!NT_SUCCESS(Status))
return (PWSTR)~0;
return End;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
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];
NTSTATUS Status;
ULONG Length;
PWSTR End;
if (!Address || !AddressString || !AddressStringLength)
return STATUS_INVALID_PARAMETER;
Status = RtlStringCchPrintfExW(Buffer,
RTL_NUMBER_OF(Buffer),
&End,
NULL,
0,
Port ? L"%u.%u.%u.%u:%u"
: 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,
WN2H(Port));
ASSERT(Status == STATUS_SUCCESS);
if (!NT_SUCCESS(Status))
return STATUS_INVALID_PARAMETER;
Length = End - AddressString;
if (*AddressStringLength > Length)
{
Status = RtlStringCchCopyW(AddressString,
*AddressStringLength,
Buffer);
ASSERT(Status == STATUS_SUCCESS);
*AddressStringLength = Length + 1;
return STATUS_SUCCESS;
}
*AddressStringLength = Length + 1;
return STATUS_INVALID_PARAMETER;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
RtlIpv4StringToAddressA(
_In_ PCSTR String,
_In_ BOOLEAN Strict,
_Out_ PCSTR *Terminator,
_Out_ struct in_addr *Addr)
{
NTSTATUS Status;
ANSI_STRING AddressA;
UNICODE_STRING AddressW;
PCWSTR TerminatorW = NULL;
Status = RtlInitAnsiStringEx(&AddressA, String);
if (!NT_SUCCESS(Status))
return Status;
Status = RtlAnsiStringToUnicodeString(&AddressW, &AddressA, TRUE);
if (!NT_SUCCESS(Status))
return Status;
Status = RtlIpv4StringToAddressW(AddressW.Buffer,
Strict,
&TerminatorW,
Addr);
ASSERT(TerminatorW >= AddressW.Buffer);
*Terminator = String + (TerminatorW - AddressW.Buffer);
RtlFreeUnicodeString(&AddressW);
return Status;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
RtlIpv4StringToAddressExA(
_In_ PCSTR AddressString,
_In_ BOOLEAN Strict,
_Out_ struct in_addr *Address,
_Out_ PUSHORT Port)
{
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 = RtlIpv4StringToAddressExW(AddressW.Buffer, Strict, Address, Port);
RtlFreeUnicodeString(&AddressW);
return Status;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
RtlIpv4StringToAddressW(
_In_ PCWSTR String,
_In_ BOOLEAN Strict,
_Out_ PCWSTR *Terminator,
_Out_ struct in_addr *Addr)
{
NTSTATUS Status;
ULONG Values[4];
ULONG Result;
INT Parts = 0;
INT i;
Status = RtlpIpv4StringToAddressParserW(String,
Strict,
Terminator,
Values,
&Parts);
if (Strict && Parts < 4)
Status = STATUS_INVALID_PARAMETER;
if (!NT_SUCCESS(Status))
return Status;
/* Combine the parts */
Result = Values[Parts - 1];
for (i = 0; i < Parts - 1; i++)
{
INT Shift = CHAR_BIT * (3 - i);
if (Values[i] > 0xFF || (Result & (0xFF << Shift)) != 0)
{
return STATUS_INVALID_PARAMETER;
}
Result |= Values[i] << Shift;
}
Addr->S_un.S_addr = RtlUlongByteSwap(Result);
return Status;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
RtlIpv4StringToAddressExW(
_In_ PCWSTR AddressString,
_In_ BOOLEAN Strict,
_Out_ struct in_addr *Address,
_Out_ PUSHORT Port)
{
PCWSTR CurrentChar;
ULONG ConvertedPort;
NTSTATUS Status;
if (!AddressString || !Address || !Port)
return STATUS_INVALID_PARAMETER;
Status = RtlIpv4StringToAddressW(AddressString,
Strict,
&CurrentChar,
Address);
if (!NT_SUCCESS(Status))
return Status;
if (!*CurrentChar)
{
*Port = 0;
return STATUS_SUCCESS;
}
if (*CurrentChar != L':')
return STATUS_INVALID_PARAMETER;
++CurrentChar;
Status = RtlpStringToUlong(CurrentChar,
FALSE,
&CurrentChar,
&ConvertedPort);
if (!NT_SUCCESS(Status))
return Status;
if (*CurrentChar || !ConvertedPort || ConvertedPort > 0xffff)
return STATUS_INVALID_PARAMETER;
*Port = WN2H(ConvertedPort);
return STATUS_SUCCESS;
}
/*
* @implemented
*/
PSTR
NTAPI
RtlIpv6AddressToStringA(
_In_ const struct in6_addr *Addr,
_Out_writes_(RTLIPV6A2S_MAX_LEN) PSTR S)
{
WCHAR Buffer[RTLIPV6A2S_MAX_LEN];
PWSTR Result;
NTSTATUS Status;
if (!S)
return (PSTR)~0;
Buffer[0] = 0;
Result = RtlIpv6AddressToStringW(Addr, Buffer);
if (Result == (PWSTR)~0)
return (PSTR)~0;
ASSERT(Result >= Buffer);
ASSERT(Result < Buffer + RTL_NUMBER_OF(Buffer));
Status = RtlUnicodeToMultiByteN(S, RTLIPV6A2S_MAX_LEN, NULL, Buffer, (ULONG)(wcslen(Buffer) + 1) * sizeof(WCHAR));
if (!NT_SUCCESS(Status))
return (PSTR)~0;
return S + strlen(S);
}
/*
* @implemented
*/
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)
{
WCHAR Buffer[RTLIPV6A2SEX_MAX_LEN];
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, (*AddressStringLength + 1) * sizeof(WCHAR));
if (!NT_SUCCESS(Status))
return STATUS_INVALID_PARAMETER;
return STATUS_SUCCESS;
}
/*
* @implemented
*/
PWSTR
NTAPI
RtlIpv6AddressToStringW(
_In_ const struct in6_addr *Addr,
_Out_writes_(RTLIPV6A2S_MAX_LEN) PWSTR S)
{
NTSTATUS Status;
UINT Parts = 8, n;
BOOLEAN SkipOnce = TRUE;
PWSTR End;
size_t Remaining;
if (!S)
return (PWSTR)~0;
Remaining = RTLIPV6A2S_MAX_LEN;
/* 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 = RtlStringCchPrintfExW(S,
Remaining,
&End,
NULL,
0,
L"::%ls%u.%u.%u.%u",
Prefix,
Addr->s6_bytes[12],
Addr->s6_bytes[13],
Addr->s6_bytes[14],
Addr->s6_bytes[15]);
ASSERT(Status == STATUS_SUCCESS);
if (!NT_SUCCESS(Status))
return (PWSTR)~0;
return End;
}
}
/* 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 = FALSE;
while (!Addr->s6_words[n + 1] && (n + 1) < Parts)
++n;
*S++ = ':';
Remaining--;
if ((n + 1) >= Parts)
{
*S++ = ':';
Remaining--;
}
}
else
{
if (n)
{
*S++ = ':';
Remaining--;
}
Status = RtlStringCchPrintfExW(S,
Remaining,
&End,
&Remaining,
0,
L"%x",
WN2H(Addr->s6_words[n]));
ASSERT(Status == STATUS_SUCCESS);
if (!NT_SUCCESS(Status))
return (PWSTR)~0;
S = End;
}
}
if (Parts < 8)
{
Status = RtlStringCchPrintfExW(S,
Remaining,
&End,
NULL,
0,
L":%u.%u.%u.%u",
Addr->s6_bytes[12],
Addr->s6_bytes[13],
Addr->s6_bytes[14],
Addr->s6_bytes[15]);
ASSERT(Status == STATUS_SUCCESS);
if (!NT_SUCCESS(Status))
return (PWSTR)~0;
return End;
}
*S = UNICODE_NULL;
return S;
}
/*
* @implemented
*/
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)
{
WCHAR Buffer[RTLIPV6A2SEX_MAX_LEN];
PWCHAR S = Buffer;
NTSTATUS Status;
ULONG Length;
size_t Remaining;
if (!Address || !AddressString || !AddressStringLength)
return STATUS_INVALID_PARAMETER;
if (Port)
*S++ = L'[';
S = RtlIpv6AddressToStringW(Address, S);
ASSERT(S != (PCWSTR)~0);
if (S == (PCWSTR)~0)
return STATUS_INVALID_PARAMETER;
ASSERT(S >= Buffer);
ASSERT(S <= Buffer + RTLIPV6A2S_MAX_LEN + 1);
Remaining = RTL_NUMBER_OF(Buffer) - (S - Buffer);
ASSERT(Remaining >= RTLIPV6A2SEX_MAX_LEN - RTLIPV6A2S_MAX_LEN);
if (ScopeId)
{
Status = RtlStringCchPrintfExW(S,
Remaining,
&S,
&Remaining,
0,
L"%%%u",
ScopeId);
ASSERT(Status == STATUS_SUCCESS);
if (!NT_SUCCESS(Status))
return STATUS_INVALID_PARAMETER;
}
if (Port)
{
Status = RtlStringCchPrintfExW(S,
Remaining,
&S,
&Remaining,
0,
L"]:%u",
WN2H(Port));
ASSERT(Status == STATUS_SUCCESS);
if (!NT_SUCCESS(Status))
return STATUS_INVALID_PARAMETER;
}
Length = S - Buffer;
ASSERT(Buffer[Length] == UNICODE_NULL);
if (*AddressStringLength > Length)
{
Status = RtlStringCchCopyW(AddressString, *AddressStringLength, Buffer);
ASSERT(Status == STATUS_SUCCESS);
*AddressStringLength = Length + 1;
return STATUS_SUCCESS;
}
*AddressStringLength = Length + 1;
return STATUS_INVALID_PARAMETER;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
RtlIpv6StringToAddressA(
_In_ PCSTR String,
_Out_ PCSTR *Terminator,
_Out_ struct in6_addr *Addr)
{
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
RtlIpv6StringToAddressExA(
_In_ PCSTR AddressString,
_Out_ struct in6_addr *Address,
_Out_ PULONG ScopeId,
_Out_ PUSHORT Port)
{
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
RtlIpv6StringToAddressW(
_In_ PCWSTR String,
_Out_ PCWSTR *Terminator,
_Out_ struct in6_addr *Addr)
{
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
RtlIpv6StringToAddressExW(
_In_ PCWSTR AddressString,
_Out_ struct in6_addr *Address,
_Out_ PULONG ScopeId,
_Out_ PUSHORT Port)
{
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 */