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 /* 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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