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