xref: /reactos/sdk/lib/rtl/network.c (revision 56229b7a)
1 /*
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)
7  */
8 
9 /* INCLUDES *****************************************************************/
10 
11 #include <rtl.h>
12 #include <ntstrsafe.h>
13 #define NDEBUG
14 #include <debug.h>
15 
16 /* maximum length of an ipv4 address expressed as a string */
17 #define IPV4_ADDR_STRING_MAX_LEN 16
18 
19 /* maximum length of an ipv4 port expressed as a string */
20 #define IPV4_PORT_STRING_MAX_LEN 7 /* with the leading ':' */
21 
22 /* maximum length of an ipv6 string for RtlIpv6AddressToString */
23 #define RTLIPV6A2S_MAX_LEN 46
24 
25 /* maximum length of an ipv6 string with scope and port for RtlIpv6AddressToStringEx */
26 #define RTLIPV6A2SEX_MAX_LEN 65
27 
28 /* network to host order conversion for little endian machines */
29 #define WN2H(w) (((w & 0xFF00) >> 8) | ((w & 0x00FF) << 8))
30 
31 /* PRIVATE FUNCTIONS **********************************************************/
32 
33 /* decode a string with given Base (8, 10 or 16) */
34 static
35 NTSTATUS
RtlpStringToUlongBase(_In_ PCWSTR String,_In_ ULONG Base,_Out_ PCWSTR * Terminator,_Out_ PULONG Out)36 RtlpStringToUlongBase(
37     _In_ PCWSTR String,
38     _In_ ULONG Base,
39     _Out_ PCWSTR *Terminator,
40     _Out_ PULONG Out)
41 {
42     NTSTATUS Status = STATUS_INVALID_PARAMETER;
43     ULONG Result = 0;
44     ULONG Digit;
45 
46     while (1)
47     {
48         Digit = towlower(*String);
49         if (isdigit(Digit) && (Base >= 10 || Digit <= L'7'))
50             Digit -= L'0';
51         else if (Digit >= L'a' && Digit <= L'f' && Base >= 16)
52             Digit -= (L'a' - 10);
53         else
54             break;
55 
56         Status = RtlULongMult(Result, Base, &Result);
57         if (!NT_SUCCESS(Status))
58         {
59             Status = STATUS_INVALID_PARAMETER;
60             break;
61         }
62 
63         Status = RtlULongAdd(Result, Digit, &Result);
64         if (!NT_SUCCESS(Status))
65         {
66             Status = STATUS_INVALID_PARAMETER;
67             break;
68         }
69         String++;
70     }
71 
72     *Terminator = String;
73     *Out = Result;
74     return Status;
75 }
76 
77 
78 static
79 NTSTATUS
RtlpStringToUlong(_In_ PCWSTR String,_In_ BOOLEAN Strict,_Out_ PCWSTR * Terminator,_Out_ PULONG Out)80 RtlpStringToUlong(
81     _In_ PCWSTR String,
82     _In_ BOOLEAN Strict,
83     _Out_ PCWSTR *Terminator,
84     _Out_ PULONG Out)
85 {
86     ULONG Base = 10;
87 
88     if (String[0] == L'0')
89     {
90         if (String[1] == L'x' || String[1] == L'X')
91         {
92             /* 0x/0X prefix -- hex */
93             String += 2;
94             Base = 16;
95         }
96         else if (String[1] >= L'0' && String[1] <= L'9')
97         {
98             /* 0 prefix -- octal */
99             String++;
100             Base = 8;
101         }
102     }
103 
104     /* Strict forbids anything but decimal */
105     if (Strict && Base != 10)
106     {
107         *Terminator = String;
108         return STATUS_INVALID_PARAMETER;
109     }
110     return RtlpStringToUlongBase(String, Base, Terminator, Out);
111 }
112 
113 /* Worker function to extract the ipv4 part of a string. */
114 NTSTATUS
115 NTAPI
116 RtlpIpv4StringToAddressParserW(
117     _In_ PCWSTR String,
118     _In_ BOOLEAN Strict,
119     _Out_ PCWSTR *Terminator,
120     _Out_writes_(4) ULONG *Values,
121     _Out_ INT *Parts)
122 {
123     NTSTATUS Status;
124     *Parts = 0;
125     do
126     {
127         Status = RtlpStringToUlong(String, Strict, &String, &Values[*Parts]);
128         (*Parts)++;
129 
130         if (*String != L'.')
131             break;
132 
133         /* Already four parts, but a dot follows? */
134         if (*Parts == 4)
135         {
136             Status = STATUS_INVALID_PARAMETER;
137             break;
138         }
139         /* Skip the dot */
140         String++;
141     } while (NT_SUCCESS(Status));
142 
143     *Terminator = String;
144     return Status;
145 }
146 
147 /* PUBLIC FUNCTIONS ***********************************************************/
148 
149 /*
150  * @implemented
151  */
152 PSTR
153 NTAPI
RtlIpv4AddressToStringA(_In_ const struct in_addr * Addr,_Out_writes_ (IPV4_ADDR_STRING_MAX_LEN)PCHAR S)154 RtlIpv4AddressToStringA(
155     _In_ const struct in_addr *Addr,
156     _Out_writes_(IPV4_ADDR_STRING_MAX_LEN) PCHAR S)
157 {
158     NTSTATUS Status;
159     PSTR End;
160 
161     if (!S)
162         return (PSTR)~0;
163 
164     Status = RtlStringCchPrintfExA(S,
165                                    IPV4_ADDR_STRING_MAX_LEN,
166                                    &End,
167                                    NULL,
168                                    0,
169                                    "%u.%u.%u.%u",
170                                    Addr->S_un.S_un_b.s_b1,
171                                    Addr->S_un.S_un_b.s_b2,
172                                    Addr->S_un.S_un_b.s_b3,
173                                    Addr->S_un.S_un_b.s_b4);
174     ASSERT(Status == STATUS_SUCCESS);
175     if (!NT_SUCCESS(Status))
176         return (PSTR)~0;
177 
178     return End;
179 }
180 
181 /*
182  * @implemented
183  */
184 NTSTATUS
185 NTAPI
RtlIpv4AddressToStringExA(_In_ const struct in_addr * Address,_In_ USHORT Port,_Out_writes_to_ (* AddressStringLength,* AddressStringLength)PCHAR AddressString,_Inout_ PULONG AddressStringLength)186 RtlIpv4AddressToStringExA(
187     _In_ const struct in_addr *Address,
188     _In_ USHORT Port,
189     _Out_writes_to_(*AddressStringLength, *AddressStringLength) PCHAR AddressString,
190     _Inout_ PULONG AddressStringLength)
191 {
192     CHAR Buffer[IPV4_ADDR_STRING_MAX_LEN + IPV4_PORT_STRING_MAX_LEN];
193     NTSTATUS Status;
194     ULONG Length;
195     PSTR End;
196 
197     if (!Address || !AddressString || !AddressStringLength)
198         return STATUS_INVALID_PARAMETER;
199 
200     Status = RtlStringCchPrintfExA(Buffer,
201                                    RTL_NUMBER_OF(Buffer),
202                                    &End,
203                                    NULL,
204                                    0,
205                                    Port ? "%u.%u.%u.%u:%u"
206                                         : "%u.%u.%u.%u",
207                                    Address->S_un.S_un_b.s_b1,
208                                    Address->S_un.S_un_b.s_b2,
209                                    Address->S_un.S_un_b.s_b3,
210                                    Address->S_un.S_un_b.s_b4,
211                                    WN2H(Port));
212     ASSERT(Status == STATUS_SUCCESS);
213     if (!NT_SUCCESS(Status))
214         return STATUS_INVALID_PARAMETER;
215 
216     Length = End - Buffer;
217     if (*AddressStringLength > Length)
218     {
219         Status = RtlStringCchCopyA(AddressString,
220                                    *AddressStringLength,
221                                    Buffer);
222         ASSERT(Status == STATUS_SUCCESS);
223         *AddressStringLength = Length + 1;
224         return STATUS_SUCCESS;
225     }
226 
227     *AddressStringLength = Length + 1;
228     return STATUS_INVALID_PARAMETER;
229 }
230 
231 /*
232  * @implemented
233  */
234 PWSTR
235 NTAPI
RtlIpv4AddressToStringW(_In_ const struct in_addr * Addr,_Out_writes_ (IPV4_ADDR_STRING_MAX_LEN)PWCHAR S)236 RtlIpv4AddressToStringW(
237     _In_ const struct in_addr *Addr,
238     _Out_writes_(IPV4_ADDR_STRING_MAX_LEN) PWCHAR S)
239 {
240     NTSTATUS Status;
241     PWSTR End;
242 
243     if (!S)
244         return (PWSTR)~0;
245 
246     Status = RtlStringCchPrintfExW(S,
247                                    IPV4_ADDR_STRING_MAX_LEN,
248                                    &End,
249                                    NULL,
250                                    0,
251                                    L"%u.%u.%u.%u",
252                                    Addr->S_un.S_un_b.s_b1,
253                                    Addr->S_un.S_un_b.s_b2,
254                                    Addr->S_un.S_un_b.s_b3,
255                                    Addr->S_un.S_un_b.s_b4);
256     ASSERT(Status == STATUS_SUCCESS);
257     if (!NT_SUCCESS(Status))
258         return (PWSTR)~0;
259 
260     return End;
261 }
262 
263 /*
264  * @implemented
265  */
266 NTSTATUS
267 NTAPI
RtlIpv4AddressToStringExW(_In_ const struct in_addr * Address,_In_ USHORT Port,_Out_writes_to_ (* AddressStringLength,* AddressStringLength)PWCHAR AddressString,_Inout_ PULONG AddressStringLength)268 RtlIpv4AddressToStringExW(
269     _In_ const struct in_addr *Address,
270     _In_ USHORT Port,
271     _Out_writes_to_(*AddressStringLength, *AddressStringLength) PWCHAR AddressString,
272     _Inout_ PULONG AddressStringLength)
273 {
274     WCHAR Buffer[IPV4_ADDR_STRING_MAX_LEN + IPV4_PORT_STRING_MAX_LEN];
275     NTSTATUS Status;
276     ULONG Length;
277     PWSTR End;
278 
279     if (!Address || !AddressString || !AddressStringLength)
280         return STATUS_INVALID_PARAMETER;
281 
282     Status = RtlStringCchPrintfExW(Buffer,
283                                    RTL_NUMBER_OF(Buffer),
284                                    &End,
285                                    NULL,
286                                    0,
287                                    Port ? L"%u.%u.%u.%u:%u"
288                                         : L"%u.%u.%u.%u",
289                                    Address->S_un.S_un_b.s_b1,
290                                    Address->S_un.S_un_b.s_b2,
291                                    Address->S_un.S_un_b.s_b3,
292                                    Address->S_un.S_un_b.s_b4,
293                                    WN2H(Port));
294     ASSERT(Status == STATUS_SUCCESS);
295     if (!NT_SUCCESS(Status))
296         return STATUS_INVALID_PARAMETER;
297 
298     Length = End - AddressString;
299     if (*AddressStringLength > Length)
300     {
301         Status = RtlStringCchCopyW(AddressString,
302                                    *AddressStringLength,
303                                    Buffer);
304         ASSERT(Status == STATUS_SUCCESS);
305         *AddressStringLength = Length + 1;
306         return STATUS_SUCCESS;
307     }
308 
309     *AddressStringLength = Length + 1;
310     return STATUS_INVALID_PARAMETER;
311 }
312 
313 /*
314  * @implemented
315  */
316 NTSTATUS
317 NTAPI
RtlIpv4StringToAddressA(_In_ PCSTR String,_In_ BOOLEAN Strict,_Out_ PCSTR * Terminator,_Out_ struct in_addr * Addr)318 RtlIpv4StringToAddressA(
319     _In_ PCSTR String,
320     _In_ BOOLEAN Strict,
321     _Out_ PCSTR *Terminator,
322     _Out_ struct in_addr *Addr)
323 {
324     NTSTATUS Status;
325     ANSI_STRING AddressA;
326     UNICODE_STRING AddressW;
327     PCWSTR TerminatorW = NULL;
328 
329     Status = RtlInitAnsiStringEx(&AddressA, String);
330     if (!NT_SUCCESS(Status))
331         return Status;
332 
333     Status = RtlAnsiStringToUnicodeString(&AddressW, &AddressA, TRUE);
334     if (!NT_SUCCESS(Status))
335         return Status;
336 
337     Status = RtlIpv4StringToAddressW(AddressW.Buffer,
338                                      Strict,
339                                      &TerminatorW,
340                                      Addr);
341 
342     ASSERT(TerminatorW >= AddressW.Buffer);
343     *Terminator = String + (TerminatorW - AddressW.Buffer);
344 
345     RtlFreeUnicodeString(&AddressW);
346 
347     return Status;
348 }
349 
350 /*
351  * @implemented
352  */
353 NTSTATUS
354 NTAPI
RtlIpv4StringToAddressExA(_In_ PCSTR AddressString,_In_ BOOLEAN Strict,_Out_ struct in_addr * Address,_Out_ PUSHORT Port)355 RtlIpv4StringToAddressExA(
356     _In_ PCSTR AddressString,
357     _In_ BOOLEAN Strict,
358     _Out_ struct in_addr *Address,
359     _Out_ PUSHORT Port)
360 {
361     NTSTATUS Status;
362     ANSI_STRING AddressA;
363     UNICODE_STRING AddressW;
364 
365     Status = RtlInitAnsiStringEx(&AddressA, AddressString);
366     if (!NT_SUCCESS(Status))
367         return Status;
368 
369     Status = RtlAnsiStringToUnicodeString(&AddressW, &AddressA, TRUE);
370     if (!NT_SUCCESS(Status))
371         return Status;
372 
373     Status = RtlIpv4StringToAddressExW(AddressW.Buffer, Strict, Address, Port);
374 
375     RtlFreeUnicodeString(&AddressW);
376 
377     return Status;
378 }
379 
380 /*
381  * @implemented
382  */
383 NTSTATUS
384 NTAPI
RtlIpv4StringToAddressW(_In_ PCWSTR String,_In_ BOOLEAN Strict,_Out_ PCWSTR * Terminator,_Out_ struct in_addr * Addr)385 RtlIpv4StringToAddressW(
386     _In_ PCWSTR String,
387     _In_ BOOLEAN Strict,
388     _Out_ PCWSTR *Terminator,
389     _Out_ struct in_addr *Addr)
390 {
391     NTSTATUS Status;
392     ULONG Values[4];
393     ULONG Result;
394     INT Parts = 0;
395     INT i;
396 
397     Status = RtlpIpv4StringToAddressParserW(String,
398                                             Strict,
399                                             Terminator,
400                                             Values,
401                                             &Parts);
402     if (Strict && Parts < 4)
403         Status = STATUS_INVALID_PARAMETER;
404 
405     if (!NT_SUCCESS(Status))
406         return Status;
407 
408     /* Combine the parts */
409     Result = Values[Parts - 1];
410     for (i = 0; i < Parts - 1; i++)
411     {
412         INT Shift = CHAR_BIT * (3 - i);
413 
414         if (Values[i] > 0xFF || (Result & (0xFF << Shift)) != 0)
415         {
416             return STATUS_INVALID_PARAMETER;
417         }
418         Result |= Values[i] << Shift;
419     }
420 
421     Addr->S_un.S_addr = RtlUlongByteSwap(Result);
422     return Status;
423 }
424 
425 /*
426 * @implemented
427 */
428 NTSTATUS
429 NTAPI
RtlIpv4StringToAddressExW(_In_ PCWSTR AddressString,_In_ BOOLEAN Strict,_Out_ struct in_addr * Address,_Out_ PUSHORT Port)430 RtlIpv4StringToAddressExW(
431     _In_ PCWSTR AddressString,
432     _In_ BOOLEAN Strict,
433     _Out_ struct in_addr *Address,
434     _Out_ PUSHORT Port)
435 {
436     PCWSTR CurrentChar;
437     ULONG ConvertedPort;
438     NTSTATUS Status;
439 
440     if (!AddressString || !Address || !Port)
441         return STATUS_INVALID_PARAMETER;
442 
443     Status = RtlIpv4StringToAddressW(AddressString,
444                                      Strict,
445                                      &CurrentChar,
446                                      Address);
447     if (!NT_SUCCESS(Status))
448         return Status;
449 
450     if (!*CurrentChar)
451     {
452         *Port = 0;
453         return STATUS_SUCCESS;
454     }
455 
456     if (*CurrentChar != L':')
457         return STATUS_INVALID_PARAMETER;
458     ++CurrentChar;
459 
460     Status = RtlpStringToUlong(CurrentChar,
461                                FALSE,
462                                &CurrentChar,
463                                &ConvertedPort);
464     if (!NT_SUCCESS(Status))
465         return Status;
466 
467     if (*CurrentChar || !ConvertedPort || ConvertedPort > 0xffff)
468         return STATUS_INVALID_PARAMETER;
469 
470     *Port = WN2H(ConvertedPort);
471     return STATUS_SUCCESS;
472 }
473 
474 /*
475 * @implemented
476 */
477 PSTR
478 NTAPI
RtlIpv6AddressToStringA(_In_ const struct in6_addr * Addr,_Out_writes_ (RTLIPV6A2S_MAX_LEN)PSTR S)479 RtlIpv6AddressToStringA(
480     _In_ const struct in6_addr *Addr,
481     _Out_writes_(RTLIPV6A2S_MAX_LEN) PSTR S)
482 {
483     WCHAR Buffer[RTLIPV6A2S_MAX_LEN];
484     PWSTR Result;
485     NTSTATUS Status;
486 
487     if (!S)
488         return (PSTR)~0;
489 
490     Buffer[0] = 0;
491     Result = RtlIpv6AddressToStringW(Addr, Buffer);
492     if (Result == (PWSTR)~0)
493         return (PSTR)~0;
494 
495     ASSERT(Result >= Buffer);
496     ASSERT(Result < Buffer + RTL_NUMBER_OF(Buffer));
497 
498     Status = RtlUnicodeToMultiByteN(S, RTLIPV6A2S_MAX_LEN, NULL, Buffer, (ULONG)(wcslen(Buffer) + 1) * sizeof(WCHAR));
499     if (!NT_SUCCESS(Status))
500         return (PSTR)~0;
501 
502     return S + strlen(S);
503 }
504 
505 /*
506 * @implemented
507 */
508 NTSTATUS
509 NTAPI
RtlIpv6AddressToStringExA(_In_ const struct in6_addr * Address,_In_ ULONG ScopeId,_In_ USHORT Port,_Out_writes_to_ (* AddressStringLength,* AddressStringLength)PSTR AddressString,_Inout_ PULONG AddressStringLength)510 RtlIpv6AddressToStringExA(
511     _In_ const struct in6_addr *Address,
512     _In_ ULONG ScopeId,
513     _In_ USHORT Port,
514     _Out_writes_to_(*AddressStringLength, *AddressStringLength) PSTR AddressString,
515     _Inout_ PULONG AddressStringLength)
516 {
517     WCHAR Buffer[RTLIPV6A2SEX_MAX_LEN];
518     NTSTATUS Status;
519 
520     if (!Address || !AddressString || !AddressStringLength)
521         return STATUS_INVALID_PARAMETER;
522 
523     Status = RtlIpv6AddressToStringExW(Address, ScopeId, Port, Buffer, AddressStringLength);
524     if (!NT_SUCCESS(Status))
525         return Status;
526 
527     Status = RtlUnicodeToMultiByteN(AddressString, RTLIPV6A2SEX_MAX_LEN, NULL, Buffer, (*AddressStringLength + 1) * sizeof(WCHAR));
528     if (!NT_SUCCESS(Status))
529         return STATUS_INVALID_PARAMETER;
530 
531     return STATUS_SUCCESS;
532 }
533 
534 /*
535 * @implemented
536 */
537 PWSTR
538 NTAPI
RtlIpv6AddressToStringW(_In_ const struct in6_addr * Addr,_Out_writes_ (RTLIPV6A2S_MAX_LEN)PWSTR S)539 RtlIpv6AddressToStringW(
540     _In_ const struct in6_addr *Addr,
541     _Out_writes_(RTLIPV6A2S_MAX_LEN) PWSTR S)
542 {
543     NTSTATUS Status;
544     UINT Parts = 8, n;
545     BOOLEAN SkipOnce = TRUE;
546     PWSTR End;
547     size_t Remaining;
548 
549     if (!S)
550         return (PWSTR)~0;
551 
552     Remaining = RTLIPV6A2S_MAX_LEN;
553     /* does it look like an ipv4 address contained in an ipv6? http://tools.ietf.org/html/rfc2765 */
554     if (!Addr->s6_words[0] && !Addr->s6_words[1] && !Addr->s6_words[2] && !Addr->s6_words[3] && Addr->s6_words[6])
555     {
556         PWSTR Prefix = NULL;
557         if (Addr->s6_words[4] == 0xffff && !Addr->s6_words[5])
558             Prefix = L"ffff:0:";
559         else if (!Addr->s6_words[4] && Addr->s6_words[5] == 0xffff)
560             Prefix = L"ffff:";
561         else if (!Addr->s6_words[4] && !Addr->s6_words[5])
562             Prefix = L"";
563         if (Prefix != NULL)
564         {
565             Status = RtlStringCchPrintfExW(S,
566                                            Remaining,
567                                            &End,
568                                            NULL,
569                                            0,
570                                            L"::%ls%u.%u.%u.%u",
571                                            Prefix,
572                                            Addr->s6_bytes[12],
573                                            Addr->s6_bytes[13],
574                                            Addr->s6_bytes[14],
575                                            Addr->s6_bytes[15]);
576             ASSERT(Status == STATUS_SUCCESS);
577             if (!NT_SUCCESS(Status))
578                 return (PWSTR)~0;
579             return End;
580         }
581     }
582 
583     /* does it look like an ISATAP address? http://tools.ietf.org/html/rfc5214 */
584     if (!(Addr->s6_words[4] & 0xfffd) && Addr->s6_words[5] == 0xfe5e)
585         Parts = 6;
586 
587     for (n = 0; n < Parts; ++n)
588     {
589         if (SkipOnce && ((n + 1) < Parts) && !Addr->s6_words[n] && !Addr->s6_words[n + 1])
590         {
591             SkipOnce = FALSE;
592             while (!Addr->s6_words[n + 1] && (n + 1) < Parts)
593                 ++n;
594             *S++ = ':';
595             Remaining--;
596             if ((n + 1) >= Parts)
597             {
598                 *S++ = ':';
599                 Remaining--;
600             }
601         }
602         else
603         {
604             if (n)
605             {
606                 *S++ = ':';
607                 Remaining--;
608             }
609             Status = RtlStringCchPrintfExW(S,
610                                            Remaining,
611                                            &End,
612                                            &Remaining,
613                                            0,
614                                            L"%x",
615                                            WN2H(Addr->s6_words[n]));
616             ASSERT(Status == STATUS_SUCCESS);
617             if (!NT_SUCCESS(Status))
618                 return (PWSTR)~0;
619             S = End;
620         }
621     }
622     if (Parts < 8)
623     {
624         Status = RtlStringCchPrintfExW(S,
625                                        Remaining,
626                                        &End,
627                                        NULL,
628                                        0,
629                                        L":%u.%u.%u.%u",
630                                        Addr->s6_bytes[12],
631                                        Addr->s6_bytes[13],
632                                        Addr->s6_bytes[14],
633                                        Addr->s6_bytes[15]);
634         ASSERT(Status == STATUS_SUCCESS);
635         if (!NT_SUCCESS(Status))
636             return (PWSTR)~0;
637 
638         return End;
639     }
640     *S = UNICODE_NULL;
641     return S;
642 }
643 
644 /*
645 * @implemented
646 */
647 NTSTATUS
648 NTAPI
RtlIpv6AddressToStringExW(_In_ const struct in6_addr * Address,_In_ ULONG ScopeId,_In_ USHORT Port,_Out_writes_to_ (* AddressStringLength,* AddressStringLength)PWCHAR AddressString,_Inout_ PULONG AddressStringLength)649 RtlIpv6AddressToStringExW(
650     _In_ const struct in6_addr *Address,
651     _In_ ULONG ScopeId,
652     _In_ USHORT Port,
653     _Out_writes_to_(*AddressStringLength, *AddressStringLength) PWCHAR AddressString,
654     _Inout_ PULONG AddressStringLength)
655 {
656     WCHAR Buffer[RTLIPV6A2SEX_MAX_LEN];
657     PWCHAR S = Buffer;
658     NTSTATUS Status;
659     ULONG Length;
660     size_t Remaining;
661 
662     if (!Address || !AddressString || !AddressStringLength)
663         return STATUS_INVALID_PARAMETER;
664 
665     if (Port)
666         *S++ = L'[';
667 
668     S = RtlIpv6AddressToStringW(Address, S);
669     ASSERT(S != (PCWSTR)~0);
670     if (S == (PCWSTR)~0)
671         return STATUS_INVALID_PARAMETER;
672 
673     ASSERT(S >= Buffer);
674     ASSERT(S <= Buffer + RTLIPV6A2S_MAX_LEN + 1);
675     Remaining = RTL_NUMBER_OF(Buffer) - (S - Buffer);
676     ASSERT(Remaining >= RTLIPV6A2SEX_MAX_LEN - RTLIPV6A2S_MAX_LEN);
677 
678     if (ScopeId)
679     {
680         Status = RtlStringCchPrintfExW(S,
681                                        Remaining,
682                                        &S,
683                                        &Remaining,
684                                        0,
685                                        L"%%%u",
686                                        ScopeId);
687         ASSERT(Status == STATUS_SUCCESS);
688         if (!NT_SUCCESS(Status))
689             return STATUS_INVALID_PARAMETER;
690     }
691 
692     if (Port)
693     {
694         Status = RtlStringCchPrintfExW(S,
695                                        Remaining,
696                                        &S,
697                                        &Remaining,
698                                        0,
699                                        L"]:%u",
700                                        WN2H(Port));
701         ASSERT(Status == STATUS_SUCCESS);
702         if (!NT_SUCCESS(Status))
703             return STATUS_INVALID_PARAMETER;
704     }
705 
706     Length = S - Buffer;
707     ASSERT(Buffer[Length] == UNICODE_NULL);
708     if (*AddressStringLength > Length)
709     {
710         Status = RtlStringCchCopyW(AddressString, *AddressStringLength, Buffer);
711         ASSERT(Status == STATUS_SUCCESS);
712         *AddressStringLength = Length + 1;
713         return STATUS_SUCCESS;
714     }
715 
716     *AddressStringLength = Length + 1;
717     return STATUS_INVALID_PARAMETER;
718 }
719 
720 /*
721 * @implemented
722 */
723 NTSTATUS
724 NTAPI
RtlIpv6StringToAddressA(_In_ PCSTR String,_Out_ PCSTR * Terminator,_Out_ struct in6_addr * Addr)725 RtlIpv6StringToAddressA(
726     _In_ PCSTR String,
727     _Out_ PCSTR *Terminator,
728     _Out_ struct in6_addr *Addr)
729 {
730     NTSTATUS Status;
731     ANSI_STRING StringA;
732     UNICODE_STRING StringW;
733     PCWSTR TerminatorW = NULL;
734 
735     Status = RtlInitAnsiStringEx(&StringA, String);
736     if (!NT_SUCCESS(Status))
737         return Status;
738 
739     Status = RtlAnsiStringToUnicodeString(&StringW, &StringA, TRUE);
740     if (!NT_SUCCESS(Status))
741         return Status;
742 
743     Status = RtlIpv6StringToAddressW(StringW.Buffer, &TerminatorW, Addr);
744     /* on windows the terminator is not always written, so we mimic that behavior. */
745     if (TerminatorW)
746         *Terminator = String + (TerminatorW - StringW.Buffer);
747 
748     RtlFreeUnicodeString(&StringW);
749     return Status;
750 }
751 
752 /*
753 * @implemented
754 */
755 NTSTATUS
756 NTAPI
RtlIpv6StringToAddressExA(_In_ PCSTR AddressString,_Out_ struct in6_addr * Address,_Out_ PULONG ScopeId,_Out_ PUSHORT Port)757 RtlIpv6StringToAddressExA(
758     _In_ PCSTR AddressString,
759     _Out_ struct in6_addr *Address,
760     _Out_ PULONG ScopeId,
761     _Out_ PUSHORT Port)
762 {
763     NTSTATUS Status;
764     ANSI_STRING AddressA;
765     UNICODE_STRING AddressW;
766 
767     Status = RtlInitAnsiStringEx(&AddressA, AddressString);
768     if (!NT_SUCCESS(Status))
769         return Status;
770 
771     Status = RtlAnsiStringToUnicodeString(&AddressW, &AddressA, TRUE);
772     if (!NT_SUCCESS(Status))
773         return Status;
774 
775     Status = RtlIpv6StringToAddressExW(AddressW.Buffer, Address, ScopeId, Port);
776 
777     RtlFreeUnicodeString(&AddressW);
778     return Status;
779 }
780 
781 /*
782  * RtlIpv6StringToAddress[Ex]W implementation was imported from Wine 9.6 at 20/4/2024
783  *
784  * This library is free software; you can redistribute it and/or
785  * modify it under the terms of the GNU Lesser General Public
786  * License as published by the Free Software Foundation; either
787  * version 2.1 of the License, or (at your option) any later version.
788  *
789  * For full text of the license see COPYING.LIB in the root of this project.
790  *
791  * Copyright 2020 Alex Henrie
792  */
793 
794 static const int hex_table[] = {
795     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00-0x0F */
796     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10-0x1F */
797     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x20-0x2F */
798      0,  1,  2,  3,  4,  5,  6,  7,  8,  9, -1, -1, -1, -1, -1, -1, /* 0x30-0x3F */
799     -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x40-0x4F */
800     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x50-0x5F */
801     -1, 10, 11, 12, 13, 14, 15                                      /* 0x60-0x66 */
802 };
803 
parse_ipv4_component(const WCHAR ** str,BOOL strict,ULONG * value)804 static BOOL parse_ipv4_component(const WCHAR **str, BOOL strict, ULONG *value)
805 {
806     int base = 10, d;
807     WCHAR c;
808     ULONG cur_value, prev_value = 0;
809     BOOL success = FALSE;
810 
811     if (**str == '.')
812     {
813         *str += 1;
814         return FALSE;
815     }
816 
817     if ((*str)[0] == '0')
818     {
819         if ((*str)[1] == 'x' || (*str)[1] == 'X')
820         {
821             *str += 2;
822             if (strict) return FALSE;
823             base = 16;
824         }
825         else if ((*str)[1] >= '0' && (*str)[1] <= '9')
826         {
827             *str += 1;
828             if (strict) return FALSE;
829             base = 8;
830         }
831     }
832 
833     for (cur_value = 0; **str; *str += 1)
834     {
835         c = **str;
836         if (c >= ARRAYSIZE(hex_table)) break;
837         d = hex_table[c];
838         if (d == -1 || d >= base) break;
839         cur_value = cur_value * base + d;
840         success = TRUE;
841         if (cur_value < prev_value) return FALSE; /* overflow */
842         prev_value = cur_value;
843     }
844 
845     if (success) *value = cur_value;
846     return success;
847 }
848 
parse_ipv6_component(const WCHAR ** str,int base,ULONG * value)849 static BOOL parse_ipv6_component(const WCHAR **str, int base, ULONG *value)
850 {
851     WCHAR *terminator;
852     if (**str >= ARRAYSIZE(hex_table) || hex_table[**str] == -1) return FALSE;
853     *value = min(wcstoul(*str, &terminator, base), 0x7FFFFFFF);
854     if (*terminator == '0') terminator++; /* "0x" but nothing valid after */
855     else if (terminator == *str) return FALSE;
856     *str = terminator;
857     return TRUE;
858 }
859 
ipv6_string_to_address(const WCHAR * str,BOOL ex,const WCHAR ** terminator,IN6_ADDR * address,ULONG * scope,USHORT * port)860 static NTSTATUS ipv6_string_to_address(const WCHAR *str, BOOL ex,
861                                        const WCHAR **terminator, IN6_ADDR *address, ULONG *scope, USHORT *port)
862 {
863     BOOL expecting_port = FALSE, has_0x = FALSE, too_big = FALSE;
864     int n_bytes = 0, n_ipv4_bytes = 0, gap = -1;
865     ULONG ip_component, scope_component = 0, port_component = 0;
866     const WCHAR *prev_str;
867 
868     if (str[0] == '[')
869     {
870         if (!ex) goto error;
871         expecting_port = TRUE;
872         str++;
873     }
874 
875     if (str[0] == ':')
876     {
877         if (str[1] != ':') goto error;
878         str++;
879         address->u.Word[0] = 0;
880     }
881 
882     for (;;)
883     {
884         if (!n_ipv4_bytes && *str == ':')
885         {
886             /* double colon */
887             if (gap != -1) goto error;
888             str++;
889             prev_str = str;
890             gap = n_bytes;
891             if (n_bytes == 14 || !parse_ipv6_component(&str, 16, &ip_component)) break;
892             str = prev_str;
893         }
894         else
895         {
896             prev_str = str;
897         }
898 
899         if (!n_ipv4_bytes && n_bytes <= (gap != -1 ? 10 : 12))
900         {
901             if (parse_ipv6_component(&str, 10, &ip_component) && *str == '.')
902                 n_ipv4_bytes = 1;
903             str = prev_str;
904         }
905 
906         if (n_ipv4_bytes)
907         {
908             /* IPv4 component */
909             if (!parse_ipv6_component(&str, 10, &ip_component)) goto error;
910             if (str - prev_str > 3 || ip_component > 255)
911             {
912                 too_big = TRUE;
913             }
914             else
915             {
916                 if (*str != '.' && (n_ipv4_bytes < 4 || (n_bytes < 15 && gap == -1))) goto error;
917                 address->u.Byte[n_bytes] = ip_component;
918                 n_bytes++;
919             }
920             if (n_ipv4_bytes == 4 || *str != '.') break;
921             n_ipv4_bytes++;
922         }
923         else
924         {
925             /* IPv6 component */
926             if (!parse_ipv6_component(&str, 16, &ip_component)) goto error;
927             if (prev_str[0] == '0' && (prev_str[1] == 'x' || prev_str[1] == 'X'))
928             {
929                 /* Windows "feature": the last IPv6 component can start with "0x" and be longer than 4 digits */
930                 if (terminator) *terminator = prev_str + 1; /* Windows says that the "x" is the terminator */
931                 if (n_bytes < 14 && gap == -1) return STATUS_INVALID_PARAMETER;
932                 address->u.Word[n_bytes/2] = RtlUshortByteSwap(ip_component);
933                 n_bytes += 2;
934                 has_0x = TRUE;
935                 goto fill_gap;
936             }
937             if (*str != ':' && n_bytes < 14 && gap == -1) goto error;
938             if (str - prev_str > 4)
939                 too_big = TRUE;
940             else
941                 address->u.Word[n_bytes/2] = RtlUshortByteSwap(ip_component);
942             n_bytes += 2;
943             if (*str != ':' || (gap != -1 && str[1] == ':')) break;
944         }
945         if (n_bytes == (gap != -1 ? 14 : 16)) break;
946         if (too_big) return STATUS_INVALID_PARAMETER;
947         str++;
948     }
949 
950     if (terminator) *terminator = str;
951     if (too_big) return STATUS_INVALID_PARAMETER;
952 
953 fill_gap:
954     if (gap == -1)
955     {
956         if (n_bytes < 16) goto error;
957     }
958     else
959     {
960         memmove(address->u.Byte + 16 - (n_bytes - gap), address->u.Byte + gap, n_bytes - gap);
961         memset(address->u.Byte + gap, 0, 16 - n_bytes);
962     }
963 
964     if (ex)
965     {
966         if (has_0x) goto error;
967 
968         if (*str == '%')
969         {
970             str++;
971             if (!parse_ipv4_component(&str, TRUE, &scope_component)) goto error;
972         }
973 
974         if (expecting_port)
975         {
976             if (*str != ']') goto error;
977             str++;
978             if (*str == ':')
979             {
980                 str++;
981                 if (!parse_ipv4_component(&str, FALSE, &port_component)) goto error;
982                 if (!port_component || port_component > 0xFFFF || *str) goto error;
983                 port_component = RtlUshortByteSwap(port_component);
984             }
985         }
986     }
987 
988     if (!terminator && *str) return STATUS_INVALID_PARAMETER;
989 
990     if (scope) *scope = scope_component;
991     if (port) *port = port_component;
992 
993     return STATUS_SUCCESS;
994 
995 error:
996     if (terminator) *terminator = str;
997     return STATUS_INVALID_PARAMETER;
998 }
999 
1000 /*
1001 * @implemented
1002 */
1003 NTSTATUS
1004 NTAPI
RtlIpv6StringToAddressW(_In_ PCWSTR String,_Out_ PCWSTR * Terminator,_Out_ struct in6_addr * Addr)1005 RtlIpv6StringToAddressW(
1006     _In_ PCWSTR String,
1007     _Out_ PCWSTR *Terminator,
1008     _Out_ struct in6_addr *Addr)
1009 {
1010     if (!String || !Terminator || !Addr)
1011         return STATUS_INVALID_PARAMETER;
1012 
1013     return ipv6_string_to_address(String, FALSE, Terminator, Addr, NULL, NULL);
1014 }
1015 
1016 /*
1017 * @implemented
1018 */
1019 NTSTATUS
1020 NTAPI
RtlIpv6StringToAddressExW(_In_ PCWSTR AddressString,_Out_ struct in6_addr * Address,_Out_ PULONG ScopeId,_Out_ PUSHORT Port)1021 RtlIpv6StringToAddressExW(
1022     _In_ PCWSTR AddressString,
1023     _Out_ struct in6_addr *Address,
1024     _Out_ PULONG ScopeId,
1025     _Out_ PUSHORT Port)
1026 {
1027     if (!AddressString || !Address || !ScopeId || !Port)
1028         return STATUS_INVALID_PARAMETER;
1029 
1030     return ipv6_string_to_address(AddressString, TRUE, NULL, Address, ScopeId, Port);
1031 }
1032 
1033 /* End of Wine implementation */
1034 
1035 /* EOF */
1036