xref: /reactos/sdk/lib/rtl/network.c (revision 8a978a17)
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
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
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 /* Tell us what possible base the string could be in, 10 or 16 by looking at the characters.
114    Invalid characters break the operation */
115 static
116 ULONG
117 RtlpClassifyChars(PCWSTR S, PULONG Base)
118 {
119     ULONG Len = 0;
120     *Base = 0;
121     for (Len = 0; S[Len]; ++Len)
122     {
123         if (iswascii(S[Len]) && isdigit(S[Len]))
124             *Base = max(*Base, 10);
125         else if (iswascii(S[Len]) && isxdigit(S[Len]))
126             *Base = 16;
127         else
128             break;
129     }
130     return Len;
131 }
132 
133 /* Worker function to extract the ipv4 part of a string. */
134 NTSTATUS
135 NTAPI
136 RtlpIpv4StringToAddressParserW(
137     _In_ PCWSTR String,
138     _In_ BOOLEAN Strict,
139     _Out_ PCWSTR *Terminator,
140     _Out_writes_(4) ULONG *Values,
141     _Out_ INT *Parts)
142 {
143     NTSTATUS Status;
144     *Parts = 0;
145     do
146     {
147         Status = RtlpStringToUlong(String, Strict, &String, &Values[*Parts]);
148         (*Parts)++;
149 
150         if (*String != L'.')
151             break;
152 
153         /* Already four parts, but a dot follows? */
154         if (*Parts == 4)
155         {
156             Status = STATUS_INVALID_PARAMETER;
157             break;
158         }
159         /* Skip the dot */
160         String++;
161     } while (NT_SUCCESS(Status));
162 
163     *Terminator = String;
164     return Status;
165 }
166 
167 /* PUBLIC FUNCTIONS ***********************************************************/
168 
169 /*
170  * @implemented
171  */
172 PSTR
173 NTAPI
174 RtlIpv4AddressToStringA(
175     _In_ const struct in_addr *Addr,
176     _Out_writes_(IPV4_ADDR_STRING_MAX_LEN) PCHAR S)
177 {
178     NTSTATUS Status;
179     PSTR End;
180 
181     if (!S)
182         return (PSTR)~0;
183 
184     Status = RtlStringCchPrintfExA(S,
185                                    IPV4_ADDR_STRING_MAX_LEN,
186                                    &End,
187                                    NULL,
188                                    0,
189                                    "%u.%u.%u.%u",
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))
196         return (PSTR)~0;
197 
198     return End;
199 }
200 
201 /*
202  * @implemented
203  */
204 NTSTATUS
205 NTAPI
206 RtlIpv4AddressToStringExA(
207     _In_ const struct in_addr *Address,
208     _In_ USHORT Port,
209     _Out_writes_to_(*AddressStringLength, *AddressStringLength) PCHAR AddressString,
210     _Inout_ PULONG AddressStringLength)
211 {
212     CHAR Buffer[IPV4_ADDR_STRING_MAX_LEN + IPV4_PORT_STRING_MAX_LEN];
213     NTSTATUS Status;
214     ULONG Length;
215     PSTR End;
216 
217     if (!Address || !AddressString || !AddressStringLength)
218         return STATUS_INVALID_PARAMETER;
219 
220     Status = RtlStringCchPrintfExA(Buffer,
221                                    RTL_NUMBER_OF(Buffer),
222                                    &End,
223                                    NULL,
224                                    0,
225                                    Port ? "%u.%u.%u.%u:%u"
226                                         : "%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,
231                                    WN2H(Port));
232     ASSERT(Status == STATUS_SUCCESS);
233     if (!NT_SUCCESS(Status))
234         return STATUS_INVALID_PARAMETER;
235 
236     Length = End - Buffer;
237     if (*AddressStringLength > Length)
238     {
239         Status = RtlStringCchCopyA(AddressString,
240                                    *AddressStringLength,
241                                    Buffer);
242         ASSERT(Status == STATUS_SUCCESS);
243         *AddressStringLength = Length + 1;
244         return STATUS_SUCCESS;
245     }
246 
247     *AddressStringLength = Length + 1;
248     return STATUS_INVALID_PARAMETER;
249 }
250 
251 /*
252  * @implemented
253  */
254 PWSTR
255 NTAPI
256 RtlIpv4AddressToStringW(
257     _In_ const struct in_addr *Addr,
258     _Out_writes_(IPV4_ADDR_STRING_MAX_LEN) PWCHAR S)
259 {
260     NTSTATUS Status;
261     PWSTR End;
262 
263     if (!S)
264         return (PWSTR)~0;
265 
266     Status = RtlStringCchPrintfExW(S,
267                                    IPV4_ADDR_STRING_MAX_LEN,
268                                    &End,
269                                    NULL,
270                                    0,
271                                    L"%u.%u.%u.%u",
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))
278         return (PWSTR)~0;
279 
280     return End;
281 }
282 
283 /*
284  * @implemented
285  */
286 NTSTATUS
287 NTAPI
288 RtlIpv4AddressToStringExW(
289     _In_ const struct in_addr *Address,
290     _In_ USHORT Port,
291     _Out_writes_to_(*AddressStringLength, *AddressStringLength) PWCHAR AddressString,
292     _Inout_ PULONG AddressStringLength)
293 {
294     WCHAR Buffer[IPV4_ADDR_STRING_MAX_LEN + IPV4_PORT_STRING_MAX_LEN];
295     NTSTATUS Status;
296     ULONG Length;
297     PWSTR End;
298 
299     if (!Address || !AddressString || !AddressStringLength)
300         return STATUS_INVALID_PARAMETER;
301 
302     Status = RtlStringCchPrintfExW(Buffer,
303                                    RTL_NUMBER_OF(Buffer),
304                                    &End,
305                                    NULL,
306                                    0,
307                                    Port ? L"%u.%u.%u.%u:%u"
308                                         : L"%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,
313                                    WN2H(Port));
314     ASSERT(Status == STATUS_SUCCESS);
315     if (!NT_SUCCESS(Status))
316         return STATUS_INVALID_PARAMETER;
317 
318     Length = End - AddressString;
319     if (*AddressStringLength > Length)
320     {
321         Status = RtlStringCchCopyW(AddressString,
322                                    *AddressStringLength,
323                                    Buffer);
324         ASSERT(Status == STATUS_SUCCESS);
325         *AddressStringLength = Length + 1;
326         return STATUS_SUCCESS;
327     }
328 
329     *AddressStringLength = Length + 1;
330     return STATUS_INVALID_PARAMETER;
331 }
332 
333 /*
334  * @implemented
335  */
336 NTSTATUS
337 NTAPI
338 RtlIpv4StringToAddressA(
339     _In_ PCSTR String,
340     _In_ BOOLEAN Strict,
341     _Out_ PCSTR *Terminator,
342     _Out_ struct in_addr *Addr)
343 {
344     NTSTATUS Status;
345     ANSI_STRING AddressA;
346     UNICODE_STRING AddressW;
347     PCWSTR TerminatorW = NULL;
348 
349     Status = RtlInitAnsiStringEx(&AddressA, String);
350     if (!NT_SUCCESS(Status))
351         return Status;
352 
353     Status = RtlAnsiStringToUnicodeString(&AddressW, &AddressA, TRUE);
354     if (!NT_SUCCESS(Status))
355         return Status;
356 
357     Status = RtlIpv4StringToAddressW(AddressW.Buffer,
358                                      Strict,
359                                      &TerminatorW,
360                                      Addr);
361 
362     ASSERT(TerminatorW >= AddressW.Buffer);
363     *Terminator = String + (TerminatorW - AddressW.Buffer);
364 
365     RtlFreeUnicodeString(&AddressW);
366 
367     return Status;
368 }
369 
370 /*
371  * @implemented
372  */
373 NTSTATUS
374 NTAPI
375 RtlIpv4StringToAddressExA(
376     _In_ PCSTR AddressString,
377     _In_ BOOLEAN Strict,
378     _Out_ struct in_addr *Address,
379     _Out_ PUSHORT Port)
380 {
381     NTSTATUS Status;
382     ANSI_STRING AddressA;
383     UNICODE_STRING AddressW;
384 
385     Status = RtlInitAnsiStringEx(&AddressA, AddressString);
386     if (!NT_SUCCESS(Status))
387         return Status;
388 
389     Status = RtlAnsiStringToUnicodeString(&AddressW, &AddressA, TRUE);
390     if (!NT_SUCCESS(Status))
391         return Status;
392 
393     Status = RtlIpv4StringToAddressExW(AddressW.Buffer, Strict, Address, Port);
394 
395     RtlFreeUnicodeString(&AddressW);
396 
397     return Status;
398 }
399 
400 /*
401  * @implemented
402  */
403 NTSTATUS
404 NTAPI
405 RtlIpv4StringToAddressW(
406     _In_ PCWSTR String,
407     _In_ BOOLEAN Strict,
408     _Out_ PCWSTR *Terminator,
409     _Out_ struct in_addr *Addr)
410 {
411     NTSTATUS Status;
412     ULONG Values[4];
413     ULONG Result;
414     INT Parts = 0;
415     INT i;
416 
417     Status = RtlpIpv4StringToAddressParserW(String,
418                                             Strict,
419                                             Terminator,
420                                             Values,
421                                             &Parts);
422     if (Strict && Parts < 4)
423         Status = STATUS_INVALID_PARAMETER;
424 
425     if (!NT_SUCCESS(Status))
426         return Status;
427 
428     /* Combine the parts */
429     Result = Values[Parts - 1];
430     for (i = 0; i < Parts - 1; i++)
431     {
432         INT Shift = CHAR_BIT * (3 - i);
433 
434         if (Values[i] > 0xFF || (Result & (0xFF << Shift)) != 0)
435         {
436             return STATUS_INVALID_PARAMETER;
437         }
438         Result |= Values[i] << Shift;
439     }
440 
441     Addr->S_un.S_addr = RtlUlongByteSwap(Result);
442     return Status;
443 }
444 
445 /*
446 * @implemented
447 */
448 NTSTATUS
449 NTAPI
450 RtlIpv4StringToAddressExW(
451     _In_ PCWSTR AddressString,
452     _In_ BOOLEAN Strict,
453     _Out_ struct in_addr *Address,
454     _Out_ PUSHORT Port)
455 {
456     PCWSTR CurrentChar;
457     ULONG ConvertedPort;
458     NTSTATUS Status;
459 
460     if (!AddressString || !Address || !Port)
461         return STATUS_INVALID_PARAMETER;
462 
463     Status = RtlIpv4StringToAddressW(AddressString,
464                                      Strict,
465                                      &CurrentChar,
466                                      Address);
467     if (!NT_SUCCESS(Status))
468         return Status;
469 
470     if (!*CurrentChar)
471     {
472         *Port = 0;
473         return STATUS_SUCCESS;
474     }
475 
476     if (*CurrentChar != L':')
477         return STATUS_INVALID_PARAMETER;
478     ++CurrentChar;
479 
480     Status = RtlpStringToUlong(CurrentChar,
481                                FALSE,
482                                &CurrentChar,
483                                &ConvertedPort);
484     if (!NT_SUCCESS(Status))
485         return Status;
486 
487     if (*CurrentChar || !ConvertedPort || ConvertedPort > 0xffff)
488         return STATUS_INVALID_PARAMETER;
489 
490     *Port = WN2H(ConvertedPort);
491     return STATUS_SUCCESS;
492 }
493 
494 /*
495 * @implemented
496 */
497 PSTR
498 NTAPI
499 RtlIpv6AddressToStringA(
500     _In_ const struct in6_addr *Addr,
501     _Out_writes_(RTLIPV6A2S_MAX_LEN) PSTR S)
502 {
503     WCHAR Buffer[RTLIPV6A2S_MAX_LEN];
504     PWSTR Result;
505     NTSTATUS Status;
506 
507     if (!S)
508         return (PSTR)~0;
509 
510     Buffer[0] = 0;
511     Result = RtlIpv6AddressToStringW(Addr, Buffer);
512     if (Result == (PWSTR)~0)
513         return (PSTR)~0;
514 
515     ASSERT(Result >= Buffer);
516     ASSERT(Result < Buffer + RTL_NUMBER_OF(Buffer));
517 
518     Status = RtlUnicodeToMultiByteN(S, RTLIPV6A2S_MAX_LEN, NULL, Buffer, (ULONG)(wcslen(Buffer) + 1) * sizeof(WCHAR));
519     if (!NT_SUCCESS(Status))
520         return (PSTR)~0;
521 
522     return S + strlen(S);
523 }
524 
525 /*
526 * @implemented
527 */
528 NTSTATUS
529 NTAPI
530 RtlIpv6AddressToStringExA(
531     _In_ const struct in6_addr *Address,
532     _In_ ULONG ScopeId,
533     _In_ USHORT Port,
534     _Out_writes_to_(*AddressStringLength, *AddressStringLength) PSTR AddressString,
535     _Inout_ PULONG AddressStringLength)
536 {
537     WCHAR Buffer[RTLIPV6A2SEX_MAX_LEN];
538     NTSTATUS Status;
539 
540     if (!Address || !AddressString || !AddressStringLength)
541         return STATUS_INVALID_PARAMETER;
542 
543     Status = RtlIpv6AddressToStringExW(Address, ScopeId, Port, Buffer, AddressStringLength);
544     if (!NT_SUCCESS(Status))
545         return Status;
546 
547     Status = RtlUnicodeToMultiByteN(AddressString, RTLIPV6A2SEX_MAX_LEN, NULL, Buffer, (*AddressStringLength + 1) * sizeof(WCHAR));
548     if (!NT_SUCCESS(Status))
549         return STATUS_INVALID_PARAMETER;
550 
551     return STATUS_SUCCESS;
552 }
553 
554 /*
555 * @implemented
556 */
557 PWSTR
558 NTAPI
559 RtlIpv6AddressToStringW(
560     _In_ const struct in6_addr *Addr,
561     _Out_writes_(RTLIPV6A2S_MAX_LEN) PWSTR S)
562 {
563     NTSTATUS Status;
564     UINT Parts = 8, n;
565     BOOLEAN SkipOnce = TRUE;
566     PWSTR End;
567     size_t Remaining;
568 
569     if (!S)
570         return (PWSTR)~0;
571 
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])
575     {
576         PWSTR Prefix = NULL;
577         if (Addr->s6_words[4] == 0xffff && !Addr->s6_words[5])
578             Prefix = L"ffff:0:";
579         else if (!Addr->s6_words[4] && Addr->s6_words[5] == 0xffff)
580             Prefix = L"ffff:";
581         else if (!Addr->s6_words[4] && !Addr->s6_words[5])
582             Prefix = L"";
583         if (Prefix != NULL)
584         {
585             Status = RtlStringCchPrintfExW(S,
586                                            Remaining,
587                                            &End,
588                                            NULL,
589                                            0,
590                                            L"::%ls%u.%u.%u.%u",
591                                            Prefix,
592                                            Addr->s6_bytes[12],
593                                            Addr->s6_bytes[13],
594                                            Addr->s6_bytes[14],
595                                            Addr->s6_bytes[15]);
596             ASSERT(Status == STATUS_SUCCESS);
597             if (!NT_SUCCESS(Status))
598                 return (PWSTR)~0;
599             return End;
600         }
601     }
602 
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)
605         Parts = 6;
606 
607     for (n = 0; n < Parts; ++n)
608     {
609         if (SkipOnce && ((n + 1) < Parts) && !Addr->s6_words[n] && !Addr->s6_words[n + 1])
610         {
611             SkipOnce = FALSE;
612             while (!Addr->s6_words[n + 1] && (n + 1) < Parts)
613                 ++n;
614             *S++ = ':';
615             Remaining--;
616             if ((n + 1) >= Parts)
617             {
618                 *S++ = ':';
619                 Remaining--;
620             }
621         }
622         else
623         {
624             if (n)
625             {
626                 *S++ = ':';
627                 Remaining--;
628             }
629             Status = RtlStringCchPrintfExW(S,
630                                            Remaining,
631                                            &End,
632                                            &Remaining,
633                                            0,
634                                            L"%x",
635                                            WN2H(Addr->s6_words[n]));
636             ASSERT(Status == STATUS_SUCCESS);
637             if (!NT_SUCCESS(Status))
638                 return (PWSTR)~0;
639             S = End;
640         }
641     }
642     if (Parts < 8)
643     {
644         Status = RtlStringCchPrintfExW(S,
645                                        Remaining,
646                                        &End,
647                                        NULL,
648                                        0,
649                                        L":%u.%u.%u.%u",
650                                        Addr->s6_bytes[12],
651                                        Addr->s6_bytes[13],
652                                        Addr->s6_bytes[14],
653                                        Addr->s6_bytes[15]);
654         ASSERT(Status == STATUS_SUCCESS);
655         if (!NT_SUCCESS(Status))
656             return (PWSTR)~0;
657 
658         return End;
659     }
660     *S = UNICODE_NULL;
661     return S;
662 }
663 
664 /*
665 * @implemented
666 */
667 NTSTATUS
668 NTAPI
669 RtlIpv6AddressToStringExW(
670     _In_ const struct in6_addr *Address,
671     _In_ ULONG ScopeId,
672     _In_ USHORT Port,
673     _Out_writes_to_(*AddressStringLength, *AddressStringLength) PWCHAR AddressString,
674     _Inout_ PULONG AddressStringLength)
675 {
676     WCHAR Buffer[RTLIPV6A2SEX_MAX_LEN];
677     PWCHAR S = Buffer;
678     NTSTATUS Status;
679     ULONG Length;
680     size_t Remaining;
681 
682     if (!Address || !AddressString || !AddressStringLength)
683         return STATUS_INVALID_PARAMETER;
684 
685     if (Port)
686         *S++ = L'[';
687 
688     S = RtlIpv6AddressToStringW(Address, S);
689     ASSERT(S != (PCWSTR)~0);
690     if (S == (PCWSTR)~0)
691         return STATUS_INVALID_PARAMETER;
692 
693     ASSERT(S >= Buffer);
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);
697 
698     if (ScopeId)
699     {
700         Status = RtlStringCchPrintfExW(S,
701                                        Remaining,
702                                        &S,
703                                        &Remaining,
704                                        0,
705                                        L"%%%u",
706                                        ScopeId);
707         ASSERT(Status == STATUS_SUCCESS);
708         if (!NT_SUCCESS(Status))
709             return STATUS_INVALID_PARAMETER;
710     }
711 
712     if (Port)
713     {
714         Status = RtlStringCchPrintfExW(S,
715                                        Remaining,
716                                        &S,
717                                        &Remaining,
718                                        0,
719                                        L"]:%u",
720                                        WN2H(Port));
721         ASSERT(Status == STATUS_SUCCESS);
722         if (!NT_SUCCESS(Status))
723             return STATUS_INVALID_PARAMETER;
724     }
725 
726     Length = S - Buffer;
727     ASSERT(Buffer[Length] == UNICODE_NULL);
728     if (*AddressStringLength > Length)
729     {
730         Status = RtlStringCchCopyW(AddressString, *AddressStringLength, Buffer);
731         ASSERT(Status == STATUS_SUCCESS);
732         *AddressStringLength = Length + 1;
733         return STATUS_SUCCESS;
734     }
735 
736     *AddressStringLength = Length + 1;
737     return STATUS_INVALID_PARAMETER;
738 }
739 
740 /*
741 * @implemented
742 */
743 NTSTATUS
744 NTAPI
745 RtlIpv6StringToAddressA(
746     _In_ PCSTR String,
747     _Out_ PCSTR *Terminator,
748     _Out_ struct in6_addr *Addr)
749 {
750     NTSTATUS Status;
751     ANSI_STRING StringA;
752     UNICODE_STRING StringW;
753     PCWSTR TerminatorW = NULL;
754 
755     Status = RtlInitAnsiStringEx(&StringA, String);
756     if (!NT_SUCCESS(Status))
757         return Status;
758 
759     Status = RtlAnsiStringToUnicodeString(&StringW, &StringA, TRUE);
760     if (!NT_SUCCESS(Status))
761         return Status;
762 
763     Status = RtlIpv6StringToAddressW(StringW.Buffer, &TerminatorW, Addr);
764     /* on windows the terminator is not always written, so we mimic that behavior. */
765     if (TerminatorW)
766         *Terminator = String + (TerminatorW - StringW.Buffer);
767 
768     RtlFreeUnicodeString(&StringW);
769     return Status;
770 }
771 
772 /*
773 * @implemented
774 */
775 NTSTATUS
776 NTAPI
777 RtlIpv6StringToAddressExA(
778     _In_ PCSTR AddressString,
779     _Out_ struct in6_addr *Address,
780     _Out_ PULONG ScopeId,
781     _Out_ PUSHORT Port)
782 {
783     NTSTATUS Status;
784     ANSI_STRING AddressA;
785     UNICODE_STRING AddressW;
786 
787     Status = RtlInitAnsiStringEx(&AddressA, AddressString);
788     if (!NT_SUCCESS(Status))
789         return Status;
790 
791     Status = RtlAnsiStringToUnicodeString(&AddressW, &AddressA, TRUE);
792     if (!NT_SUCCESS(Status))
793         return Status;
794 
795     Status = RtlIpv6StringToAddressExW(AddressW.Buffer, Address, ScopeId, Port);
796 
797     RtlFreeUnicodeString(&AddressW);
798     return Status;
799 }
800 
801 /*
802 * @implemented
803 */
804 NTSTATUS
805 NTAPI
806 RtlIpv6StringToAddressW(
807     _In_ PCWSTR String,
808     _Out_ PCWSTR *Terminator,
809     _Out_ struct in6_addr *Addr)
810 {
811     INT n, j;
812     INT StartSkip = -1, Parts = 0;
813     ULONG Base, Len;
814     NTSTATUS Status = STATUS_SUCCESS;
815     enum { None, Number, Colon, DoubleColon } Last = None;
816     BOOLEAN SkipoutLastHex = FALSE;
817 
818     if (!String || !Terminator || !Addr)
819         return STATUS_INVALID_PARAMETER;
820 
821     for (n = 0; n < 8;)
822     {
823         Len = RtlpClassifyChars(String, &Base);
824         if (Len == 0)
825         {
826             /* not a number, and no ':' or already encountered an ':' */
827             if (String[0] != ':' || Last == Colon)
828                 break;
829 
830             /* double colon, 1 or more fields set to 0. mark the position, and move on. */
831             if (StartSkip == -1 && String[1] == ':')
832             {
833                 /* this case was observed in windows, but it does not seem correct. */
834                 if (!n)
835                 {
836                     Addr->s6_words[n++] = 0;
837                     Addr->s6_words[n++] = 0;
838                 }
839                 StartSkip = n;
840                 String += 2;
841                 Last = DoubleColon;
842             }
843             else if (String[1] == ':' || Last != Number)
844             {
845                 /* a double colon after we already encountered one, or a the last parsed item was not a number. */
846                 break;
847             }
848             else
849             {
850                 ++String;
851                 Last = Colon;
852             }
853         }
854         else if (Len > 4)
855         {
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)
860                 return Status;
861             String += Len;
862             break;
863         }
864         else
865         {
866             ULONG Value;
867             if (String[Len] == '.' && n <= 6)
868             {
869                 ULONG Values[4];
870                 INT PartsV4 = 0;
871                 /* this could be an ipv4 address inside an ipv6 address http://tools.ietf.org/html/rfc2765 */
872                 Last = Number;
873                 Status = RtlpIpv4StringToAddressParserW(String, TRUE, &String, Values, &PartsV4);
874                 for(j = 0; j < PartsV4; ++j)
875                 {
876                     if (Values[j] > 255)
877                     {
878                         Status = STATUS_INVALID_PARAMETER;
879                         if (j != 3)
880                             return Status;
881                         break;
882                     }
883                     if ((j == PartsV4 - 1) &&
884                         (j < 3 ||
885                          (*String == ':' && StartSkip == -1) ||
886                          (StartSkip == -1 && n < 6) ||
887                          Status == STATUS_INVALID_PARAMETER))
888                     {
889                         Status = STATUS_INVALID_PARAMETER;
890                         break;
891                     }
892                     Addr->s6_bytes[n * 2 + j] = Values[j] & 0xff;
893                 }
894                 /* mark enough parts as converted in case we are the last item to be converted */
895                 n += 2;
896                 /* mark 2 parts as converted in case we are not the last item, and we encountered a double colon. */
897                 Parts+=2;
898                 break;
899             }
900 
901             if (String[Len] != ':' && n < 7 && StartSkip == -1)
902             {
903                 /* if we decoded atleast some numbers, update the terminator to point to the first invalid char */
904                 if (Base)
905                     String += Len;
906                 Status = STATUS_INVALID_PARAMETER;
907                 break;
908             }
909 
910             if (Len == 1 && towlower(String[Len]) == 'x' && String[0] == '0')
911             {
912                 Len = RtlpClassifyChars(String + 2, &Base);
913                 if (Len > 0 && Len <= 4)
914                 {
915                     *Terminator = String + 1;
916                     String += 2;
917                     SkipoutLastHex = TRUE;
918                 }
919             }
920 
921             Status = RtlpStringToUlongBase(String, 16, &String, &Value);
922             if (!NT_SUCCESS(Status))
923                 break;
924 
925             if (StartSkip != -1)
926                 Parts++;
927             Addr->s6_words[n++] = WN2H(Value);
928             Last = Number;
929             if (SkipoutLastHex)
930                 break;
931         }
932     }
933 
934     if (StartSkip != -1 && Status != STATUS_INVALID_PARAMETER && Last != Colon)
935     {
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]));
939         n = 8;
940     }
941 
942     /* we have already set the terminator */
943     if (SkipoutLastHex)
944         return n < 8 ? STATUS_INVALID_PARAMETER : Status;
945     *Terminator = String;
946     return n < 8 ? STATUS_INVALID_PARAMETER : Status;
947 }
948 
949 /*
950 * @implemented
951 */
952 NTSTATUS
953 NTAPI
954 RtlIpv6StringToAddressExW(
955     _In_ PCWSTR AddressString,
956     _Out_ struct in6_addr *Address,
957     _Out_ PULONG ScopeId,
958     _Out_ PUSHORT Port)
959 {
960     NTSTATUS Status;
961     ULONG ConvertedPort = 0, ConvertedScope = 0;
962     if (!AddressString || !Address || !ScopeId || !Port)
963         return STATUS_INVALID_PARAMETER;
964 
965     if (*AddressString == '[')
966     {
967         ConvertedPort = 1;
968         ++AddressString;
969     }
970     Status = RtlIpv6StringToAddressW(AddressString, &AddressString, Address);
971     if (!NT_SUCCESS(Status))
972         return STATUS_INVALID_PARAMETER;
973 
974     if (*AddressString == '%')
975     {
976         ++AddressString;
977         Status = RtlpStringToUlongBase(AddressString, 10, &AddressString, &ConvertedScope);
978         if (!NT_SUCCESS(Status))
979             return STATUS_INVALID_PARAMETER;
980     }
981     else if (*AddressString && !(ConvertedPort && *AddressString == ']'))
982     {
983         return STATUS_INVALID_PARAMETER;
984     }
985 
986     if (ConvertedPort)
987     {
988         if (*AddressString++ !=']')
989             return STATUS_INVALID_PARAMETER;
990         if (*AddressString ==':')
991         {
992             ULONG Base = 10;
993             if (*++AddressString == '0')
994             {
995                 if (towlower(*++AddressString) != 'x')
996                     return STATUS_INVALID_PARAMETER;
997                 ++AddressString;
998                 Base = 16;
999             }
1000             Status = RtlpStringToUlongBase(AddressString, Base, &AddressString, &ConvertedPort);
1001             if (!NT_SUCCESS(Status) || ConvertedPort > 0xffff)
1002                 return STATUS_INVALID_PARAMETER;
1003         }
1004         else
1005         {
1006             ConvertedPort = 0;
1007         }
1008     }
1009 
1010     if (*AddressString == 0)
1011     {
1012         *ScopeId = ConvertedScope;
1013         *Port = WN2H(ConvertedPort);
1014         return STATUS_SUCCESS;
1015     }
1016     return STATUS_INVALID_PARAMETER;
1017 }
1018 
1019 /* EOF */
1020