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.sumtype; 20 import std.typecons; 21 import tanya.algorithm.iteration; 22 import tanya.algorithm.mutation; 23 import tanya.container.string; 24 import tanya.conv; 25 import tanya.format; 26 import tanya.memory.lifetime; 27 import tanya.meta.trait; 28 import tanya.meta.transform; 29 import tanya.net.iface; 30 import tanya.net.inet; 31 import tanya.range; 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 SumType!(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.match!( 1099 (Address4 address4) => true, 1100 (Address6 address6) => false 1101 ); 1102 } 1103 1104 /// 1105 @nogc nothrow pure @safe unittest 1106 { 1107 assert(Address(Address4.any()).isV4()); 1108 } 1109 1110 /** 1111 * Determines whether this is an IPv6 address. 1112 * 1113 * Returns: $(D_KEYWORD true) if this is an IPv6 address, 1114 * $(D_KEYWORD false) otherwise. 1115 */ 1116 bool isV6() const @nogc nothrow pure @safe 1117 { 1118 return this.address.match!( 1119 (Address4 address4) => false, 1120 (Address6 address6) => true 1121 ); 1122 } 1123 1124 /// 1125 @nogc nothrow pure @safe unittest 1126 { 1127 assert(Address(Address6.any()).isV6()); 1128 } 1129 1130 /** 1131 * Get the address as an IPv4 address. 1132 * 1133 * This method doesn't convert the address, so the address should be 1134 * already an IPv4 one. 1135 * 1136 * Returns: IPv4 address. 1137 * 1138 * Precondition: This is an IPv4 address. 1139 */ 1140 Address4 toV4() inout @nogc nothrow pure @safe 1141 { 1142 return this.address.match!( 1143 (Address4 address4) => address4, 1144 _ => assert(false, "Not an IPv4 address") 1145 ); 1146 } 1147 1148 /// 1149 @nogc nothrow pure @safe unittest 1150 { 1151 auto expected = Address4.loopback; 1152 assert(Address(expected).toV4() == expected); 1153 } 1154 1155 /** 1156 * Get the address as an IPv6 address. 1157 * 1158 * This method doesn't convert the address, so the address should be 1159 * already an IPv6 one. 1160 * 1161 * Returns: IPv6 address. 1162 * 1163 * Precondition: This is an IPv6 address. 1164 */ 1165 Address6 toV6() inout @nogc nothrow pure @safe 1166 { 1167 return this.address.match!( 1168 (Address6 address6) => address6, 1169 _ => assert(false, "Not an IPv6 address") 1170 ); 1171 } 1172 1173 /// 1174 @nogc nothrow pure @safe unittest 1175 { 1176 auto expected = Address6.loopback; 1177 assert(Address(expected).toV6() == expected); 1178 } 1179 1180 /** 1181 * Determines whether this is a loopback address. 1182 * 1183 * Returns: $(D_KEYWORD true) if this is a loopback address, 1184 * $(D_KEYWORD false) otherwise. 1185 * 1186 * See_Also: $(D_PSYMBOL Address4.loopback), 1187 * $(D_PSYMBOL Address6.loopback). 1188 */ 1189 bool isLoopback() const @nogc nothrow pure @safe 1190 { 1191 return this.address.match!( 1192 (Address4 address) => address.isLoopback(), 1193 (Address6 address) => address.isLoopback() 1194 ); 1195 } 1196 1197 /// 1198 @nogc nothrow pure @safe unittest 1199 { 1200 assert(Address(Address4.loopback()).isLoopback()); 1201 assert(Address(Address6.loopback()).isLoopback()); 1202 } 1203 1204 /** 1205 * Determines whether this address' destination is a group of endpoints. 1206 * 1207 * Returns: $(D_KEYWORD true) if this is a multicast address, 1208 * $(D_KEYWORD false) otherwise. 1209 * 1210 * See_Also: $(D_PSYMBOL Address4.isMulticast), 1211 * $(D_PSYMBOL Address6.isMulticast). 1212 */ 1213 bool isMulticast() const @nogc nothrow pure @safe 1214 { 1215 return this.address.match!( 1216 (Address4 address) => address.isMulticast(), 1217 (Address6 address) => address.isMulticast() 1218 ); 1219 } 1220 1221 /// 1222 @nogc nothrow @safe unittest 1223 { 1224 assert(Address(address4("224.0.0.3").get).isMulticast()); 1225 assert(Address(address6("ff00::").get).isMulticast()); 1226 } 1227 1228 /** 1229 * Determines whether this is an unspecified address. 1230 * 1231 * Returns: $(D_KEYWORD true) if this is an unspecified address, 1232 * $(D_KEYWORD false) otherwise. 1233 * 1234 * See_Also: $(D_PSYMBOL Address4.isAny), $(D_PSYMBOL Address6.isAny). 1235 */ 1236 bool isAny() const @nogc nothrow pure @safe 1237 { 1238 return this.address.match!( 1239 (Address4 address) => address.isAny(), 1240 (Address6 address) => address.isAny() 1241 ); 1242 } 1243 1244 /// 1245 @nogc nothrow pure @safe unittest 1246 { 1247 assert(Address(Address4.any).isAny()); 1248 assert(Address(Address6.any).isAny()); 1249 } 1250 1251 /** 1252 * Compares two addresses for equality. 1253 * 1254 * Params: 1255 * T = The type of the other address. It can be $(D_PSYMBOL Address), 1256 * $(D_PSYMBOL Address4) or $(D_PSYMBOL Address6). 1257 * that = The address to compare with. 1258 * 1259 * Returns: $(D_KEYWORD true) if this and $(D_PARAM that) addresses are 1260 * representations of the same IP address, $(D_KEYWORD false) 1261 * otherwise. 1262 */ 1263 bool opEquals(T)(T that) const 1264 if (is(Unqual!T == Address4)) 1265 { 1266 return this.address.match!( 1267 (Address4 address) => address == that, 1268 (Address6 address) => false 1269 ); 1270 } 1271 1272 /// 1273 bool opEquals(T)(T that) const 1274 if (is(Unqual!T == Address6)) 1275 { 1276 return this.address.match!( 1277 (Address4 address) => false, 1278 (Address6 address) => address == that, 1279 ); 1280 } 1281 1282 /// 1283 @nogc nothrow pure @safe unittest 1284 { 1285 assert(Address(Address4.loopback) == Address4.loopback); 1286 assert(Address(Address6.loopback) == Address6.loopback); 1287 assert(Address(Address4.loopback) != Address6.loopback); 1288 } 1289 1290 /// ditto 1291 bool opEquals(T)(T that) const 1292 if (is(Unqual!T == Address)) 1293 { 1294 return this.address == that.address; 1295 } 1296 1297 /// 1298 @nogc nothrow pure @safe unittest 1299 { 1300 assert(Address(Address6.loopback) == Address(Address6.loopback)); 1301 assert(Address(Address4.loopback) != Address(Address6.loopback)); 1302 } 1303 1304 ref Address opAssign(T)(T that) 1305 if (is(Unqual!T == Address4) || is(Unqual!T == Address6)) 1306 { 1307 this.address = that; 1308 return this; 1309 } 1310 1311 /// 1312 @nogc nothrow pure @safe unittest 1313 { 1314 Address address = Address4.any; 1315 address = Address4.loopback; 1316 assert(address == Address4.loopback); 1317 } 1318 } 1319 1320 /** 1321 * Service endpoint specified by a version independent IP address and port. 1322 */ 1323 struct Endpoint 1324 { 1325 private AddressFamily family = AddressFamily.unspec; 1326 private ubyte[ushort.sizeof] service; 1327 private Address4 address4; // Unused sin6_flowinfo if IPv6 1328 private Address6 address6; // Unused if IPv4 1329 1330 static assert(Address4.sizeof == 4); 1331 1332 /// Allows the system to select a free port. 1333 enum ushort anyPort = 0; 1334 1335 /** 1336 * Constructs an endpoint. 1337 * 1338 * Params: 1339 * T = Address type (IPv4 or IPv6). 1340 * address = IP address that should be associated with the endpoint. 1341 * port = Port number in network byte order. 1342 */ 1343 this(T)(T address, const ushort port = anyPort) 1344 if (is(T == Address) || is(T == Address4) || is(T == Address6)) 1345 { 1346 this.address = address; 1347 this.port = port; 1348 } 1349 1350 /** 1351 * Returns: Port number in network byte order. 1352 */ 1353 @property inout(ushort) port() inout const @nogc nothrow pure @safe 1354 { 1355 return this.service[].toHostOrder!ushort(); 1356 } 1357 1358 /** 1359 * Params: 1360 * port = Port number in network byte order. 1361 */ 1362 @property void port(const ushort port) @nogc nothrow pure @safe 1363 { 1364 NetworkOrder!(ushort.sizeof)(port).copy(this.service[]); 1365 } 1366 1367 /** 1368 * Returns: IP address associated with the endpoint. 1369 */ 1370 @property inout(Address) address() inout @nogc nothrow pure @safe 1371 { 1372 if (this.family == AddressFamily.inet) 1373 { 1374 return Address(this.address4); 1375 } 1376 else if (this.family == AddressFamily.inet6) 1377 { 1378 return Address(this.address6); 1379 } 1380 return Address.init; 1381 } 1382 1383 /** 1384 * Params: 1385 * address = IP address associated with the endpoint. 1386 */ 1387 @property void address(Address address) @nogc nothrow pure @safe 1388 { 1389 if (address.isV4()) 1390 { 1391 this.address = address.toV4(); 1392 } 1393 else if (address.isV6()) 1394 { 1395 this.address = address.toV6(); 1396 } 1397 } 1398 1399 /// ditto 1400 @property void address(Address4 address) @nogc nothrow pure @safe 1401 { 1402 this.family = AddressFamily.inet; 1403 this.address4 = address; 1404 } 1405 1406 /// ditto 1407 @property void address(Address6 address) @nogc nothrow pure @safe 1408 { 1409 this.family = AddressFamily.inet6; 1410 this.address4 = Address4(0); 1411 this.address6 = address; 1412 } 1413 }