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 }