[NTOS:CM] CmpCmdHiveOpen(): Resolve FileAttributes->RootDirectory when a hive file...
[reactos.git] / sdk / lib / rtl / network.c
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, (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, (wcslen(Buffer) + 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 */