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 }