1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 /** 6 * Internet Protocol implementation. 7 * 8 * Copyright: Eugene Wissner 2018-2020. 9 * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, 10 * Mozilla Public License, v. 2.0). 11 * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) 12 * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/net/ip.d, 13 * tanya/net/ip.d) 14 */ 15 module tanya.net.ip; 16 17 import std.algorithm.comparison; 18 import std.ascii; 19 import std.typecons; 20 import tanya.algorithm.iteration; 21 import tanya.algorithm.mutation; 22 import tanya.container..string; 23 import tanya.conv; 24 import tanya.format; 25 import tanya.memory.lifetime; 26 import tanya.meta.trait; 27 import tanya.meta.transform; 28 import tanya.net.iface; 29 import tanya.net.inet; 30 import tanya.range; 31 import tanya.typecons; 32 33 /** 34 * IPv4 internet address. 35 */ 36 struct Address4 37 { 38 // In network byte order. 39 private uint address; 40 41 version (LittleEndian) 42 { 43 private enum uint loopback_ = 0x0100007fU; 44 enum byte step = 8; 45 } 46 else 47 { 48 private enum uint loopback_ = 0x7f000001U; 49 enum byte step = -8; 50 } 51 private enum uint any_ = 0U; 52 private enum uint broadcast = uint.max; 53 54 /** 55 * Constructs an $(D_PSYMBOL Address4) from an unsigned integer in host 56 * byte order. 57 * 58 * Params: 59 * address = The address as an unsigned integer in host byte order. 60 */ 61 this(uint address) @nogc nothrow pure @trusted 62 { 63 copy(NetworkOrder!4(address), (cast(ubyte*) &this.address)[0 .. 4]); 64 } 65 66 /// 67 @nogc nothrow pure @safe unittest 68 { 69 assert(Address4(0x00202000U).toUInt() == 0x00202000U); 70 } 71 72 /** 73 * Compares two $(D_PARAM Address4) objects. 74 * 75 * Params: 76 * that = Another address. 77 * 78 * Returns: Positive number if $(D_KEYWORD this) is larger than 79 * $(D_PARAM that), negative - if it is smaller, or 0 if they 80 * equal. 81 */ 82 int opCmp(ref const Address4 that) const @nogc nothrow pure @safe 83 { 84 const lhs = toUInt(); 85 const rhs = that.toUInt(); 86 return (rhs < lhs) - (lhs < rhs); 87 } 88 89 /// ditto 90 int opCmp(const Address4 that) const @nogc nothrow pure @safe 91 { 92 return opCmp(that); 93 } 94 95 /// 96 @nogc nothrow pure @safe unittest 97 { 98 assert(address4("127.0.0.1").get > address4("126.0.0.0").get); 99 assert(address4("127.0.0.1").get < address4("127.0.0.2").get); 100 assert(address4("127.0.0.1") == address4("127.0.0.1")); 101 } 102 103 /** 104 * Returns object that represents 127.0.0.1. 105 * 106 * Returns: Object that represents the Loopback address. 107 */ 108 static @property Address4 loopback() @nogc nothrow pure @safe 109 { 110 typeof(return) address; 111 address.address = Address4.loopback_; 112 return address; 113 } 114 115 /// 116 @nogc nothrow pure @safe unittest 117 { 118 assert(Address4.loopback().isLoopback()); 119 } 120 121 /** 122 * Returns object that represents 0.0.0.0. 123 * 124 * Returns: Object that represents any address. 125 */ 126 static @property Address4 any() @nogc nothrow pure @safe 127 { 128 typeof(return) address; 129 address.address = Address4.any_; 130 return address; 131 } 132 133 /// 134 @nogc nothrow pure @safe unittest 135 { 136 assert(Address4.any().isAny()); 137 } 138 139 /** 140 * Loopback address is 127.0.0.1. 141 * 142 * Returns: $(D_KEYWORD true) if this is a loopback address, 143 * $(D_KEYWORD false) otherwise. 144 */ 145 bool isLoopback() const @nogc nothrow pure @safe 146 { 147 return this.address == loopback_; 148 } 149 150 /// 151 @nogc nothrow pure @safe unittest 152 { 153 assert(address4("127.0.0.1").get.isLoopback()); 154 } 155 156 /** 157 * 0.0.0.0 can represent any address. This function checks whether this 158 * address is 0.0.0.0. 159 * 160 * Returns: $(D_KEYWORD true) if this is an unspecified address, 161 * $(D_KEYWORD false) otherwise. 162 */ 163 bool isAny() const @nogc nothrow pure @safe 164 { 165 return this.address == any_; 166 } 167 168 /// 169 @nogc nothrow pure @safe unittest 170 { 171 assert(address4("0.0.0.0").get.isAny()); 172 } 173 174 /** 175 * Broadcast address is 255.255.255.255. 176 * 177 * Returns: $(D_KEYWORD true) if this is a broadcast address, 178 * $(D_KEYWORD false) otherwise. 179 */ 180 bool isBroadcast() const @nogc nothrow pure @safe 181 { 182 return this.address == broadcast; 183 } 184 185 /// 186 @nogc nothrow pure @safe unittest 187 { 188 assert(address4("255.255.255.255").get.isBroadcast()); 189 } 190 191 /** 192 * Determines whether this address' destination is a group of endpoints. 193 * 194 * Returns: $(D_KEYWORD true) if this is a multicast address, 195 * $(D_KEYWORD false) otherwise. 196 * 197 * See_Also: $(D_PSYMBOL isUnicast). 198 */ 199 bool isMulticast() const @nogc nothrow pure @safe 200 { 201 version (LittleEndian) 202 { 203 enum uint mask = 0xe0; 204 } 205 else 206 { 207 enum uint mask = 0xe0000000U; 208 } 209 return (this.address & mask) == mask; 210 } 211 212 /// 213 @nogc nothrow pure @safe unittest 214 { 215 assert(address4("224.0.0.3").get.isMulticast()); 216 } 217 218 /** 219 * Determines whether this address' destination is a single endpoint. 220 * 221 * Returns: $(D_KEYWORD true) if this is a multicast address, 222 * $(D_KEYWORD false) otherwise. 223 * 224 * See_Also: $(D_PSYMBOL isMulticast). 225 */ 226 bool isUnicast() const @nogc nothrow pure @safe 227 { 228 return !isMulticast(); 229 } 230 231 /// 232 @nogc nothrow pure @safe unittest 233 { 234 assert(address4("192.168.0.1").get.isUnicast()); 235 } 236 237 /** 238 * Writes this IPv4 address in dotted-decimal notation. 239 * 240 * Params: 241 * OR = Type of the output range. 242 * output = Output range. 243 * 244 * Returns: $(D_PARAM output). 245 */ 246 OR toString(OR)(OR output) const 247 if (isOutputRange!(OR, const(char)[])) 248 { 249 const octets = (() @trusted => (cast(ubyte*) &this.address)[0 .. 4])(); 250 enum string fmt = "{}.{}.{}.{}"; 251 version (LittleEndian) 252 { 253 return sformat!fmt(output, 254 octets[0], 255 octets[1], 256 octets[2], 257 octets[3]); 258 } 259 else 260 { 261 return sformat!fmt(output, 262 octets[3], 263 octets[2], 264 octets[1], 265 octets[0]); 266 } 267 } 268 269 /// 270 @nogc nothrow pure @safe unittest 271 { 272 import tanya.container..string : String; 273 import tanya.range : backInserter; 274 275 const dottedDecimal = "192.168.0.1"; 276 String actual; 277 const address = address4(dottedDecimal); 278 279 address.get.toString(backInserter(actual)); 280 assert(actual == dottedDecimal); 281 } 282 283 /** 284 * Produces a byte array containing this address in network byte order. 285 * 286 * Returns: This address as raw bytes in network byte order. 287 */ 288 ubyte[4] toBytes() const @nogc nothrow pure @safe 289 { 290 ubyte[4] bytes; 291 copy((() @trusted => (cast(ubyte*) &this.address)[0 .. 4])(), bytes[]); 292 return bytes; 293 } 294 295 /// 296 @nogc nothrow pure @safe unittest 297 { 298 const actual = address4("192.168.0.1"); 299 const ubyte[4] expected = [192, 168, 0, 1]; 300 assert(actual.get.toBytes() == expected); 301 } 302 303 /** 304 * Converts this address to an unsigned integer in host byte order. 305 * 306 * Returns: This address as an unsigned integer in host byte order. 307 */ 308 uint toUInt() const @nogc nothrow pure @safe 309 { 310 alias slice = () @trusted => (cast(ubyte*) &this.address)[0 .. 4]; 311 return toHostOrder!uint(slice()); 312 } 313 314 /// 315 @nogc nothrow pure @safe unittest 316 { 317 assert(address4("127.0.0.1").get.toUInt() == 0x7f000001U); 318 } 319 } 320 321 /** 322 * Parses a string containing an IPv4 address in dotted-decimal notation. 323 * 324 * Params: 325 * R = Input range type. 326 * range = Stringish range containing the address. 327 * 328 * Returns: $(D_PSYMBOL Nullable) containing the address if the parsing was 329 * successful, or nothing otherwise. 330 */ 331 Nullable!Address4 address4(R)(R range) 332 if (isForwardRange!R && is(Unqual!(ElementType!R) == char) && hasLength!R) 333 { 334 Address4 result; 335 version (LittleEndian) 336 { 337 ubyte shift; 338 enum ubyte cond = 24; 339 } 340 else 341 { 342 ubyte shift = 24; 343 enum ubyte cond = 0; 344 } 345 346 for (; shift != cond; shift += Address4.step, range.popFront()) 347 { 348 if (range.empty || range.front == '.') 349 { 350 return typeof(return)(); 351 } 352 result.address |= readIntegral!ubyte(range) << shift; 353 if (range.empty || range.front != '.') 354 { 355 return typeof(return)(); 356 } 357 } 358 359 if (range.empty || range.front == '.') 360 { 361 return typeof(return)(); 362 } 363 result.address |= readIntegral!ubyte(range) << shift; 364 return range.empty ? typeof(return)(result) : typeof(return)(); 365 } 366 367 /** 368 * Constructs an $(D_PSYMBOL Address4) from raw bytes in network byte order. 369 * 370 * Params: 371 * R = Input range type. 372 * range = $(D_KEYWORD ubyte) range containing the address. 373 * 374 * Returns: $(D_PSYMBOL Nullable) containing the address if the $(D_PARAM range) 375 * contains exactly 4 bytes, or nothing otherwise. 376 */ 377 Nullable!Address4 address4(R)(R range) 378 if (isInputRange!R && is(Unqual!(ElementType!R) == ubyte)) 379 { 380 Address4 result; 381 version (LittleEndian) 382 { 383 ubyte shift; 384 } 385 else 386 { 387 ubyte shift = 24; 388 } 389 390 for (; shift <= 24; shift += Address4.step, range.popFront()) 391 { 392 if (range.empty) 393 { 394 return typeof(return)(); 395 } 396 result.address |= range.front << shift; 397 } 398 399 return range.empty ? typeof(return)(result) : typeof(return)(); 400 } 401 402 /// 403 @nogc nothrow pure @safe unittest 404 { 405 { 406 ubyte[4] actual = [127, 0, 0, 1]; 407 assert(address4(actual[]).get.isLoopback()); 408 } 409 { 410 ubyte[3] actual = [127, 0, 0]; 411 assert(address4(actual[]).isNull); 412 } 413 { 414 ubyte[5] actual = [127, 0, 0, 0, 1]; 415 assert(address4(actual[]).isNull); 416 } 417 } 418 419 /** 420 * IPv6 internet address. 421 */ 422 struct Address6 423 { 424 // Raw bytes 425 private ubyte[16] address; 426 427 /// Scope ID. 428 uint scopeID; 429 430 /** 431 * Constructs an $(D_PSYMBOL Address6) from an array containing raw bytes 432 * in network byte order and scope ID. 433 * 434 * Params: 435 * address = The address as an unsigned integer in host byte order. 436 * scopeID = Scope ID. 437 */ 438 this(ubyte[16] address, uint scopeID = 0) @nogc nothrow pure @safe 439 { 440 copy(address[], this.address[]); 441 this.scopeID = scopeID; 442 } 443 444 /// 445 @nogc nothrow pure @safe unittest 446 { 447 const ubyte[16] expected = [ 0, 1, 0, 2, 0, 3, 0, 4, 448 0, 5, 0, 6, 0, 7, 0, 8 ]; 449 auto actual = Address6(expected, 1); 450 assert(actual.toBytes() == expected); 451 assert(actual.scopeID == 1); 452 } 453 454 /** 455 * Compares two $(D_PARAM Address6) objects. 456 * 457 * If $(D_KEYWORD this) and $(D_PARAM that) contain the same address, scope 458 * IDs are compared. 459 * 460 * Params: 461 * that = Another address. 462 * 463 * Returns: Positive number if $(D_KEYWORD this) is larger than 464 * $(D_PARAM that), negative - if it is smaller, or 0 if they 465 * equal. 466 */ 467 int opCmp(ref const Address6 that) const @nogc nothrow pure @safe 468 { 469 const diff = cmp(this.address[], that.address[]); 470 if (diff == 0) 471 { 472 return (that.scopeID < this.scopeID) - (this.scopeID < that.scopeID); 473 } 474 return diff; 475 } 476 477 /// ditto 478 int opCmp(const Address6 that) const @nogc nothrow pure @safe 479 { 480 return opCmp(that); 481 } 482 483 /// 484 @nogc nothrow @safe unittest 485 { 486 assert(address6("::14").get > address6("::1").get); 487 assert(address6("::1").get < address6("::14").get); 488 assert(address6("::1") == address6("::1")); 489 assert(address6("fe80::1%1").get < address6("fe80::1%2").get); 490 assert(address6("fe80::1%2").get > address6("fe80::1%1").get); 491 } 492 493 /** 494 * Returns object that represents ::. 495 * 496 * Returns: Object that represents any address. 497 */ 498 static @property Address6 any() @nogc nothrow pure @safe 499 { 500 return Address6(); 501 } 502 503 /// 504 @nogc nothrow pure @safe unittest 505 { 506 assert(Address6.any().isAny()); 507 } 508 509 /** 510 * Returns object that represents ::1. 511 * 512 * Returns: Object that represents the Loopback address. 513 */ 514 static @property Address6 loopback() @nogc nothrow pure @safe 515 { 516 typeof(return) address; 517 address.address[$ - 1] = 1; 518 return address; 519 } 520 521 /// 522 @nogc nothrow pure @safe unittest 523 { 524 assert(Address6.loopback().isLoopback()); 525 } 526 527 /** 528 * :: can represent any address. This function checks whether this 529 * address is ::. 530 * 531 * Returns: $(D_KEYWORD true) if this is an unspecified address, 532 * $(D_KEYWORD false) otherwise. 533 */ 534 bool isAny() const @nogc nothrow pure @safe 535 { 536 return this.address == any.address; 537 } 538 539 /// 540 @nogc nothrow @safe unittest 541 { 542 assert(address6("::").get.isAny()); 543 } 544 545 /** 546 * Loopback address is ::1. 547 * 548 * Returns: $(D_KEYWORD true) if this is a loopback address, 549 * $(D_KEYWORD false) otherwise. 550 */ 551 bool isLoopback() const @nogc nothrow pure @safe 552 { 553 return this.address == loopback.address; 554 } 555 556 /// 557 @nogc nothrow @safe unittest 558 { 559 assert(address6("::1").get.isLoopback()); 560 } 561 562 /** 563 * Determines whether this address' destination is a group of endpoints. 564 * 565 * Returns: $(D_KEYWORD true) if this is a multicast address, 566 * $(D_KEYWORD false) otherwise. 567 * 568 * See_Also: $(D_PSYMBOL isUnicast). 569 */ 570 bool isMulticast() const @nogc nothrow pure @safe 571 { 572 return this.address[0] == 0xff; 573 } 574 575 /// 576 @nogc nothrow @safe unittest 577 { 578 assert(address6("ff00::").get.isMulticast()); 579 } 580 581 /** 582 * Determines whether this address' destination is a single endpoint. 583 * 584 * Returns: $(D_KEYWORD true) if this is a multicast address, 585 * $(D_KEYWORD false) otherwise. 586 * 587 * See_Also: $(D_PSYMBOL isMulticast). 588 */ 589 bool isUnicast() const @nogc nothrow pure @safe 590 { 591 return !isMulticast(); 592 } 593 594 /// 595 @nogc nothrow @safe unittest 596 { 597 assert(address6("::1").get.isUnicast()); 598 } 599 600 /** 601 * Determines whether this address is a link-local unicast address. 602 * 603 * Returns: $(D_KEYWORD true) if this is a link-local address, 604 * $(D_KEYWORD false) otherwise. 605 */ 606 bool isLinkLocal() const @nogc nothrow pure @safe 607 { 608 return this.address[0] == 0xfe && (this.address[1] & 0xc0) == 0x80; 609 } 610 611 /// 612 @nogc nothrow @safe unittest 613 { 614 assert(address6("fe80::1").get.isLinkLocal()); 615 } 616 617 /** 618 * Determines whether this address is an Unique Local Address (ULA). 619 * 620 * Returns: $(D_KEYWORD true) if this is an Unique Local Address, 621 * $(D_KEYWORD false) otherwise. 622 */ 623 bool isUniqueLocal() const @nogc nothrow pure @safe 624 { 625 return this.address[0] == 0xfc || this.address[0] == 0xfd; 626 } 627 628 /// 629 @nogc nothrow @safe unittest 630 { 631 assert(address6("fd80:124e:34f3::1").get.isUniqueLocal()); 632 } 633 634 /** 635 * Writes text representation of this address to an output range. 636 * 637 * Params: 638 * OR = Type of the output range. 639 * output = Output range. 640 * 641 * Returns: $(D_PARAM output). 642 */ 643 OR toString(OR)(OR output) const 644 if (isOutputRange!(OR, const(char)[])) 645 { 646 ptrdiff_t largestGroupIndex = -1; 647 size_t largestGroupSize; 648 size_t zeroesInGroup; 649 size_t groupIndex; 650 651 // Look for the longest group of zeroes 652 for (size_t i; i < this.address.length; i += 2) 653 { 654 if (this.address[i] == 0 && this.address[i + 1] == 0) 655 { 656 if (zeroesInGroup++ == 0) 657 { 658 groupIndex = i; 659 } 660 } 661 else 662 { 663 zeroesInGroup = 0; 664 } 665 if (zeroesInGroup > largestGroupSize && zeroesInGroup > 1) 666 { 667 largestGroupSize = zeroesInGroup; 668 largestGroupIndex = groupIndex; 669 } 670 } 671 672 // Write the address 673 size_t i; 674 if (largestGroupIndex != 0) 675 { 676 writeGroup(output, i); 677 } 678 if (largestGroupIndex != -1) 679 { 680 while (i < largestGroupIndex) 681 { 682 put(output, ":"); 683 writeGroup(output, i); 684 } 685 put(output, "::"); 686 i += largestGroupSize + 2; 687 if (i < (this.address.length - 1)) 688 { 689 writeGroup(output, i); 690 } 691 } 692 693 while (i < this.address.length - 1) 694 { 695 put(output, ":"); 696 writeGroup(output, i); 697 } 698 699 return output; 700 } 701 702 /// 703 @nogc nothrow @safe unittest 704 { 705 import tanya.container..string : String; 706 import tanya.range : backInserter; 707 708 String actual; 709 710 address6("1:2:3:4:5:6:7:8").get.toString(backInserter(actual)); 711 assert(actual == "1:2:3:4:5:6:7:8"); 712 } 713 714 private void writeGroup(OR)(ref OR output, ref size_t i) const 715 { 716 ubyte low = this.address[i] & 0xf; 717 ubyte high = this.address[i] >> 4; 718 719 bool groupStarted = writeHexDigit!OR(output, high); 720 groupStarted = writeHexDigit!OR(output, low, groupStarted); 721 722 ++i; 723 low = this.address[i] & 0xf; 724 high = this.address[i] >> 4; 725 726 writeHexDigit!OR(output, high, groupStarted); 727 put(output, low.toHexDigit.singleton); 728 ++i; 729 } 730 731 /** 732 * Produces a byte array containing this address in network byte order. 733 * 734 * Returns: This address as raw bytes in network byte order. 735 */ 736 ubyte[16] toBytes() const @nogc nothrow pure @safe 737 { 738 return this.address; 739 } 740 741 /// 742 @nogc nothrow @safe unittest 743 { 744 auto actual = address6("1:2:3:4:5:6:7:8"); 745 ubyte[16] expected = [0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8]; 746 assert(actual.get.toBytes() == expected); 747 } 748 } 749 750 private void read2Bytes(R)(ref R range, ubyte[] address) 751 { 752 ushort group = readIntegral!ushort(range, 16); 753 address[0] = cast(ubyte) (group >> 8); 754 address[1] = group & 0xff; 755 } 756 757 private char toHexDigit(ubyte digit) @nogc nothrow pure @safe 758 in 759 { 760 assert(digit < 16); 761 } 762 do 763 { 764 return cast(char) (digit >= 10 ? (digit - 10 + 'a') : (digit + '0')); 765 } 766 767 private bool writeHexDigit(OR)(ref OR output, 768 ubyte digit, 769 bool groupStarted = false) 770 in 771 { 772 assert(digit < 16); 773 } 774 do 775 { 776 if (digit != 0 || groupStarted) 777 { 778 put(output, digit.toHexDigit.singleton); 779 return true; 780 } 781 return groupStarted; 782 } 783 784 /** 785 * Parses a string containing an IPv6 address. 786 * 787 * This function isn't pure since an IPv6 address can contain interface name 788 * or interface ID (separated from the address by `%`). If an interface name 789 * is specified (i.e. first character after `%` is not a digit), the parser 790 * tries to convert it to the ID of that interface. If the interface with the 791 * given name can't be found, the parser doesn't fail, but just ignores the 792 * invalid interface name, scope ID is `0` then. 793 * 794 * If an ID is given (i.e. first character after `%` is a digit), 795 * $(D_PSYMBOL address6) just stores it in $(D_PSYMBOL Address6.scopeID) without 796 * checking whether an interface with this ID really exists. If the ID is 797 * invalid (if it is too long or contains non decimal characters), parsing 798 * fails and nothing is returned. 799 * 800 * If neither an ID nor a name is given, $(D_PSYMBOL Address6.scopeID) is set 801 * to `0`. 802 * 803 * Params: 804 * R = Input range type. 805 * range = Stringish range containing the address. 806 * 807 * Returns: $(D_PSYMBOL Nullable) containing the address if the parsing was 808 * successful, or nothing otherwise. 809 */ 810 Nullable!Address6 address6(R)(R range) 811 if (isForwardRange!R && is(Unqual!(ElementType!R) == char) && hasLength!R) 812 { 813 if (range.empty) 814 { 815 return typeof(return)(); 816 } 817 Address6 result; 818 ubyte[12] tail; 819 size_t j; 820 821 // An address begins with a number, not ':'. But there is a special case 822 // if the address begins with '::'. 823 if (range.front == ':') 824 { 825 range.popFront(); 826 if (range.empty || range.front != ':') 827 { 828 return typeof(return)(); 829 } 830 range.popFront(); 831 goto ParseTail; 832 } 833 834 // Parse the address before '::'. 835 // This loop parses the whole address if it doesn't contain '::'. 836 static foreach (i; 0 .. 7) 837 { 838 { // To make "state" definition local 839 static if (i == 6) // Can be embedded IPv4 840 { 841 auto state = range.save(); 842 } 843 read2Bytes(range, result.address[i * 2 .. $]); 844 if (range.empty) 845 { 846 return typeof(return)(); 847 } 848 static if (i == 6) 849 { 850 if (range.front == '.') 851 { 852 swap(range, state); 853 goto ParseIPv4; 854 } 855 } 856 if (range.front != ':') 857 { 858 return typeof(return)(); 859 } 860 range.popFront(); 861 if (range.empty) 862 { 863 return typeof(return)(); 864 } 865 if (range.front == ':') 866 { 867 range.popFront(); 868 goto ParseTail; 869 } 870 } 871 } 872 read2Bytes(range, result.address[14 .. $]); 873 874 if (range.empty) 875 { 876 return typeof(return)(result); 877 } 878 else if (range.front == '%') 879 { 880 goto ParseIface; 881 } 882 else 883 { 884 return typeof(return)(); 885 } 886 887 ParseTail: // after :: 888 // Normally the address can't end with ':', but a special case is if the 889 // address ends with '::'. So the first iteration of the loop below is 890 // unrolled to check whether the address contains something after '::' at 891 // all. 892 if (range.empty) 893 { 894 return typeof(return)(result); // ends with :: 895 } 896 if (range.front == ':') 897 { 898 return typeof(return)(); 899 } 900 { // To make "state" definition local 901 auto state = range.save(); 902 903 read2Bytes(range, tail[j .. $]); 904 if (range.empty) 905 { 906 goto CopyTail; 907 } 908 else if (range.front == '%') 909 { 910 goto ParseIface; 911 } 912 else if (range.front == '.') 913 { 914 swap(range, state); 915 goto ParseIPv4; 916 } 917 else if (range.front != ':') 918 { 919 return typeof(return)(); 920 } 921 range.popFront(); 922 } 923 924 j = 2; 925 for (size_t i = 2; i <= 11; i += 2, j += 2, range.popFront()) 926 { 927 if (range.empty || range.front == ':') 928 { 929 return typeof(return)(); 930 } 931 auto state = range.save(); 932 read2Bytes(range, tail[j .. $]); 933 934 if (range.empty) 935 { 936 goto CopyTail; 937 } 938 else if (range.front == '%') 939 { 940 goto ParseIface; 941 } 942 else if (range.front == '.') 943 { 944 swap(range, state); 945 goto ParseIPv4; 946 } 947 else if (range.front != ':') 948 { 949 return typeof(return)(); 950 } 951 } 952 953 ParseIPv4: 954 // We know there is a number followed by '.'. We have to ensure this number 955 // is an octet 956 tail[j] = readIntegral!ubyte(range); 957 static foreach (i; 1 .. 4) 958 { 959 if (range.empty || range.front != '.') 960 { 961 return typeof(return)(); 962 } 963 range.popFront(); 964 if (range.empty) 965 { 966 return typeof(return)(); 967 } 968 tail[j + i] = readIntegral!ubyte(range); 969 } 970 j += 2; 971 972 if (range.empty) 973 { 974 goto CopyTail; 975 } 976 else if (range.front != '%') 977 { 978 return typeof(return)(); 979 } 980 981 ParseIface: // Scope name or ID 982 range.popFront(); 983 if (range.empty) 984 { 985 return typeof(return)(); 986 } 987 else if (isDigit(range.front)) 988 { 989 const scopeID = readIntegral!uint(range); 990 if (range.empty) 991 { 992 result.scopeID = scopeID; 993 } 994 else 995 { 996 return typeof(return)(); 997 } 998 } 999 else 1000 { 1001 result.scopeID = nameToIndex(range); 1002 } 1003 1004 CopyTail: 1005 copy(tail[0 .. j + 2], result.address[$ - j - 2 .. $]); 1006 return typeof(return)(result); 1007 } 1008 1009 /** 1010 * Constructs an $(D_PSYMBOL Address6) from raw bytes in network byte order and 1011 * the scope ID. 1012 * 1013 * Params: 1014 * R = Input range type. 1015 * range = $(D_KEYWORD ubyte) range containing the address. 1016 * scopeID = Scope ID. 1017 * 1018 * Returns: $(D_PSYMBOL Nullable) containing the address if the $(D_PARAM range) 1019 * contains exactly 16 bytes, or nothing otherwise. 1020 */ 1021 Nullable!Address6 address6(R)(R range, uint scopeID = 0) 1022 if (isInputRange!R && is(Unqual!(ElementType!R) == ubyte)) 1023 { 1024 Address6 result; 1025 int i; 1026 1027 for (; i < 16 && !range.empty; ++i, range.popFront()) 1028 { 1029 result.address[i] = range.front; 1030 } 1031 result.scopeID = scopeID; 1032 1033 return range.empty && i == 16 ? typeof(return)(result) : typeof(return)(); 1034 } 1035 1036 /// 1037 @nogc nothrow pure @safe unittest 1038 { 1039 { 1040 ubyte[16] actual = [ 1, 2, 3, 4, 5, 6, 7, 8, 1041 9, 10, 11, 12, 13, 14, 15, 16 ]; 1042 assert(!address6(actual[]).isNull); 1043 } 1044 { 1045 ubyte[15] actual = [ 1, 2, 3, 4, 5, 6, 7, 8, 1046 9, 10, 11, 12, 13, 14, 15 ]; 1047 assert(address6(actual[]).isNull); 1048 } 1049 { 1050 ubyte[17] actual = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 1051 10, 11, 12, 13, 14, 15, 16, 17 ]; 1052 assert(address6(actual[]).isNull); 1053 } 1054 { 1055 assert(address6(cast(ubyte[]) []).isNull); 1056 } 1057 } 1058 1059 /** 1060 * Address storage, that can hold either an IPv4 or IPv6 address. 1061 */ 1062 struct Address 1063 { 1064 private Variant!(Address4, Address6) address; 1065 1066 @disable this(); 1067 1068 /** 1069 * Initializes the addres with an IPv4 address. 1070 * 1071 * Params: 1072 * address = IPv6 address. 1073 */ 1074 this(Address4 address) @nogc nothrow pure @safe 1075 { 1076 this.address = address; 1077 } 1078 1079 /** 1080 * Initializes the addres with an IPv4 address. 1081 * 1082 * Params: 1083 * address = IPv6 address. 1084 */ 1085 this(Address6 address) @nogc nothrow pure @safe 1086 { 1087 this.address = address; 1088 } 1089 1090 /** 1091 * Determines whether this is an IPv4 address. 1092 * 1093 * Returns: $(D_KEYWORD true) if this is an IPv4 address, 1094 * $(D_KEYWORD false) otherwise. 1095 */ 1096 bool isV4() const @nogc nothrow pure @safe 1097 { 1098 return this.address.peek!Address4; 1099 } 1100 1101 /// 1102 @nogc nothrow pure @safe unittest 1103 { 1104 assert(Address(Address4.any()).isV4()); 1105 } 1106 1107 /** 1108 * Determines whether this is an IPv6 address. 1109 * 1110 * Returns: $(D_KEYWORD true) if this is an IPv6 address, 1111 * $(D_KEYWORD false) otherwise. 1112 */ 1113 bool isV6() const @nogc nothrow pure @safe 1114 { 1115 return this.address.peek!Address6; 1116 } 1117 1118 /// 1119 @nogc nothrow pure @safe unittest 1120 { 1121 assert(Address(Address6.any()).isV6()); 1122 } 1123 1124 /** 1125 * Get the address as an IPv4 address. 1126 * 1127 * This method doesn't convert the address, so the address should be 1128 * already an IPv4 one. 1129 * 1130 * Returns: IPv4 address. 1131 * 1132 * Precondition: This is an IPv4 address. 1133 */ 1134 ref inout(Address4) toV4() inout @nogc nothrow pure @safe 1135 in 1136 { 1137 assert(this.address.peek!Address4); 1138 } 1139 do 1140 { 1141 return this.address.get!Address4; 1142 } 1143 1144 /// 1145 @nogc nothrow pure @safe unittest 1146 { 1147 auto expected = Address4.loopback; 1148 assert(Address(expected).toV4() == expected); 1149 } 1150 1151 /** 1152 * Get the address as an IPv6 address. 1153 * 1154 * This method doesn't convert the address, so the address should be 1155 * already an IPv6 one. 1156 * 1157 * Returns: IPv6 address. 1158 * 1159 * Precondition: This is an IPv6 address. 1160 */ 1161 ref inout(Address6) toV6() inout @nogc nothrow pure @safe 1162 in 1163 { 1164 assert(this.address.peek!Address6); 1165 } 1166 do 1167 { 1168 return this.address.get!Address6; 1169 } 1170 1171 /// 1172 @nogc nothrow pure @safe unittest 1173 { 1174 auto expected = Address6.loopback; 1175 assert(Address(expected).toV6() == expected); 1176 } 1177 1178 /** 1179 * Determines whether this is a loopback address. 1180 * 1181 * Returns: $(D_KEYWORD true) if this is a loopback address, 1182 * $(D_KEYWORD false) otherwise. 1183 * 1184 * See_Also: $(D_PSYMBOL Address4.loopback), 1185 * $(D_PSYMBOL Address6.loopback). 1186 */ 1187 bool isLoopback() const @nogc nothrow pure @safe 1188 in 1189 { 1190 assert(this.address.hasValue); 1191 } 1192 do 1193 { 1194 if (this.address.peek!Address4) 1195 { 1196 return this.address.get!Address4.isLoopback(); 1197 } 1198 return this.address.get!Address6.isLoopback(); 1199 } 1200 1201 /// 1202 @nogc nothrow pure @safe unittest 1203 { 1204 assert(Address(Address4.loopback()).isLoopback()); 1205 assert(Address(Address6.loopback()).isLoopback()); 1206 } 1207 1208 /** 1209 * Determines whether this address' destination is a group of endpoints. 1210 * 1211 * Returns: $(D_KEYWORD true) if this is a multicast address, 1212 * $(D_KEYWORD false) otherwise. 1213 * 1214 * See_Also: $(D_PSYMBOL Address4.isMulticast), 1215 * $(D_PSYMBOL Address6.isMulticast). 1216 */ 1217 bool isMulticast() const @nogc nothrow pure @safe 1218 in 1219 { 1220 assert(this.address.hasValue); 1221 } 1222 do 1223 { 1224 if (this.address.peek!Address4) 1225 { 1226 return this.address.get!Address4.isMulticast(); 1227 } 1228 return this.address.get!Address6.isMulticast(); 1229 } 1230 1231 /// 1232 @nogc nothrow @safe unittest 1233 { 1234 assert(Address(address4("224.0.0.3").get).isMulticast()); 1235 assert(Address(address6("ff00::").get).isMulticast()); 1236 } 1237 1238 /** 1239 * Determines whether this is an unspecified address. 1240 * 1241 * Returns: $(D_KEYWORD true) if this is an unspecified address, 1242 * $(D_KEYWORD false) otherwise. 1243 * 1244 * See_Also: $(D_PSYMBOL Address4.isAny), $(D_PSYMBOL Address6.isAny). 1245 */ 1246 bool isAny() const @nogc nothrow pure @safe 1247 in 1248 { 1249 assert(this.address.hasValue); 1250 } 1251 do 1252 { 1253 if (this.address.peek!Address4) 1254 { 1255 return this.address.get!Address4.isAny(); 1256 } 1257 return this.address.get!Address6.isAny(); 1258 } 1259 1260 /// 1261 @nogc nothrow pure @safe unittest 1262 { 1263 assert(Address(Address4.any).isAny()); 1264 assert(Address(Address6.any).isAny()); 1265 } 1266 1267 /** 1268 * Compares two addresses for equality. 1269 * 1270 * Params: 1271 * T = The type of the other address. It can be $(D_PSYMBOL Address), 1272 * $(D_PSYMBOL Address4) or $(D_PSYMBOL Address6). 1273 * that = The address to compare with. 1274 * 1275 * Returns: $(D_KEYWORD true) if this and $(D_PARAM that) addresses are 1276 * representations of the same IP address, $(D_KEYWORD false) 1277 * otherwise. 1278 */ 1279 bool opEquals(T)(T that) const 1280 if (is(Unqual!T == Address4) || is(Unqual!T == Address6)) 1281 { 1282 alias AddressType = Unqual!T; 1283 if (this.address.peek!AddressType) 1284 { 1285 return this.address.get!AddressType == that; 1286 } 1287 return false; 1288 } 1289 1290 /// 1291 @nogc nothrow pure @safe unittest 1292 { 1293 assert(Address(Address4.loopback) == Address4.loopback); 1294 assert(Address(Address6.loopback) == Address6.loopback); 1295 assert(Address(Address4.loopback) != Address6.loopback); 1296 } 1297 1298 /// ditto 1299 bool opEquals(T)(T that) const 1300 if (is(Unqual!T == Address)) 1301 { 1302 return this.address == that.address; 1303 } 1304 1305 /// 1306 @nogc nothrow pure @safe unittest 1307 { 1308 assert(Address(Address6.loopback) == Address(Address6.loopback)); 1309 assert(Address(Address4.loopback) != Address(Address6.loopback)); 1310 } 1311 1312 ref Address opAssign(T)(T that) 1313 if (is(Unqual!T == Address4) || is(Unqual!T == Address6)) 1314 { 1315 this.address = that; 1316 return this; 1317 } 1318 1319 /// 1320 @nogc nothrow pure @safe unittest 1321 { 1322 Address address = Address4.any; 1323 address = Address4.loopback; 1324 assert(address == Address4.loopback); 1325 } 1326 } 1327 1328 /** 1329 * Service endpoint specified by a version independent IP address and port. 1330 */ 1331 struct Endpoint 1332 { 1333 private AddressFamily family = AddressFamily.unspec; 1334 private ubyte[ushort.sizeof] service; 1335 private Address4 address4; // Unused sin6_flowinfo if IPv6 1336 private Address6 address6; // Unused if IPv4 1337 1338 static assert(Address4.sizeof == 4); 1339 1340 /// Allows the system to select a free port. 1341 enum ushort anyPort = 0; 1342 1343 /** 1344 * Constructs an endpoint. 1345 * 1346 * Params: 1347 * T = Address type (IPv4 or IPv6). 1348 * address = IP address that should be associated with the endpoint. 1349 * port = Port number in network byte order. 1350 */ 1351 this(T)(T address, const ushort port = anyPort) 1352 if (is(T == Address) || is(T == Address4) || is(T == Address6)) 1353 { 1354 this.address = address; 1355 this.port = port; 1356 } 1357 1358 /** 1359 * Returns: Port number in network byte order. 1360 */ 1361 @property inout(ushort) port() inout const @nogc nothrow pure @safe 1362 { 1363 return this.service[].toHostOrder!ushort(); 1364 } 1365 1366 /** 1367 * Params: 1368 * port = Port number in network byte order. 1369 */ 1370 @property void port(const ushort port) @nogc nothrow pure @safe 1371 { 1372 NetworkOrder!(ushort.sizeof)(port).copy(this.service[]); 1373 } 1374 1375 /** 1376 * Returns: IP address associated with the endpoint. 1377 */ 1378 @property inout(Address) address() inout @nogc nothrow pure @safe 1379 { 1380 if (this.family == AddressFamily.inet) 1381 { 1382 return Address(this.address4); 1383 } 1384 else if (this.family == AddressFamily.inet6) 1385 { 1386 return Address(this.address6); 1387 } 1388 return Address.init; 1389 } 1390 1391 /** 1392 * Params: 1393 * address = IP address associated with the endpoint. 1394 */ 1395 @property void address(Address address) @nogc nothrow pure @safe 1396 { 1397 if (address.isV4()) 1398 { 1399 this.address = address.toV4(); 1400 } 1401 else if (address.isV6()) 1402 { 1403 this.address = address.toV6(); 1404 } 1405 } 1406 1407 /// ditto 1408 @property void address(Address4 address) @nogc nothrow pure @safe 1409 { 1410 this.family = AddressFamily.inet; 1411 this.address4 = address; 1412 } 1413 1414 /// ditto 1415 @property void address(Address6 address) @nogc nothrow pure @safe 1416 { 1417 this.family = AddressFamily.inet6; 1418 this.address4 = Address4(0); 1419 this.address6 = address; 1420 } 1421 }