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