2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Runtime Library
4 * PURPOSE: Network Address Translation implementation
5 * PROGRAMMER: Alex Ionescu (alexi@tinykrnl.org)
6 * Thomas Faber (thomas.faber@reactos.org)
9 /* INCLUDES *****************************************************************/
12 #include <ntstrsafe.h>
16 /* maximum length of an ipv4 address expressed as a string */
17 #define IPV4_ADDR_STRING_MAX_LEN 16
19 /* maximum length of an ipv4 port expressed as a string */
20 #define IPV4_PORT_STRING_MAX_LEN 7 /* with the leading ':' */
22 /* maximum length of an ipv6 string for RtlIpv6AddressToString */
23 #define RTLIPV6A2S_MAX_LEN 46
25 /* maximum length of an ipv6 string with scope and port for RtlIpv6AddressToStringEx */
26 #define RTLIPV6A2SEX_MAX_LEN 65
28 /* network to host order conversion for little endian machines */
29 #define WN2H(w) (((w & 0xFF00) >> 8) | ((w & 0x00FF) << 8))
31 /* PRIVATE FUNCTIONS **********************************************************/
33 /* decode a string with given Base (8, 10 or 16) */
36 RtlpStringToUlongBase(
39 _Out_ PCWSTR
*Terminator
,
42 NTSTATUS Status
= STATUS_INVALID_PARAMETER
;
48 Digit
= towlower(*String
);
49 if (isdigit(Digit
) && (Base
>= 10 || Digit
<= L
'7'))
51 else if (Digit
>= L
'a' && Digit
<= L
'f' && Base
>= 16)
56 Status
= RtlULongMult(Result
, Base
, &Result
);
57 if (!NT_SUCCESS(Status
))
59 Status
= STATUS_INVALID_PARAMETER
;
63 Status
= RtlULongAdd(Result
, Digit
, &Result
);
64 if (!NT_SUCCESS(Status
))
66 Status
= STATUS_INVALID_PARAMETER
;
83 _Out_ PCWSTR
*Terminator
,
88 if (String
[0] == L
'0')
90 if (String
[1] == L
'x' || String
[1] == L
'X')
92 /* 0x/0X prefix -- hex */
96 else if (String
[1] >= L
'0' && String
[1] <= L
'9')
98 /* 0 prefix -- octal */
104 /* Strict forbids anything but decimal */
105 if (Strict
&& Base
!= 10)
107 *Terminator
= String
;
108 return STATUS_INVALID_PARAMETER
;
110 return RtlpStringToUlongBase(String
, Base
, Terminator
, Out
);
113 /* Tell us what possible base the string could be in, 10 or 16 by looking at the characters.
114 Invalid characters break the operation */
117 RtlpClassifyChars(PCWSTR S
, PULONG Base
)
121 for (Len
= 0; S
[Len
]; ++Len
)
123 if (iswascii(S
[Len
]) && isdigit(S
[Len
]))
124 *Base
= max(*Base
, 10);
125 else if (iswascii(S
[Len
]) && isxdigit(S
[Len
]))
133 /* Worker function to extract the ipv4 part of a string. */
136 RtlpIpv4StringToAddressParserW(
139 _Out_ PCWSTR
*Terminator
,
140 _Out_writes_(4) ULONG
*Values
,
147 Status
= RtlpStringToUlong(String
, Strict
, &String
, &Values
[*Parts
]);
153 /* Already four parts, but a dot follows? */
156 Status
= STATUS_INVALID_PARAMETER
;
161 } while (NT_SUCCESS(Status
));
163 *Terminator
= String
;
167 /* PUBLIC FUNCTIONS ***********************************************************/
174 RtlIpv4AddressToStringA(
175 _In_
const struct in_addr
*Addr
,
176 _Out_writes_(IPV4_ADDR_STRING_MAX_LEN
) PCHAR S
)
184 Status
= RtlStringCchPrintfExA(S
,
185 IPV4_ADDR_STRING_MAX_LEN
,
190 Addr
->S_un
.S_un_b
.s_b1
,
191 Addr
->S_un
.S_un_b
.s_b2
,
192 Addr
->S_un
.S_un_b
.s_b3
,
193 Addr
->S_un
.S_un_b
.s_b4
);
194 ASSERT(Status
== STATUS_SUCCESS
);
195 if (!NT_SUCCESS(Status
))
206 RtlIpv4AddressToStringExA(
207 _In_
const struct in_addr
*Address
,
209 _Out_writes_to_(*AddressStringLength
, *AddressStringLength
) PCHAR AddressString
,
210 _Inout_ PULONG AddressStringLength
)
212 CHAR Buffer
[IPV4_ADDR_STRING_MAX_LEN
+ IPV4_PORT_STRING_MAX_LEN
];
217 if (!Address
|| !AddressString
|| !AddressStringLength
)
218 return STATUS_INVALID_PARAMETER
;
220 Status
= RtlStringCchPrintfExA(Buffer
,
221 RTL_NUMBER_OF(Buffer
),
225 Port
? "%u.%u.%u.%u:%u"
227 Address
->S_un
.S_un_b
.s_b1
,
228 Address
->S_un
.S_un_b
.s_b2
,
229 Address
->S_un
.S_un_b
.s_b3
,
230 Address
->S_un
.S_un_b
.s_b4
,
232 ASSERT(Status
== STATUS_SUCCESS
);
233 if (!NT_SUCCESS(Status
))
234 return STATUS_INVALID_PARAMETER
;
236 Length
= End
- Buffer
;
237 if (*AddressStringLength
> Length
)
239 Status
= RtlStringCchCopyA(AddressString
,
240 *AddressStringLength
,
242 ASSERT(Status
== STATUS_SUCCESS
);
243 *AddressStringLength
= Length
+ 1;
244 return STATUS_SUCCESS
;
247 *AddressStringLength
= Length
+ 1;
248 return STATUS_INVALID_PARAMETER
;
256 RtlIpv4AddressToStringW(
257 _In_
const struct in_addr
*Addr
,
258 _Out_writes_(IPV4_ADDR_STRING_MAX_LEN
) PWCHAR S
)
266 Status
= RtlStringCchPrintfExW(S
,
267 IPV4_ADDR_STRING_MAX_LEN
,
272 Addr
->S_un
.S_un_b
.s_b1
,
273 Addr
->S_un
.S_un_b
.s_b2
,
274 Addr
->S_un
.S_un_b
.s_b3
,
275 Addr
->S_un
.S_un_b
.s_b4
);
276 ASSERT(Status
== STATUS_SUCCESS
);
277 if (!NT_SUCCESS(Status
))
288 RtlIpv4AddressToStringExW(
289 _In_
const struct in_addr
*Address
,
291 _Out_writes_to_(*AddressStringLength
, *AddressStringLength
) PWCHAR AddressString
,
292 _Inout_ PULONG AddressStringLength
)
294 WCHAR Buffer
[IPV4_ADDR_STRING_MAX_LEN
+ IPV4_PORT_STRING_MAX_LEN
];
299 if (!Address
|| !AddressString
|| !AddressStringLength
)
300 return STATUS_INVALID_PARAMETER
;
302 Status
= RtlStringCchPrintfExW(Buffer
,
303 RTL_NUMBER_OF(Buffer
),
307 Port
? L
"%u.%u.%u.%u:%u"
309 Address
->S_un
.S_un_b
.s_b1
,
310 Address
->S_un
.S_un_b
.s_b2
,
311 Address
->S_un
.S_un_b
.s_b3
,
312 Address
->S_un
.S_un_b
.s_b4
,
314 ASSERT(Status
== STATUS_SUCCESS
);
315 if (!NT_SUCCESS(Status
))
316 return STATUS_INVALID_PARAMETER
;
318 Length
= End
- AddressString
;
319 if (*AddressStringLength
> Length
)
321 Status
= RtlStringCchCopyW(AddressString
,
322 *AddressStringLength
,
324 ASSERT(Status
== STATUS_SUCCESS
);
325 *AddressStringLength
= Length
+ 1;
326 return STATUS_SUCCESS
;
329 *AddressStringLength
= Length
+ 1;
330 return STATUS_INVALID_PARAMETER
;
338 RtlIpv4StringToAddressA(
341 _Out_ PCSTR
*Terminator
,
342 _Out_
struct in_addr
*Addr
)
345 ANSI_STRING AddressA
;
346 UNICODE_STRING AddressW
;
347 PCWSTR TerminatorW
= NULL
;
349 Status
= RtlInitAnsiStringEx(&AddressA
, String
);
350 if (!NT_SUCCESS(Status
))
353 Status
= RtlAnsiStringToUnicodeString(&AddressW
, &AddressA
, TRUE
);
354 if (!NT_SUCCESS(Status
))
357 Status
= RtlIpv4StringToAddressW(AddressW
.Buffer
,
362 ASSERT(TerminatorW
>= AddressW
.Buffer
);
363 *Terminator
= String
+ (TerminatorW
- AddressW
.Buffer
);
365 RtlFreeUnicodeString(&AddressW
);
375 RtlIpv4StringToAddressExA(
376 _In_ PCSTR AddressString
,
378 _Out_
struct in_addr
*Address
,
382 ANSI_STRING AddressA
;
383 UNICODE_STRING AddressW
;
385 Status
= RtlInitAnsiStringEx(&AddressA
, AddressString
);
386 if (!NT_SUCCESS(Status
))
389 Status
= RtlAnsiStringToUnicodeString(&AddressW
, &AddressA
, TRUE
);
390 if (!NT_SUCCESS(Status
))
393 Status
= RtlIpv4StringToAddressExW(AddressW
.Buffer
, Strict
, Address
, Port
);
395 RtlFreeUnicodeString(&AddressW
);
405 RtlIpv4StringToAddressW(
408 _Out_ PCWSTR
*Terminator
,
409 _Out_
struct in_addr
*Addr
)
417 Status
= RtlpIpv4StringToAddressParserW(String
,
422 if (Strict
&& Parts
< 4)
423 Status
= STATUS_INVALID_PARAMETER
;
425 if (!NT_SUCCESS(Status
))
428 /* Combine the parts */
429 Result
= Values
[Parts
- 1];
430 for (i
= 0; i
< Parts
- 1; i
++)
432 INT Shift
= CHAR_BIT
* (3 - i
);
434 if (Values
[i
] > 0xFF || (Result
& (0xFF << Shift
)) != 0)
436 return STATUS_INVALID_PARAMETER
;
438 Result
|= Values
[i
] << Shift
;
441 Addr
->S_un
.S_addr
= RtlUlongByteSwap(Result
);
450 RtlIpv4StringToAddressExW(
451 _In_ PCWSTR AddressString
,
453 _Out_
struct in_addr
*Address
,
460 if (!AddressString
|| !Address
|| !Port
)
461 return STATUS_INVALID_PARAMETER
;
463 Status
= RtlIpv4StringToAddressW(AddressString
,
467 if (!NT_SUCCESS(Status
))
473 return STATUS_SUCCESS
;
476 if (*CurrentChar
!= L
':')
477 return STATUS_INVALID_PARAMETER
;
480 Status
= RtlpStringToUlong(CurrentChar
,
484 if (!NT_SUCCESS(Status
))
487 if (*CurrentChar
|| !ConvertedPort
|| ConvertedPort
> 0xffff)
488 return STATUS_INVALID_PARAMETER
;
490 *Port
= WN2H(ConvertedPort
);
491 return STATUS_SUCCESS
;
499 RtlIpv6AddressToStringA(
500 _In_
const struct in6_addr
*Addr
,
501 _Out_writes_(RTLIPV6A2S_MAX_LEN
) PSTR S
)
503 WCHAR Buffer
[RTLIPV6A2S_MAX_LEN
];
511 Result
= RtlIpv6AddressToStringW(Addr
, Buffer
);
512 if (Result
== (PWSTR
)~0)
515 ASSERT(Result
>= Buffer
);
516 ASSERT(Result
< Buffer
+ RTL_NUMBER_OF(Buffer
));
518 Status
= RtlUnicodeToMultiByteN(S
, RTLIPV6A2S_MAX_LEN
, NULL
, Buffer
, (wcslen(Buffer
) + 1) * sizeof(WCHAR
));
519 if (!NT_SUCCESS(Status
))
522 return S
+ strlen(S
);
530 RtlIpv6AddressToStringExA(
531 _In_
const struct in6_addr
*Address
,
534 _Out_writes_to_(*AddressStringLength
, *AddressStringLength
) PSTR AddressString
,
535 _Inout_ PULONG AddressStringLength
)
537 WCHAR Buffer
[RTLIPV6A2SEX_MAX_LEN
];
540 if (!Address
|| !AddressString
|| !AddressStringLength
)
541 return STATUS_INVALID_PARAMETER
;
543 Status
= RtlIpv6AddressToStringExW(Address
, ScopeId
, Port
, Buffer
, AddressStringLength
);
544 if (!NT_SUCCESS(Status
))
547 Status
= RtlUnicodeToMultiByteN(AddressString
, RTLIPV6A2SEX_MAX_LEN
, NULL
, Buffer
, (wcslen(Buffer
) + 1) * sizeof(WCHAR
));
548 if (!NT_SUCCESS(Status
))
549 return STATUS_INVALID_PARAMETER
;
551 return STATUS_SUCCESS
;
559 RtlIpv6AddressToStringW(
560 _In_
const struct in6_addr
*Addr
,
561 _Out_writes_(RTLIPV6A2S_MAX_LEN
) PWSTR S
)
565 BOOLEAN SkipOnce
= TRUE
;
572 Remaining
= RTLIPV6A2S_MAX_LEN
;
573 /* does it look like an ipv4 address contained in an ipv6? http://tools.ietf.org/html/rfc2765 */
574 if (!Addr
->s6_words
[0] && !Addr
->s6_words
[1] && !Addr
->s6_words
[2] && !Addr
->s6_words
[3] && Addr
->s6_words
[6])
577 if (Addr
->s6_words
[4] == 0xffff && !Addr
->s6_words
[5])
579 else if (!Addr
->s6_words
[4] && Addr
->s6_words
[5] == 0xffff)
581 else if (!Addr
->s6_words
[4] && !Addr
->s6_words
[5])
585 Status
= RtlStringCchPrintfExW(S
,
596 ASSERT(Status
== STATUS_SUCCESS
);
597 if (!NT_SUCCESS(Status
))
603 /* does it look like an ISATAP address? http://tools.ietf.org/html/rfc5214 */
604 if (!(Addr
->s6_words
[4] & 0xfffd) && Addr
->s6_words
[5] == 0xfe5e)
607 for (n
= 0; n
< Parts
; ++n
)
609 if (SkipOnce
&& ((n
+ 1) < Parts
) && !Addr
->s6_words
[n
] && !Addr
->s6_words
[n
+ 1])
612 while (!Addr
->s6_words
[n
+ 1] && (n
+ 1) < Parts
)
616 if ((n
+ 1) >= Parts
)
629 Status
= RtlStringCchPrintfExW(S
,
635 WN2H(Addr
->s6_words
[n
]));
636 ASSERT(Status
== STATUS_SUCCESS
);
637 if (!NT_SUCCESS(Status
))
644 Status
= RtlStringCchPrintfExW(S
,
654 ASSERT(Status
== STATUS_SUCCESS
);
655 if (!NT_SUCCESS(Status
))
669 RtlIpv6AddressToStringExW(
670 _In_
const struct in6_addr
*Address
,
673 _Out_writes_to_(*AddressStringLength
, *AddressStringLength
) PWCHAR AddressString
,
674 _Inout_ PULONG AddressStringLength
)
676 WCHAR Buffer
[RTLIPV6A2SEX_MAX_LEN
];
682 if (!Address
|| !AddressString
|| !AddressStringLength
)
683 return STATUS_INVALID_PARAMETER
;
688 S
= RtlIpv6AddressToStringW(Address
, S
);
689 ASSERT(S
!= (PCWSTR
)~0);
691 return STATUS_INVALID_PARAMETER
;
694 ASSERT(S
<= Buffer
+ RTLIPV6A2S_MAX_LEN
+ 1);
695 Remaining
= RTL_NUMBER_OF(Buffer
) - (S
- Buffer
);
696 ASSERT(Remaining
>= RTLIPV6A2SEX_MAX_LEN
- RTLIPV6A2S_MAX_LEN
);
700 Status
= RtlStringCchPrintfExW(S
,
707 ASSERT(Status
== STATUS_SUCCESS
);
708 if (!NT_SUCCESS(Status
))
709 return STATUS_INVALID_PARAMETER
;
714 Status
= RtlStringCchPrintfExW(S
,
721 ASSERT(Status
== STATUS_SUCCESS
);
722 if (!NT_SUCCESS(Status
))
723 return STATUS_INVALID_PARAMETER
;
727 ASSERT(Buffer
[Length
] == UNICODE_NULL
);
728 if (*AddressStringLength
> Length
)
730 Status
= RtlStringCchCopyW(AddressString
, *AddressStringLength
, Buffer
);
731 ASSERT(Status
== STATUS_SUCCESS
);
732 *AddressStringLength
= Length
+ 1;
733 return STATUS_SUCCESS
;
736 *AddressStringLength
= Length
+ 1;
737 return STATUS_INVALID_PARAMETER
;
745 RtlIpv6StringToAddressA(
747 _Out_ PCSTR
*Terminator
,
748 _Out_
struct in6_addr
*Addr
)
752 UNICODE_STRING StringW
;
753 PCWSTR TerminatorW
= NULL
;
755 Status
= RtlInitAnsiStringEx(&StringA
, String
);
756 if (!NT_SUCCESS(Status
))
759 Status
= RtlAnsiStringToUnicodeString(&StringW
, &StringA
, TRUE
);
760 if (!NT_SUCCESS(Status
))
763 Status
= RtlIpv6StringToAddressW(StringW
.Buffer
, &TerminatorW
, Addr
);
764 /* on windows the terminator is not always written, so we mimic that behavior. */
766 *Terminator
= String
+ (TerminatorW
- StringW
.Buffer
);
768 RtlFreeUnicodeString(&StringW
);
777 RtlIpv6StringToAddressExA(
778 _In_ PCSTR AddressString
,
779 _Out_
struct in6_addr
*Address
,
780 _Out_ PULONG ScopeId
,
784 ANSI_STRING AddressA
;
785 UNICODE_STRING AddressW
;
787 Status
= RtlInitAnsiStringEx(&AddressA
, AddressString
);
788 if (!NT_SUCCESS(Status
))
791 Status
= RtlAnsiStringToUnicodeString(&AddressW
, &AddressA
, TRUE
);
792 if (!NT_SUCCESS(Status
))
795 Status
= RtlIpv6StringToAddressExW(AddressW
.Buffer
, Address
, ScopeId
, Port
);
797 RtlFreeUnicodeString(&AddressW
);
806 RtlIpv6StringToAddressW(
808 _Out_ PCWSTR
*Terminator
,
809 _Out_
struct in6_addr
*Addr
)
812 INT StartSkip
= -1, Parts
= 0;
814 NTSTATUS Status
= STATUS_SUCCESS
;
815 enum { None
, Number
, Colon
, DoubleColon
} Last
= None
;
816 BOOLEAN SkipoutLastHex
= FALSE
;
818 if (!String
|| !Terminator
|| !Addr
)
819 return STATUS_INVALID_PARAMETER
;
823 Len
= RtlpClassifyChars(String
, &Base
);
826 /* not a number, and no ':' or already encountered an ':' */
827 if (String
[0] != ':' || Last
== Colon
)
830 /* double colon, 1 or more fields set to 0. mark the position, and move on. */
831 if (StartSkip
== -1 && String
[1] == ':')
833 /* this case was observed in windows, but it does not seem correct. */
836 Addr
->s6_words
[n
++] = 0;
837 Addr
->s6_words
[n
++] = 0;
843 else if (String
[1] == ':' || Last
!= Number
)
845 /* a double colon after we already encountered one, or a the last parsed item was not a number. */
856 /* it seems that when encountering more than 4 chars, the terminator is not updated,
857 unless the previously encountered item is a double colon.... */
858 Status
= STATUS_INVALID_PARAMETER
;
859 if (Last
!= DoubleColon
)
867 if (String
[Len
] == '.' && n
<= 6)
871 /* this could be an ipv4 address inside an ipv6 address http://tools.ietf.org/html/rfc2765 */
873 Status
= RtlpIpv4StringToAddressParserW(String
, TRUE
, &String
, Values
, &PartsV4
);
874 for(j
= 0; j
< PartsV4
; ++j
)
878 Status
= STATUS_INVALID_PARAMETER
;
883 if ((j
== PartsV4
- 1) &&
885 (*String
== ':' && StartSkip
== -1) ||
886 (StartSkip
== -1 && n
< 6) ||
887 Status
== STATUS_INVALID_PARAMETER
))
889 Status
= STATUS_INVALID_PARAMETER
;
892 Addr
->s6_bytes
[n
* 2 + j
] = Values
[j
] & 0xff;
894 /* mark enough parts as converted in case we are the last item to be converted */
896 /* mark 2 parts as converted in case we are not the last item, and we encountered a double colon. */
901 if (String
[Len
] != ':' && n
< 7 && StartSkip
== -1)
903 /* if we decoded atleast some numbers, update the terminator to point to the first invalid char */
906 Status
= STATUS_INVALID_PARAMETER
;
910 if (Len
== 1 && towlower(String
[Len
]) == 'x' && String
[0] == '0')
912 Len
= RtlpClassifyChars(String
+ 2, &Base
);
913 if (Len
> 0 && Len
<= 4)
915 *Terminator
= String
+ 1;
917 SkipoutLastHex
= TRUE
;
921 Status
= RtlpStringToUlongBase(String
, 16, &String
, &Value
);
922 if (!NT_SUCCESS(Status
))
927 Addr
->s6_words
[n
++] = WN2H(Value
);
934 if (StartSkip
!= -1 && Status
!= STATUS_INVALID_PARAMETER
&& Last
!= Colon
)
936 /* we found a '::' somewhere, so expand that. */
937 memmove(&Addr
->s6_words
[8-Parts
], &Addr
->s6_words
[StartSkip
], Parts
* sizeof(Addr
->s6_words
[0]));
938 memset(&Addr
->s6_words
[StartSkip
], 0, (8-StartSkip
-Parts
) * sizeof(Addr
->s6_words
[0]));
942 /* we have already set the terminator */
944 return n
< 8 ? STATUS_INVALID_PARAMETER
: Status
;
945 *Terminator
= String
;
946 return n
< 8 ? STATUS_INVALID_PARAMETER
: Status
;
954 RtlIpv6StringToAddressExW(
955 _In_ PCWSTR AddressString
,
956 _Out_
struct in6_addr
*Address
,
957 _Out_ PULONG ScopeId
,
961 ULONG ConvertedPort
= 0, ConvertedScope
= 0;
962 if (!AddressString
|| !Address
|| !ScopeId
|| !Port
)
963 return STATUS_INVALID_PARAMETER
;
965 if (*AddressString
== '[')
970 Status
= RtlIpv6StringToAddressW(AddressString
, &AddressString
, Address
);
971 if (!NT_SUCCESS(Status
))
972 return STATUS_INVALID_PARAMETER
;
974 if (*AddressString
== '%')
977 Status
= RtlpStringToUlongBase(AddressString
, 10, &AddressString
, &ConvertedScope
);
978 if (!NT_SUCCESS(Status
))
979 return STATUS_INVALID_PARAMETER
;
981 else if (*AddressString
&& !(ConvertedPort
&& *AddressString
== ']'))
983 return STATUS_INVALID_PARAMETER
;
988 if (*AddressString
++ !=']')
989 return STATUS_INVALID_PARAMETER
;
990 if (*AddressString
==':')
993 if (*++AddressString
== '0')
995 if (towlower(*++AddressString
) != 'x')
996 return STATUS_INVALID_PARAMETER
;
1000 Status
= RtlpStringToUlongBase(AddressString
, Base
, &AddressString
, &ConvertedPort
);
1001 if (!NT_SUCCESS(Status
) || ConvertedPort
> 0xffff)
1002 return STATUS_INVALID_PARAMETER
;
1010 if (*AddressString
== 0)
1012 *ScopeId
= ConvertedScope
;
1013 *Port
= WN2H(ConvertedPort
);
1014 return STATUS_SUCCESS
;
1016 return STATUS_INVALID_PARAMETER
;