mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 18:15:11 +00:00
56229b7a06
Signed-off-by: Alex Henrie <alexhenrie24@gmail.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org> wine commit id 474d1f0b2daa8583fa82fe203b207807ba29499d
1035 lines
28 KiB
C
1035 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);
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/*
|
|
* RtlIpv6StringToAddress[Ex]W implementation was imported from Wine 9.6 at 20/4/2024
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* For full text of the license see COPYING.LIB in the root of this project.
|
|
*
|
|
* Copyright 2020 Alex Henrie
|
|
*/
|
|
|
|
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 parse_ipv4_component(const WCHAR **str, BOOL strict, ULONG *value)
|
|
{
|
|
int base = 10, d;
|
|
WCHAR c;
|
|
ULONG cur_value, prev_value = 0;
|
|
BOOL success = FALSE;
|
|
|
|
if (**str == '.')
|
|
{
|
|
*str += 1;
|
|
return FALSE;
|
|
}
|
|
|
|
if ((*str)[0] == '0')
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
for (cur_value = 0; **str; *str += 1)
|
|
{
|
|
c = **str;
|
|
if (c >= ARRAYSIZE(hex_table)) break;
|
|
d = hex_table[c];
|
|
if (d == -1 || d >= base) break;
|
|
cur_value = cur_value * base + d;
|
|
success = TRUE;
|
|
if (cur_value < prev_value) return FALSE; /* overflow */
|
|
prev_value = cur_value;
|
|
}
|
|
|
|
if (success) *value = cur_value;
|
|
return success;
|
|
}
|
|
|
|
static BOOL parse_ipv6_component(const WCHAR **str, int base, ULONG *value)
|
|
{
|
|
WCHAR *terminator;
|
|
if (**str >= ARRAYSIZE(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++;
|
|
address->u.Word[0] = 0;
|
|
}
|
|
|
|
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] = RtlUshortByteSwap(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] = RtlUshortByteSwap(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 = RtlUshortByteSwap(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;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
RtlIpv6StringToAddressW(
|
|
_In_ PCWSTR String,
|
|
_Out_ PCWSTR *Terminator,
|
|
_Out_ struct in6_addr *Addr)
|
|
{
|
|
if (!String || !Terminator || !Addr)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
return ipv6_string_to_address(String, FALSE, Terminator, Addr, NULL, NULL);
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
RtlIpv6StringToAddressExW(
|
|
_In_ PCWSTR AddressString,
|
|
_Out_ struct in6_addr *Address,
|
|
_Out_ PULONG ScopeId,
|
|
_Out_ PUSHORT Port)
|
|
{
|
|
if (!AddressString || !Address || !ScopeId || !Port)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
return ipv6_string_to_address(AddressString, TRUE, NULL, Address, ScopeId, Port);
|
|
}
|
|
|
|
/* End of Wine implementation */
|
|
|
|
/* EOF */
|