coreplus/net/ip.rs
1use crate::io::Write as _;
2use core::{
3    cmp::Ordering,
4    fmt::{self, Write as FmtWrite},
5    str,
6};
7
8/// An IP address, either IPv4 or IPv6.
9///
10/// This enum can contain either an [`Ipv4Addr`] or an [`Ipv6Addr`], see their
11/// respective documentation for more details.
12///
13/// The size of an `IpAddr` instance may vary depending on the target operating
14/// system.
15///
16/// # Examples
17///
18/// ```
19/// use coreplus::net::{IpAddr, Ipv4Addr, Ipv6Addr};
20///
21/// let localhost_v4 = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
22/// let localhost_v6 = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
23///
24/// assert_eq!("127.0.0.1".parse(), Ok(localhost_v4));
25/// assert_eq!("::1".parse(), Ok(localhost_v6));
26///
27/// assert_eq!(localhost_v4.is_ipv6(), false);
28/// assert_eq!(localhost_v4.is_ipv4(), true);
29/// ```
30#[derive(Copy, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
31pub enum IpAddr {
32    /// An IPv4 address.
33    V4(Ipv4Addr),
34    /// An IPv6 address.
35    V6(Ipv6Addr),
36}
37
38/// An IPv4 address.
39///
40/// IPv4 addresses are defined as 32-bit integers in [IETF RFC 791].
41/// They are usually represented as four octets.
42///
43/// See [`IpAddr`] for a type encompassing both IPv4 and IPv6 addresses.
44///
45/// The size of an `Ipv4Addr` struct may vary depending on the target operating
46/// system.
47///
48/// [IETF RFC 791]: https://tools.ietf.org/html/rfc791
49///
50/// # Textual representation
51///
52/// `Ipv4Addr` provides a [`FromStr`] implementation. The four octets are in decimal
53/// notation, divided by `.` (this is called "dot-decimal notation").
54/// Notably, octal numbers and hexadecimal numbers are not allowed per [IETF RFC 6943].
55///
56/// [IETF RFC 6943]: https://tools.ietf.org/html/rfc6943#section-3.1.1
57/// [`FromStr`]: core::str::FromStr
58///
59/// # Examples
60///
61/// ```
62/// use coreplus::net::Ipv4Addr;
63///
64/// let localhost = Ipv4Addr::new(127, 0, 0, 1);
65/// assert_eq!("127.0.0.1".parse(), Ok(localhost));
66/// assert_eq!(localhost.is_loopback(), true);
67/// ```
68#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Hash)]
69pub struct Ipv4Addr {
70    inner: u32,
71}
72
73/// An IPv6 address.
74///
75/// IPv6 addresses are defined as 128-bit integers in [IETF RFC 4291].
76/// They are usually represented as eight 16-bit segments.
77///
78/// See [`IpAddr`] for a type encompassing both IPv4 and IPv6 addresses.
79///
80/// The size of an `Ipv6Addr` struct may vary depending on the target operating
81/// system.
82///
83/// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291
84///
85/// # Textual representation
86///
87/// `Ipv6Addr` provides a [`FromStr`] implementation. There are many ways to represent
88/// an IPv6 address in text, but in general, each segments is written in hexadecimal
89/// notation, and segments are separated by `:`. For more information, see
90/// [IETF RFC 5952].
91///
92/// [`FromStr`]: core::str::FromStr
93/// [IETF RFC 5952]: https://tools.ietf.org/html/rfc5952
94///
95/// # Examples
96///
97/// ```
98/// use coreplus::net::Ipv6Addr;
99///
100/// let localhost = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
101/// assert_eq!("::1".parse(), Ok(localhost));
102/// assert_eq!(localhost.is_loopback(), true);
103/// ```
104#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Hash)]
105pub struct Ipv6Addr {
106    inner: [u8; 16],
107}
108
109#[allow(missing_docs)]
110#[derive(Copy, PartialEq, Eq, Clone, Hash, Debug)]
111pub enum Ipv6MulticastScope {
112    InterfaceLocal,
113    LinkLocal,
114    RealmLocal,
115    AdminLocal,
116    SiteLocal,
117    OrganizationLocal,
118    Global,
119}
120
121impl IpAddr {
122    /// Returns [`true`] for the special 'unspecified' address.
123    ///
124    /// See the documentation for [`Ipv4Addr::is_unspecified()`] and
125    /// [`Ipv6Addr::is_unspecified()`] for more details.
126    ///
127    /// # Examples
128    ///
129    /// ```
130    /// use coreplus::net::{IpAddr, Ipv4Addr, Ipv6Addr};
131    ///
132    /// assert_eq!(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)).is_unspecified(), true);
133    /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)).is_unspecified(), true);
134    /// ```
135    #[inline]
136    pub const fn is_unspecified(&self) -> bool {
137        match self {
138            IpAddr::V4(ip) => ip.is_unspecified(),
139            IpAddr::V6(ip) => ip.is_unspecified(),
140        }
141    }
142
143    /// Returns [`true`] if this is a loopback address.
144    ///
145    /// See the documentation for [`Ipv4Addr::is_loopback()`] and
146    /// [`Ipv6Addr::is_loopback()`] for more details.
147    ///
148    /// # Examples
149    ///
150    /// ```
151    /// use coreplus::net::{IpAddr, Ipv4Addr, Ipv6Addr};
152    ///
153    /// assert_eq!(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)).is_loopback(), true);
154    /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0x1)).is_loopback(), true);
155    /// ```
156    #[inline]
157    pub const fn is_loopback(&self) -> bool {
158        match self {
159            IpAddr::V4(ip) => ip.is_loopback(),
160            IpAddr::V6(ip) => ip.is_loopback(),
161        }
162    }
163
164    /// Returns [`true`] if the address appears to be globally routable.
165    ///
166    /// See the documentation for [`Ipv4Addr::is_global()`] and
167    /// [`Ipv6Addr::is_global()`] for more details.
168    ///
169    /// # Examples
170    ///
171    /// ```
172    /// #![feature(ip)]
173    ///
174    /// use coreplus::net::{IpAddr, Ipv4Addr, Ipv6Addr};
175    ///
176    /// assert_eq!(IpAddr::V4(Ipv4Addr::new(80, 9, 12, 3)).is_global(), true);
177    /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0x1c9, 0, 0, 0xafc8, 0, 0x1)).is_global(), true);
178    /// ```
179    #[inline]
180    pub const fn is_global(&self) -> bool {
181        match self {
182            IpAddr::V4(ip) => ip.is_global(),
183            IpAddr::V6(ip) => ip.is_global(),
184        }
185    }
186
187    /// Returns [`true`] if this is a multicast address.
188    ///
189    /// See the documentation for [`Ipv4Addr::is_multicast()`] and
190    /// [`Ipv6Addr::is_multicast()`] for more details.
191    ///
192    /// # Examples
193    ///
194    /// ```
195    /// use coreplus::net::{IpAddr, Ipv4Addr, Ipv6Addr};
196    ///
197    /// assert_eq!(IpAddr::V4(Ipv4Addr::new(224, 254, 0, 0)).is_multicast(), true);
198    /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0)).is_multicast(), true);
199    /// ```
200    #[inline]
201    pub const fn is_multicast(&self) -> bool {
202        match self {
203            IpAddr::V4(ip) => ip.is_multicast(),
204            IpAddr::V6(ip) => ip.is_multicast(),
205        }
206    }
207
208    /// Returns [`true`] if this address is in a range designated for documentation.
209    ///
210    /// See the documentation for [`Ipv4Addr::is_documentation()`] and
211    /// [`Ipv6Addr::is_documentation()`] for more details.
212    ///
213    /// # Examples
214    ///
215    /// ```
216    /// #![feature(ip)]
217    ///
218    /// use coreplus::net::{IpAddr, Ipv4Addr, Ipv6Addr};
219    ///
220    /// assert_eq!(IpAddr::V4(Ipv4Addr::new(203, 0, 113, 6)).is_documentation(), true);
221    /// assert_eq!(
222    ///     IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0)).is_documentation(),
223    ///     true
224    /// );
225    /// ```
226    #[inline]
227    pub const fn is_documentation(&self) -> bool {
228        match self {
229            IpAddr::V4(ip) => ip.is_documentation(),
230            IpAddr::V6(ip) => ip.is_documentation(),
231        }
232    }
233
234    /// Returns [`true`] if this address is an [`IPv4` address], and [`false`]
235    /// otherwise.
236    ///
237    /// [`IPv4` address]: IpAddr::V4
238    ///
239    /// # Examples
240    ///
241    /// ```
242    /// use coreplus::net::{IpAddr, Ipv4Addr, Ipv6Addr};
243    ///
244    /// assert_eq!(IpAddr::V4(Ipv4Addr::new(203, 0, 113, 6)).is_ipv4(), true);
245    /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0)).is_ipv4(), false);
246    /// ```
247    #[inline]
248    pub const fn is_ipv4(&self) -> bool {
249        matches!(self, IpAddr::V4(_))
250    }
251
252    /// Returns [`true`] if this address is an [`IPv6` address], and [`false`]
253    /// otherwise.
254    ///
255    /// [`IPv6` address]: IpAddr::V6
256    ///
257    /// # Examples
258    ///
259    /// ```
260    /// use coreplus::net::{IpAddr, Ipv4Addr, Ipv6Addr};
261    ///
262    /// assert_eq!(IpAddr::V4(Ipv4Addr::new(203, 0, 113, 6)).is_ipv6(), false);
263    /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0)).is_ipv6(), true);
264    /// ```
265    #[inline]
266    pub const fn is_ipv6(&self) -> bool {
267        matches!(self, IpAddr::V6(_))
268    }
269}
270
271impl Ipv4Addr {
272    /// Creates a new IPv4 address from four eight-bit octets.
273    ///
274    /// The result will represent the IP address `a`.`b`.`c`.`d`.
275    ///
276    /// # Examples
277    ///
278    /// ```
279    /// use coreplus::net::Ipv4Addr;
280    ///
281    /// let addr = Ipv4Addr::new(127, 0, 0, 1);
282    /// ```
283    #[inline]
284    pub const fn new(a: u8, b: u8, c: u8, d: u8) -> Ipv4Addr {
285        // `s_addr` is stored as BE on all machine and the array is in BE order.
286        // So the native endian conversion method is used so that it's never swapped.
287        Ipv4Addr {
288            inner: u32::from_ne_bytes([a, b, c, d]),
289        }
290    }
291
292    /// An IPv4 address with the address pointing to localhost: `127.0.0.1`
293    ///
294    /// # Examples
295    ///
296    /// ```
297    /// use coreplus::net::Ipv4Addr;
298    ///
299    /// let addr = Ipv4Addr::LOCALHOST;
300    /// assert_eq!(addr, Ipv4Addr::new(127, 0, 0, 1));
301    /// ```
302    pub const LOCALHOST: Self = Ipv4Addr::new(127, 0, 0, 1);
303
304    /// An IPv4 address representing an unspecified address: `0.0.0.0`
305    ///
306    /// This corresponds to the constant `INADDR_ANY` in other languages.
307    ///
308    /// # Examples
309    ///
310    /// ```
311    /// use coreplus::net::Ipv4Addr;
312    ///
313    /// let addr = Ipv4Addr::UNSPECIFIED;
314    /// assert_eq!(addr, Ipv4Addr::new(0, 0, 0, 0));
315    /// ```
316    #[doc(alias = "INADDR_ANY")]
317    pub const UNSPECIFIED: Self = Ipv4Addr::new(0, 0, 0, 0);
318
319    /// An IPv4 address representing the broadcast address: `255.255.255.255`
320    ///
321    /// # Examples
322    ///
323    /// ```
324    /// use coreplus::net::Ipv4Addr;
325    ///
326    /// let addr = Ipv4Addr::BROADCAST;
327    /// assert_eq!(addr, Ipv4Addr::new(255, 255, 255, 255));
328    /// ```
329    pub const BROADCAST: Self = Ipv4Addr::new(255, 255, 255, 255);
330
331    /// Returns the four eight-bit integers that make up this address.
332    ///
333    /// # Examples
334    ///
335    /// ```
336    /// use coreplus::net::Ipv4Addr;
337    ///
338    /// let addr = Ipv4Addr::new(127, 0, 0, 1);
339    /// assert_eq!(addr.octets(), [127, 0, 0, 1]);
340    /// ```
341    #[inline]
342    pub const fn octets(&self) -> [u8; 4] {
343        // This returns the order we want because s_addr is stored in big-endian.
344        self.inner.to_ne_bytes()
345    }
346
347    /// Returns [`true`] for the special 'unspecified' address (`0.0.0.0`).
348    ///
349    /// This property is defined in _UNIX Network Programming, Second Edition_,
350    /// W. Richard Stevens, p. 891; see also [ip7].
351    ///
352    /// [ip7]: https://man7.org/linux/man-pages/man7/ip.7.html
353    ///
354    /// # Examples
355    ///
356    /// ```
357    /// use coreplus::net::Ipv4Addr;
358    ///
359    /// assert_eq!(Ipv4Addr::new(0, 0, 0, 0).is_unspecified(), true);
360    /// assert_eq!(Ipv4Addr::new(45, 22, 13, 197).is_unspecified(), false);
361    /// ```
362    #[inline]
363    pub const fn is_unspecified(&self) -> bool {
364        self.inner == 0
365    }
366
367    /// Returns [`true`] if this is a loopback address (`127.0.0.0/8`).
368    ///
369    /// This property is defined by [IETF RFC 1122].
370    ///
371    /// [IETF RFC 1122]: https://tools.ietf.org/html/rfc1122
372    ///
373    /// # Examples
374    ///
375    /// ```
376    /// use coreplus::net::Ipv4Addr;
377    ///
378    /// assert_eq!(Ipv4Addr::new(127, 0, 0, 1).is_loopback(), true);
379    /// assert_eq!(Ipv4Addr::new(45, 22, 13, 197).is_loopback(), false);
380    /// ```
381    #[inline]
382    pub const fn is_loopback(&self) -> bool {
383        self.octets()[0] == 127
384    }
385
386    /// Returns [`true`] if this is a private address.
387    ///
388    /// The private address ranges are defined in [IETF RFC 1918] and include:
389    ///
390    ///  - `10.0.0.0/8`
391    ///  - `172.16.0.0/12`
392    ///  - `192.168.0.0/16`
393    ///
394    /// [IETF RFC 1918]: https://tools.ietf.org/html/rfc1918
395    ///
396    /// # Examples
397    ///
398    /// ```
399    /// use coreplus::net::Ipv4Addr;
400    ///
401    /// assert_eq!(Ipv4Addr::new(10, 0, 0, 1).is_private(), true);
402    /// assert_eq!(Ipv4Addr::new(10, 10, 10, 10).is_private(), true);
403    /// assert_eq!(Ipv4Addr::new(172, 16, 10, 10).is_private(), true);
404    /// assert_eq!(Ipv4Addr::new(172, 29, 45, 14).is_private(), true);
405    /// assert_eq!(Ipv4Addr::new(172, 32, 0, 2).is_private(), false);
406    /// assert_eq!(Ipv4Addr::new(192, 168, 0, 2).is_private(), true);
407    /// assert_eq!(Ipv4Addr::new(192, 169, 0, 2).is_private(), false);
408    /// ```
409    #[inline]
410    pub const fn is_private(&self) -> bool {
411        match self.octets() {
412            [10, ..] => true,
413            [172, b, ..] if b >= 16 && b <= 31 => true,
414            [192, 168, ..] => true,
415            _ => false,
416        }
417    }
418
419    /// Returns [`true`] if the address is link-local (`169.254.0.0/16`).
420    ///
421    /// This property is defined by [IETF RFC 3927].
422    ///
423    /// [IETF RFC 3927]: https://tools.ietf.org/html/rfc3927
424    ///
425    /// # Examples
426    ///
427    /// ```
428    /// use coreplus::net::Ipv4Addr;
429    ///
430    /// assert_eq!(Ipv4Addr::new(169, 254, 0, 0).is_link_local(), true);
431    /// assert_eq!(Ipv4Addr::new(169, 254, 10, 65).is_link_local(), true);
432    /// assert_eq!(Ipv4Addr::new(16, 89, 10, 65).is_link_local(), false);
433    /// ```
434    #[inline]
435    pub const fn is_link_local(&self) -> bool {
436        matches!(self.octets(), [169, 254, ..])
437    }
438
439    /// Returns [`true`] if the address appears to be globally routable.
440    /// See [iana-ipv4-special-registry][ipv4-sr].
441    ///
442    /// The following return [`false`]:
443    ///
444    /// - private addresses (see [`Ipv4Addr::is_private()`])
445    /// - the loopback address (see [`Ipv4Addr::is_loopback()`])
446    /// - the link-local address (see [`Ipv4Addr::is_link_local()`])
447    /// - the broadcast address (see [`Ipv4Addr::is_broadcast()`])
448    /// - addresses used for documentation (see [`Ipv4Addr::is_documentation()`])
449    /// - the unspecified address (see [`Ipv4Addr::is_unspecified()`]), and the whole
450    ///   `0.0.0.0/8` block
451    /// - addresses reserved for future protocols (see
452    /// [`Ipv4Addr::is_ietf_protocol_assignment()`], except
453    /// `192.0.0.9/32` and `192.0.0.10/32` which are globally routable
454    /// - addresses reserved for future use (see [`Ipv4Addr::is_reserved()`]
455    /// - addresses reserved for networking devices benchmarking (see
456    /// [`Ipv4Addr::is_benchmarking()`])
457    ///
458    /// [ipv4-sr]: https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
459    ///
460    /// # Examples
461    ///
462    /// ```
463    /// #![feature(ip)]
464    ///
465    /// use coreplus::net::Ipv4Addr;
466    ///
467    /// // private addresses are not global
468    /// assert_eq!(Ipv4Addr::new(10, 254, 0, 0).is_global(), false);
469    /// assert_eq!(Ipv4Addr::new(192, 168, 10, 65).is_global(), false);
470    /// assert_eq!(Ipv4Addr::new(172, 16, 10, 65).is_global(), false);
471    ///
472    /// // the 0.0.0.0/8 block is not global
473    /// assert_eq!(Ipv4Addr::new(0, 1, 2, 3).is_global(), false);
474    /// // in particular, the unspecified address is not global
475    /// assert_eq!(Ipv4Addr::new(0, 0, 0, 0).is_global(), false);
476    ///
477    /// // the loopback address is not global
478    /// assert_eq!(Ipv4Addr::new(127, 0, 0, 1).is_global(), false);
479    ///
480    /// // link local addresses are not global
481    /// assert_eq!(Ipv4Addr::new(169, 254, 45, 1).is_global(), false);
482    ///
483    /// // the broadcast address is not global
484    /// assert_eq!(Ipv4Addr::new(255, 255, 255, 255).is_global(), false);
485    ///
486    /// // the address space designated for documentation is not global
487    /// assert_eq!(Ipv4Addr::new(192, 0, 2, 255).is_global(), false);
488    /// assert_eq!(Ipv4Addr::new(198, 51, 100, 65).is_global(), false);
489    /// assert_eq!(Ipv4Addr::new(203, 0, 113, 6).is_global(), false);
490    ///
491    /// // shared addresses are not global
492    /// assert_eq!(Ipv4Addr::new(100, 100, 0, 0).is_global(), false);
493    ///
494    /// // addresses reserved for protocol assignment are not global
495    /// assert_eq!(Ipv4Addr::new(192, 0, 0, 0).is_global(), false);
496    /// assert_eq!(Ipv4Addr::new(192, 0, 0, 255).is_global(), false);
497    ///
498    /// // addresses reserved for future use are not global
499    /// assert_eq!(Ipv4Addr::new(250, 10, 20, 30).is_global(), false);
500    ///
501    /// // addresses reserved for network devices benchmarking are not global
502    /// assert_eq!(Ipv4Addr::new(198, 18, 0, 0).is_global(), false);
503    ///
504    /// // All the other addresses are global
505    /// assert_eq!(Ipv4Addr::new(1, 1, 1, 1).is_global(), true);
506    /// assert_eq!(Ipv4Addr::new(80, 9, 12, 3).is_global(), true);
507    /// ```
508    #[inline]
509    pub const fn is_global(&self) -> bool {
510        // check if this address is 192.0.0.9 or 192.0.0.10. These addresses are the only two
511        // globally routable addresses in the 192.0.0.0/24 range.
512        if u32::from_be_bytes(self.octets()) == 0xc0000009
513            || u32::from_be_bytes(self.octets()) == 0xc000000a
514        {
515            return true;
516        }
517        !self.is_private()
518            && !self.is_loopback()
519            && !self.is_link_local()
520            && !self.is_broadcast()
521            && !self.is_documentation()
522            && !self.is_shared()
523            && !self.is_ietf_protocol_assignment()
524            && !self.is_reserved()
525            && !self.is_benchmarking()
526            // Make sure the address is not in 0.0.0.0/8
527            && self.octets()[0] != 0
528    }
529
530    /// Returns [`true`] if this address is part of the Shared Address Space defined in
531    /// [IETF RFC 6598] (`100.64.0.0/10`).
532    ///
533    /// [IETF RFC 6598]: https://tools.ietf.org/html/rfc6598
534    ///
535    /// # Examples
536    ///
537    /// ```
538    /// #![feature(ip)]
539    /// use coreplus::net::Ipv4Addr;
540    ///
541    /// assert_eq!(Ipv4Addr::new(100, 64, 0, 0).is_shared(), true);
542    /// assert_eq!(Ipv4Addr::new(100, 127, 255, 255).is_shared(), true);
543    /// assert_eq!(Ipv4Addr::new(100, 128, 0, 0).is_shared(), false);
544    /// ```
545    #[inline]
546    pub const fn is_shared(&self) -> bool {
547        self.octets()[0] == 100 && (self.octets()[1] & 0b1100_0000 == 0b0100_0000)
548    }
549
550    /// Returns [`true`] if this address is part of `192.0.0.0/24`, which is reserved to
551    /// IANA for IETF protocol assignments, as documented in [IETF RFC 6890].
552    ///
553    /// Note that parts of this block are in use:
554    ///
555    /// - `192.0.0.8/32` is the "IPv4 dummy address" (see [IETF RFC 7600])
556    /// - `192.0.0.9/32` is the "Port Control Protocol Anycast" (see [IETF RFC 7723])
557    /// - `192.0.0.10/32` is used for NAT traversal (see [IETF RFC 8155])
558    ///
559    /// [IETF RFC 6890]: https://tools.ietf.org/html/rfc6890
560    /// [IETF RFC 7600]: https://tools.ietf.org/html/rfc7600
561    /// [IETF RFC 7723]: https://tools.ietf.org/html/rfc7723
562    /// [IETF RFC 8155]: https://tools.ietf.org/html/rfc8155
563    ///
564    /// # Examples
565    ///
566    /// ```
567    /// #![feature(ip)]
568    /// use coreplus::net::Ipv4Addr;
569    ///
570    /// assert_eq!(Ipv4Addr::new(192, 0, 0, 0).is_ietf_protocol_assignment(), true);
571    /// assert_eq!(Ipv4Addr::new(192, 0, 0, 8).is_ietf_protocol_assignment(), true);
572    /// assert_eq!(Ipv4Addr::new(192, 0, 0, 9).is_ietf_protocol_assignment(), true);
573    /// assert_eq!(Ipv4Addr::new(192, 0, 0, 255).is_ietf_protocol_assignment(), true);
574    /// assert_eq!(Ipv4Addr::new(192, 0, 1, 0).is_ietf_protocol_assignment(), false);
575    /// assert_eq!(Ipv4Addr::new(191, 255, 255, 255).is_ietf_protocol_assignment(), false);
576    /// ```
577    #[inline]
578    pub const fn is_ietf_protocol_assignment(&self) -> bool {
579        self.octets()[0] == 192 && self.octets()[1] == 0 && self.octets()[2] == 0
580    }
581
582    /// Returns [`true`] if this address part of the `198.18.0.0/15` range, which is reserved for
583    /// network devices benchmarking. This range is defined in [IETF RFC 2544] as `192.18.0.0`
584    /// through `198.19.255.255` but [errata 423] corrects it to `198.18.0.0/15`.
585    ///
586    /// [IETF RFC 2544]: https://tools.ietf.org/html/rfc2544
587    /// [errata 423]: https://www.rfc-editor.org/errata/eid423
588    ///
589    /// # Examples
590    ///
591    /// ```
592    /// #![feature(ip)]
593    /// use coreplus::net::Ipv4Addr;
594    ///
595    /// assert_eq!(Ipv4Addr::new(198, 17, 255, 255).is_benchmarking(), false);
596    /// assert_eq!(Ipv4Addr::new(198, 18, 0, 0).is_benchmarking(), true);
597    /// assert_eq!(Ipv4Addr::new(198, 19, 255, 255).is_benchmarking(), true);
598    /// assert_eq!(Ipv4Addr::new(198, 20, 0, 0).is_benchmarking(), false);
599    /// ```
600    #[inline]
601    pub const fn is_benchmarking(&self) -> bool {
602        self.octets()[0] == 198 && (self.octets()[1] & 0xfe) == 18
603    }
604
605    /// Returns [`true`] if this address is reserved by IANA for future use. [IETF RFC 1112]
606    /// defines the block of reserved addresses as `240.0.0.0/4`. This range normally includes the
607    /// broadcast address `255.255.255.255`, but this implementation explicitly excludes it, since
608    /// it is obviously not reserved for future use.
609    ///
610    /// [IETF RFC 1112]: https://tools.ietf.org/html/rfc1112
611    ///
612    /// # Warning
613    ///
614    /// As IANA assigns new addresses, this method will be
615    /// updated. This may result in non-reserved addresses being
616    /// treated as reserved in code that relies on an outdated version
617    /// of this method.
618    ///
619    /// # Examples
620    ///
621    /// ```
622    /// #![feature(ip)]
623    /// use coreplus::net::Ipv4Addr;
624    ///
625    /// assert_eq!(Ipv4Addr::new(240, 0, 0, 0).is_reserved(), true);
626    /// assert_eq!(Ipv4Addr::new(255, 255, 255, 254).is_reserved(), true);
627    ///
628    /// assert_eq!(Ipv4Addr::new(239, 255, 255, 255).is_reserved(), false);
629    /// // The broadcast address is not considered as reserved for future use by this implementation
630    /// assert_eq!(Ipv4Addr::new(255, 255, 255, 255).is_reserved(), false);
631    /// ```
632    #[inline]
633    pub const fn is_reserved(&self) -> bool {
634        self.octets()[0] & 240 == 240 && !self.is_broadcast()
635    }
636
637    /// Returns [`true`] if this is a multicast address (`224.0.0.0/4`).
638    ///
639    /// Multicast addresses have a most significant octet between `224` and `239`,
640    /// and is defined by [IETF RFC 5771].
641    ///
642    /// [IETF RFC 5771]: https://tools.ietf.org/html/rfc5771
643    ///
644    /// # Examples
645    ///
646    /// ```
647    /// use coreplus::net::Ipv4Addr;
648    ///
649    /// assert_eq!(Ipv4Addr::new(224, 254, 0, 0).is_multicast(), true);
650    /// assert_eq!(Ipv4Addr::new(236, 168, 10, 65).is_multicast(), true);
651    /// assert_eq!(Ipv4Addr::new(172, 16, 10, 65).is_multicast(), false);
652    /// ```
653    #[inline]
654    pub const fn is_multicast(&self) -> bool {
655        self.octets()[0] >= 224 && self.octets()[0] <= 239
656    }
657
658    /// Returns [`true`] if this is a broadcast address (`255.255.255.255`).
659    ///
660    /// A broadcast address has all octets set to `255` as defined in [IETF RFC 919].
661    ///
662    /// [IETF RFC 919]: https://tools.ietf.org/html/rfc919
663    ///
664    /// # Examples
665    ///
666    /// ```
667    /// use coreplus::net::Ipv4Addr;
668    ///
669    /// assert_eq!(Ipv4Addr::new(255, 255, 255, 255).is_broadcast(), true);
670    /// assert_eq!(Ipv4Addr::new(236, 168, 10, 65).is_broadcast(), false);
671    /// ```
672    #[inline]
673    pub const fn is_broadcast(&self) -> bool {
674        u32::from_be_bytes(self.octets()) == u32::from_be_bytes(Self::BROADCAST.octets())
675    }
676
677    /// Returns [`true`] if this address is in a range designated for documentation.
678    ///
679    /// This is defined in [IETF RFC 5737]:
680    ///
681    /// - `192.0.2.0/24` (TEST-NET-1)
682    /// - `198.51.100.0/24` (TEST-NET-2)
683    /// - `203.0.113.0/24` (TEST-NET-3)
684    ///
685    /// [IETF RFC 5737]: https://tools.ietf.org/html/rfc5737
686    ///
687    /// # Examples
688    ///
689    /// ```
690    /// use coreplus::net::Ipv4Addr;
691    ///
692    /// assert_eq!(Ipv4Addr::new(192, 0, 2, 255).is_documentation(), true);
693    /// assert_eq!(Ipv4Addr::new(198, 51, 100, 65).is_documentation(), true);
694    /// assert_eq!(Ipv4Addr::new(203, 0, 113, 6).is_documentation(), true);
695    /// assert_eq!(Ipv4Addr::new(193, 34, 17, 19).is_documentation(), false);
696    /// ```
697    #[inline]
698    pub const fn is_documentation(&self) -> bool {
699        match self.octets() {
700            [192, 0, 2, _] => true,
701            [198, 51, 100, _] => true,
702            [203, 0, 113, _] => true,
703            _ => false,
704        }
705    }
706
707    /// Converts this address to an IPv4-compatible [`IPv6` address].
708    ///
709    /// `a.b.c.d` becomes `::a.b.c.d`
710    ///
711    /// This isn't typically the method you want; these addresses don't typically
712    /// function on modern systems. Use `to_ipv6_mapped` instead.
713    ///
714    /// [`IPv6` address]: Ipv6Addr
715    ///
716    /// # Examples
717    ///
718    /// ```
719    /// use coreplus::net::{Ipv4Addr, Ipv6Addr};
720    ///
721    /// assert_eq!(
722    ///     Ipv4Addr::new(192, 0, 2, 255).to_ipv6_compatible(),
723    ///     Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0xc000, 0x2ff)
724    /// );
725    /// ```
726    #[inline]
727    pub const fn to_ipv6_compatible(&self) -> Ipv6Addr {
728        let [a, b, c, d] = self.octets();
729        Ipv6Addr {
730            inner: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, a, b, c, d],
731        }
732    }
733
734    /// Converts this address to an IPv4-mapped [`IPv6` address].
735    ///
736    /// `a.b.c.d` becomes `::ffff:a.b.c.d`
737    ///
738    /// [`IPv6` address]: Ipv6Addr
739    ///
740    /// # Examples
741    ///
742    /// ```
743    /// use coreplus::net::{Ipv4Addr, Ipv6Addr};
744    ///
745    /// assert_eq!(Ipv4Addr::new(192, 0, 2, 255).to_ipv6_mapped(),
746    ///            Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc000, 0x2ff));
747    /// ```
748    #[inline]
749    pub const fn to_ipv6_mapped(&self) -> Ipv6Addr {
750        let [a, b, c, d] = self.octets();
751        Ipv6Addr {
752            inner: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, a, b, c, d],
753        }
754    }
755}
756
757impl fmt::Display for IpAddr {
758    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
759        match self {
760            IpAddr::V4(ip) => ip.fmt(fmt),
761            IpAddr::V6(ip) => ip.fmt(fmt),
762        }
763    }
764}
765
766impl fmt::Debug for IpAddr {
767    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
768        fmt::Display::fmt(self, fmt)
769    }
770}
771
772impl From<Ipv4Addr> for IpAddr {
773    /// Copies this address to a new `IpAddr::V4`.
774    ///
775    /// # Examples
776    ///
777    /// ```
778    /// use coreplus::net::{IpAddr, Ipv4Addr};
779    ///
780    /// let addr = Ipv4Addr::new(127, 0, 0, 1);
781    ///
782    /// assert_eq!(
783    ///     IpAddr::V4(addr),
784    ///     IpAddr::from(addr)
785    /// )
786    /// ```
787    #[inline]
788    fn from(ipv4: Ipv4Addr) -> IpAddr {
789        IpAddr::V4(ipv4)
790    }
791}
792
793impl From<Ipv6Addr> for IpAddr {
794    /// Copies this address to a new `IpAddr::V6`.
795    ///
796    /// # Examples
797    ///
798    /// ```
799    /// use coreplus::net::{IpAddr, Ipv6Addr};
800    ///
801    /// let addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff);
802    ///
803    /// assert_eq!(
804    ///     IpAddr::V6(addr),
805    ///     IpAddr::from(addr)
806    /// );
807    /// ```
808    #[inline]
809    fn from(ipv6: Ipv6Addr) -> IpAddr {
810        IpAddr::V6(ipv6)
811    }
812}
813
814impl fmt::Display for Ipv4Addr {
815    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
816        let octets = self.octets();
817        // Fast Path: if there's no alignment stuff, write directly to the buffer
818        if fmt.precision().is_none() && fmt.width().is_none() {
819            write!(
820                fmt,
821                "{}.{}.{}.{}",
822                octets[0], octets[1], octets[2], octets[3]
823            )
824        } else {
825            const IPV4_BUF_LEN: usize = 15; // Long enough for the longest possible IPv4 address
826            let mut buf = [0u8; IPV4_BUF_LEN];
827            let mut buf_slice = &mut buf[..];
828
829            // Note: The call to write should never fail, hence the unwrap
830            write!(
831                buf_slice,
832                "{}.{}.{}.{}",
833                octets[0], octets[1], octets[2], octets[3]
834            )
835            .unwrap();
836            let len = IPV4_BUF_LEN - buf_slice.len();
837
838            // This unsafe is OK because we know what is being written to the buffer
839            let buf = unsafe { str::from_utf8_unchecked(&buf[..len]) };
840            fmt.pad(buf)
841        }
842    }
843}
844
845impl fmt::Debug for Ipv4Addr {
846    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
847        fmt::Display::fmt(self, fmt)
848    }
849}
850
851impl PartialEq<Ipv4Addr> for IpAddr {
852    #[inline]
853    fn eq(&self, other: &Ipv4Addr) -> bool {
854        match self {
855            IpAddr::V4(v4) => v4 == other,
856            IpAddr::V6(_) => false,
857        }
858    }
859}
860
861impl PartialEq<IpAddr> for Ipv4Addr {
862    #[inline]
863    fn eq(&self, other: &IpAddr) -> bool {
864        match other {
865            IpAddr::V4(v4) => self == v4,
866            IpAddr::V6(_) => false,
867        }
868    }
869}
870
871impl PartialOrd<Ipv4Addr> for IpAddr {
872    #[inline]
873    fn partial_cmp(&self, other: &Ipv4Addr) -> Option<Ordering> {
874        match self {
875            IpAddr::V4(v4) => v4.partial_cmp(other),
876            IpAddr::V6(_) => Some(Ordering::Greater),
877        }
878    }
879}
880
881impl PartialOrd<IpAddr> for Ipv4Addr {
882    #[inline]
883    fn partial_cmp(&self, other: &IpAddr) -> Option<Ordering> {
884        match other {
885            IpAddr::V4(v4) => self.partial_cmp(v4),
886            IpAddr::V6(_) => Some(Ordering::Less),
887        }
888    }
889}
890
891impl Ord for Ipv4Addr {
892    #[inline]
893    fn cmp(&self, other: &Ipv4Addr) -> Ordering {
894        // Compare as native endian
895        u32::from_be(self.inner).cmp(&u32::from_be(other.inner))
896    }
897}
898
899impl From<Ipv4Addr> for u32 {
900    /// Converts an `Ipv4Addr` into a host byte order `u32`.
901    ///
902    /// # Examples
903    ///
904    /// ```
905    /// use coreplus::net::Ipv4Addr;
906    ///
907    /// let addr = Ipv4Addr::new(0xca, 0xfe, 0xba, 0xbe);
908    /// assert_eq!(0xcafebabe, u32::from(addr));
909    /// ```
910    #[inline]
911    fn from(ip: Ipv4Addr) -> u32 {
912        let ip = ip.octets();
913        u32::from_be_bytes(ip)
914    }
915}
916
917impl From<u32> for Ipv4Addr {
918    /// Converts a host byte order `u32` into an `Ipv4Addr`.
919    ///
920    /// # Examples
921    ///
922    /// ```
923    /// use coreplus::net::Ipv4Addr;
924    ///
925    /// let addr = Ipv4Addr::from(0xcafebabe);
926    /// assert_eq!(Ipv4Addr::new(0xca, 0xfe, 0xba, 0xbe), addr);
927    /// ```
928    #[inline]
929    fn from(ip: u32) -> Ipv4Addr {
930        Ipv4Addr::from(ip.to_be_bytes())
931    }
932}
933
934impl From<[u8; 4]> for Ipv4Addr {
935    /// Creates an `Ipv4Addr` from a four element byte array.
936    ///
937    /// # Examples
938    ///
939    /// ```
940    /// use coreplus::net::Ipv4Addr;
941    ///
942    /// let addr = Ipv4Addr::from([13u8, 12u8, 11u8, 10u8]);
943    /// assert_eq!(Ipv4Addr::new(13, 12, 11, 10), addr);
944    /// ```
945    #[inline]
946    fn from(octets: [u8; 4]) -> Ipv4Addr {
947        Ipv4Addr::new(octets[0], octets[1], octets[2], octets[3])
948    }
949}
950
951impl From<[u8; 4]> for IpAddr {
952    /// Creates an `IpAddr::V4` from a four element byte array.
953    ///
954    /// # Examples
955    ///
956    /// ```
957    /// use coreplus::net::{IpAddr, Ipv4Addr};
958    ///
959    /// let addr = IpAddr::from([13u8, 12u8, 11u8, 10u8]);
960    /// assert_eq!(IpAddr::V4(Ipv4Addr::new(13, 12, 11, 10)), addr);
961    /// ```
962    #[inline]
963    fn from(octets: [u8; 4]) -> IpAddr {
964        IpAddr::V4(Ipv4Addr::from(octets))
965    }
966}
967
968impl Ipv6Addr {
969    /// Creates a new IPv6 address from eight 16-bit segments.
970    ///
971    /// The result will represent the IP address `a:b:c:d:e:f:g:h`.
972    ///
973    /// # Examples
974    ///
975    /// ```
976    /// use coreplus::net::Ipv6Addr;
977    ///
978    /// let addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff);
979    /// ```
980    #[inline]
981    pub const fn new(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> Ipv6Addr {
982        let addr16 = [
983            a.to_be().to_ne_bytes(),
984            b.to_be().to_ne_bytes(),
985            c.to_be().to_ne_bytes(),
986            d.to_be().to_ne_bytes(),
987            e.to_be().to_ne_bytes(),
988            f.to_be().to_ne_bytes(),
989            g.to_be().to_ne_bytes(),
990            h.to_be().to_ne_bytes(),
991        ];
992        Ipv6Addr {
993            inner: [
994                addr16[0][0],
995                addr16[0][1],
996                addr16[1][0],
997                addr16[1][1],
998                addr16[2][0],
999                addr16[2][1],
1000                addr16[3][0],
1001                addr16[3][1],
1002                addr16[4][0],
1003                addr16[4][1],
1004                addr16[5][0],
1005                addr16[5][1],
1006                addr16[6][0],
1007                addr16[6][1],
1008                addr16[7][0],
1009                addr16[7][1],
1010            ],
1011        }
1012    }
1013
1014    /// An IPv6 address representing localhost: `::1`.
1015    ///
1016    /// # Examples
1017    ///
1018    /// ```
1019    /// use coreplus::net::Ipv6Addr;
1020    ///
1021    /// let addr = Ipv6Addr::LOCALHOST;
1022    /// assert_eq!(addr, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
1023    /// ```
1024    pub const LOCALHOST: Self = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
1025
1026    /// An IPv6 address representing the unspecified address: `::`
1027    ///
1028    /// # Examples
1029    ///
1030    /// ```
1031    /// use coreplus::net::Ipv6Addr;
1032    ///
1033    /// let addr = Ipv6Addr::UNSPECIFIED;
1034    /// assert_eq!(addr, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0));
1035    /// ```
1036    pub const UNSPECIFIED: Self = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
1037
1038    /// Returns the eight 16-bit segments that make up this address.
1039    ///
1040    /// # Examples
1041    ///
1042    /// ```
1043    /// use coreplus::net::Ipv6Addr;
1044    ///
1045    /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).segments(),
1046    ///            [0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff]);
1047    /// ```
1048    #[inline]
1049    pub const fn segments(&self) -> [u16; 8] {
1050        let z = self.inner;
1051        let [a, b, c, d, e, f, g, h] = [
1052            [z[0], z[1]],
1053            [z[2], z[3]],
1054            [z[4], z[5]],
1055            [z[6], z[7]],
1056            [z[8], z[9]],
1057            [z[10], z[11]],
1058            [z[12], z[13]],
1059            [z[14], z[15]],
1060        ];
1061        // We want native endian u16
1062        [
1063            u16::from_be_bytes(a),
1064            u16::from_be_bytes(b),
1065            u16::from_be_bytes(c),
1066            u16::from_be_bytes(d),
1067            u16::from_be_bytes(e),
1068            u16::from_be_bytes(f),
1069            u16::from_be_bytes(g),
1070            u16::from_be_bytes(h),
1071        ]
1072    }
1073
1074    /// Returns [`true`] for the special 'unspecified' address (`::`).
1075    ///
1076    /// This property is defined in [IETF RFC 4291].
1077    ///
1078    /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291
1079    ///
1080    /// # Examples
1081    ///
1082    /// ```
1083    /// use coreplus::net::Ipv6Addr;
1084    ///
1085    /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_unspecified(), false);
1086    /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0).is_unspecified(), true);
1087    /// ```
1088    #[inline]
1089    pub const fn is_unspecified(&self) -> bool {
1090        u128::from_be_bytes(self.octets()) == u128::from_be_bytes(Ipv6Addr::UNSPECIFIED.octets())
1091    }
1092
1093    /// Returns [`true`] if this is a loopback address (::1).
1094    ///
1095    /// This property is defined in [IETF RFC 4291].
1096    ///
1097    /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291
1098    ///
1099    /// # Examples
1100    ///
1101    /// ```
1102    /// use coreplus::net::Ipv6Addr;
1103    ///
1104    /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_loopback(), false);
1105    /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0x1).is_loopback(), true);
1106    /// ```
1107    #[inline]
1108    pub const fn is_loopback(&self) -> bool {
1109        u128::from_be_bytes(self.octets()) == u128::from_be_bytes(Ipv6Addr::LOCALHOST.octets())
1110    }
1111
1112    /// Returns [`true`] if the address appears to be globally routable.
1113    ///
1114    /// The following return [`false`]:
1115    ///
1116    /// - the loopback address
1117    /// - link-local and unique local unicast addresses
1118    /// - interface-, link-, realm-, admin- and site-local multicast addresses
1119    ///
1120    /// # Examples
1121    ///
1122    /// ```
1123    /// #![feature(ip)]
1124    ///
1125    /// use coreplus::net::Ipv6Addr;
1126    ///
1127    /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_global(), true);
1128    /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0x1).is_global(), false);
1129    /// assert_eq!(Ipv6Addr::new(0, 0, 0x1c9, 0, 0, 0xafc8, 0, 0x1).is_global(), true);
1130    /// ```
1131    #[inline]
1132    pub const fn is_global(&self) -> bool {
1133        match self.multicast_scope() {
1134            Some(Ipv6MulticastScope::Global) => true,
1135            None => self.is_unicast_global(),
1136            _ => false,
1137        }
1138    }
1139
1140    /// Returns [`true`] if this is a unique local address (`fc00::/7`).
1141    ///
1142    /// This property is defined in [IETF RFC 4193].
1143    ///
1144    /// [IETF RFC 4193]: https://tools.ietf.org/html/rfc4193
1145    ///
1146    /// # Examples
1147    ///
1148    /// ```
1149    /// #![feature(ip)]
1150    ///
1151    /// use coreplus::net::Ipv6Addr;
1152    ///
1153    /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_unique_local(), false);
1154    /// assert_eq!(Ipv6Addr::new(0xfc02, 0, 0, 0, 0, 0, 0, 0).is_unique_local(), true);
1155    /// ```
1156    #[inline]
1157    pub const fn is_unique_local(&self) -> bool {
1158        (self.segments()[0] & 0xfe00) == 0xfc00
1159    }
1160
1161    /// Returns [`true`] if this is a unicast address, as defined by [IETF RFC 4291].
1162    /// Any address that is not a [multicast address] (`ff00::/8`) is unicast.
1163    ///
1164    /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291
1165    /// [multicast address]: Ipv6Addr::is_multicast
1166    ///
1167    /// # Examples
1168    ///
1169    /// ```
1170    /// #![feature(ip)]
1171    ///
1172    /// use coreplus::net::Ipv6Addr;
1173    ///
1174    /// // The unspecified and loopback addresses are unicast.
1175    /// assert_eq!(Ipv6Addr::UNSPECIFIED.is_unicast(), true);
1176    /// assert_eq!(Ipv6Addr::LOCALHOST.is_unicast(), true);
1177    ///
1178    /// // Any address that is not a multicast address (`ff00::/8`) is unicast.
1179    /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_unicast(), true);
1180    /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).is_unicast(), false);
1181    /// ```
1182    #[inline]
1183    pub const fn is_unicast(&self) -> bool {
1184        !self.is_multicast()
1185    }
1186
1187    /// Returns `true` if the address is a unicast address with link-local scope,
1188    /// as defined in [RFC 4291].
1189    ///
1190    /// A unicast address has link-local scope if it has the prefix `fe80::/10`, as per [RFC 4291 section 2.4].
1191    /// Note that this encompasses more addresses than those defined in [RFC 4291 section 2.5.6],
1192    /// which describes "Link-Local IPv6 Unicast Addresses" as having the following stricter format:
1193    ///
1194    /// ```text
1195    /// | 10 bits  |         54 bits         |          64 bits           |
1196    /// +----------+-------------------------+----------------------------+
1197    /// |1111111010|           0             |       interface ID         |
1198    /// +----------+-------------------------+----------------------------+
1199    /// ```
1200    /// So while currently the only addresses with link-local scope an application will encounter are all in `fe80::/64`,
1201    /// this might change in the future with the publication of new standards. More addresses in `fe80::/10` could be allocated,
1202    /// and those addresses will have link-local scope.
1203    ///
1204    /// Also note that while [RFC 4291 section 2.5.3] mentions about the [loopback address] (`::1`) that "it is treated as having Link-Local scope",
1205    /// this does not mean that the loopback address actually has link-local scope and this method will return `false` on it.
1206    ///
1207    /// [RFC 4291]: https://tools.ietf.org/html/rfc4291
1208    /// [RFC 4291 section 2.4]: https://tools.ietf.org/html/rfc4291#section-2.4
1209    /// [RFC 4291 section 2.5.3]: https://tools.ietf.org/html/rfc4291#section-2.5.3
1210    /// [RFC 4291 section 2.5.6]: https://tools.ietf.org/html/rfc4291#section-2.5.6
1211    /// [loopback address]: Ipv6Addr::LOCALHOST
1212    ///
1213    /// # Examples
1214    ///
1215    /// ```
1216    /// #![feature(ip)]
1217    ///
1218    /// use coreplus::net::Ipv6Addr;
1219    ///
1220    /// // The loopback address (`::1`) does not actually have link-local scope.
1221    /// assert_eq!(Ipv6Addr::LOCALHOST.is_unicast_link_local(), false);
1222    ///
1223    /// // Only addresses in `fe80::/10` have link-local scope.
1224    /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_unicast_link_local(), false);
1225    /// assert_eq!(Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0).is_unicast_link_local(), true);
1226    ///
1227    /// // Addresses outside the stricter `fe80::/64` also have link-local scope.
1228    /// assert_eq!(Ipv6Addr::new(0xfe80, 0, 0, 1, 0, 0, 0, 0).is_unicast_link_local(), true);
1229    /// assert_eq!(Ipv6Addr::new(0xfe81, 0, 0, 0, 0, 0, 0, 0).is_unicast_link_local(), true);
1230    /// ```
1231    #[inline]
1232    pub const fn is_unicast_link_local(&self) -> bool {
1233        (self.segments()[0] & 0xffc0) == 0xfe80
1234    }
1235
1236    /// Returns [`true`] if this is an address reserved for documentation
1237    /// (`2001:db8::/32`).
1238    ///
1239    /// This property is defined in [IETF RFC 3849].
1240    ///
1241    /// [IETF RFC 3849]: https://tools.ietf.org/html/rfc3849
1242    ///
1243    /// # Examples
1244    ///
1245    /// ```
1246    /// #![feature(ip)]
1247    ///
1248    /// use coreplus::net::Ipv6Addr;
1249    ///
1250    /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_documentation(), false);
1251    /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_documentation(), true);
1252    /// ```
1253    #[inline]
1254    pub const fn is_documentation(&self) -> bool {
1255        (self.segments()[0] == 0x2001) && (self.segments()[1] == 0xdb8)
1256    }
1257
1258    /// Returns [`true`] if the address is a globally routable unicast address.
1259    ///
1260    /// The following return false:
1261    ///
1262    /// - the loopback address
1263    /// - the link-local addresses
1264    /// - unique local addresses
1265    /// - the unspecified address
1266    /// - the address range reserved for documentation
1267    ///
1268    /// This method returns [`true`] for site-local addresses as per [RFC 4291 section 2.5.7]
1269    ///
1270    /// ```no_rust
1271    /// The special behavior of [the site-local unicast] prefix defined in [RFC3513] must no longer
1272    /// be supported in new implementations (i.e., new implementations must treat this prefix as
1273    /// Global Unicast).
1274    /// ```
1275    ///
1276    /// [RFC 4291 section 2.5.7]: https://tools.ietf.org/html/rfc4291#section-2.5.7
1277    ///
1278    /// # Examples
1279    ///
1280    /// ```
1281    /// #![feature(ip)]
1282    ///
1283    /// use coreplus::net::Ipv6Addr;
1284    ///
1285    /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_unicast_global(), false);
1286    /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_unicast_global(), true);
1287    /// ```
1288    #[inline]
1289    pub const fn is_unicast_global(&self) -> bool {
1290        self.is_unicast()
1291            && !self.is_loopback()
1292            && !self.is_unicast_link_local()
1293            && !self.is_unique_local()
1294            && !self.is_unspecified()
1295            && !self.is_documentation()
1296    }
1297
1298    /// Returns the address's multicast scope if the address is multicast.
1299    ///
1300    /// # Examples
1301    ///
1302    /// ```
1303    /// #![feature(ip)]
1304    ///
1305    /// use coreplus::net::{Ipv6Addr, Ipv6MulticastScope};
1306    ///
1307    /// assert_eq!(
1308    ///     Ipv6Addr::new(0xff0e, 0, 0, 0, 0, 0, 0, 0).multicast_scope(),
1309    ///     Some(Ipv6MulticastScope::Global)
1310    /// );
1311    /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).multicast_scope(), None);
1312    /// ```
1313    #[inline]
1314    pub const fn multicast_scope(&self) -> Option<Ipv6MulticastScope> {
1315        if self.is_multicast() {
1316            match self.segments()[0] & 0x000f {
1317                1 => Some(Ipv6MulticastScope::InterfaceLocal),
1318                2 => Some(Ipv6MulticastScope::LinkLocal),
1319                3 => Some(Ipv6MulticastScope::RealmLocal),
1320                4 => Some(Ipv6MulticastScope::AdminLocal),
1321                5 => Some(Ipv6MulticastScope::SiteLocal),
1322                8 => Some(Ipv6MulticastScope::OrganizationLocal),
1323                14 => Some(Ipv6MulticastScope::Global),
1324                _ => None,
1325            }
1326        } else {
1327            None
1328        }
1329    }
1330
1331    /// Returns [`true`] if this is a multicast address (`ff00::/8`).
1332    ///
1333    /// This property is defined by [IETF RFC 4291].
1334    ///
1335    /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291
1336    ///
1337    /// # Examples
1338    ///
1339    /// ```
1340    /// use coreplus::net::Ipv6Addr;
1341    ///
1342    /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).is_multicast(), true);
1343    /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_multicast(), false);
1344    /// ```
1345    #[inline]
1346    pub const fn is_multicast(&self) -> bool {
1347        (self.segments()[0] & 0xff00) == 0xff00
1348    }
1349
1350    /// Converts this address to an [`IPv4` address] if it's an "IPv4-mapped IPv6 address"
1351    /// defined in [IETF RFC 4291 section 2.5.5.2], otherwise returns [`None`].
1352    ///
1353    /// `::ffff:a.b.c.d` becomes `a.b.c.d`.
1354    /// All addresses *not* starting with `::ffff` will return `None`.
1355    ///
1356    /// [`IPv4` address]: Ipv4Addr
1357    /// [IETF RFC 4291 section 2.5.5.2]: https://tools.ietf.org/html/rfc4291#section-2.5.5.2
1358    ///
1359    /// # Examples
1360    ///
1361    /// ```
1362    /// #![feature(ip)]
1363    ///
1364    /// use coreplus::net::{Ipv4Addr, Ipv6Addr};
1365    ///
1366    /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).to_ipv4_mapped(), None);
1367    /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).to_ipv4_mapped(),
1368    ///            Some(Ipv4Addr::new(192, 10, 2, 255)));
1369    /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).to_ipv4_mapped(), None);
1370    /// ```
1371    #[inline]
1372    pub const fn to_ipv4_mapped(&self) -> Option<Ipv4Addr> {
1373        match self.octets() {
1374            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, a, b, c, d] => {
1375                Some(Ipv4Addr::new(a, b, c, d))
1376            }
1377            _ => None,
1378        }
1379    }
1380
1381    /// Converts this address to an [`IPv4` address]. Returns [`None`] if this address is
1382    /// neither IPv4-compatible or IPv4-mapped.
1383    ///
1384    /// `::a.b.c.d` and `::ffff:a.b.c.d` become `a.b.c.d`
1385    ///
1386    /// [`IPv4` address]: Ipv4Addr
1387    ///
1388    /// # Examples
1389    ///
1390    /// ```
1391    /// use coreplus::net::{Ipv4Addr, Ipv6Addr};
1392    ///
1393    /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).to_ipv4(), None);
1394    /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).to_ipv4(),
1395    ///            Some(Ipv4Addr::new(192, 10, 2, 255)));
1396    /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).to_ipv4(),
1397    ///            Some(Ipv4Addr::new(0, 0, 0, 1)));
1398    /// ```
1399    #[inline]
1400    pub const fn to_ipv4(&self) -> Option<Ipv4Addr> {
1401        if let [0, 0, 0, 0, 0, 0 | 0xffff, ab, cd] = self.segments() {
1402            let [a, b] = ab.to_be_bytes();
1403            let [c, d] = cd.to_be_bytes();
1404            Some(Ipv4Addr::new(a, b, c, d))
1405        } else {
1406            None
1407        }
1408    }
1409
1410    /// Returns the sixteen eight-bit integers the IPv6 address consists of.
1411    ///
1412    /// ```
1413    /// use coreplus::net::Ipv6Addr;
1414    ///
1415    /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).octets(),
1416    ///            [255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
1417    /// ```
1418    #[inline]
1419    pub const fn octets(&self) -> [u8; 16] {
1420        self.inner
1421    }
1422}
1423
1424/// Write an Ipv6Addr, conforming to the canonical style described by
1425/// [RFC 5952](https://tools.ietf.org/html/rfc5952).
1426impl fmt::Display for Ipv6Addr {
1427    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1428        // If there are no alignment requirements, write out the IP address to
1429        // f. Otherwise, write it to a local buffer, then use f.pad.
1430        if f.precision().is_none() && f.width().is_none() {
1431            let segments = self.segments();
1432
1433            // Special case for :: and ::1; otherwise they get written with the
1434            // IPv4 formatter
1435            if self.is_unspecified() {
1436                f.write_str("::")
1437            } else if self.is_loopback() {
1438                f.write_str("::1")
1439            } else if let Some(ipv4) = self.to_ipv4() {
1440                match segments[5] {
1441                    // IPv4 Compatible address
1442                    0 => write!(f, "::{}", ipv4),
1443                    // IPv4 Mapped address
1444                    0xffff => write!(f, "::ffff:{}", ipv4),
1445                    _ => unreachable!(),
1446                }
1447            } else {
1448                #[derive(Copy, Clone, Default)]
1449                struct Span {
1450                    start: usize,
1451                    len: usize,
1452                }
1453
1454                // Find the inner 0 span
1455                let zeroes = {
1456                    let mut longest = Span::default();
1457                    let mut current = Span::default();
1458
1459                    for (i, &segment) in segments.iter().enumerate() {
1460                        if segment == 0 {
1461                            if current.len == 0 {
1462                                current.start = i;
1463                            }
1464
1465                            current.len += 1;
1466
1467                            if current.len > longest.len {
1468                                longest = current;
1469                            }
1470                        } else {
1471                            current = Span::default();
1472                        }
1473                    }
1474
1475                    longest
1476                };
1477
1478                /// Write a colon-separated part of the address
1479                #[inline]
1480                fn fmt_subslice(f: &mut fmt::Formatter<'_>, chunk: &[u16]) -> fmt::Result {
1481                    if let Some((first, tail)) = chunk.split_first() {
1482                        write!(f, "{:x}", first)?;
1483                        for segment in tail {
1484                            f.write_char(':')?;
1485                            write!(f, "{:x}", segment)?;
1486                        }
1487                    }
1488                    Ok(())
1489                }
1490
1491                if zeroes.len > 1 {
1492                    fmt_subslice(f, &segments[..zeroes.start])?;
1493                    f.write_str("::")?;
1494                    fmt_subslice(f, &segments[zeroes.start + zeroes.len..])
1495                } else {
1496                    fmt_subslice(f, &segments)
1497                }
1498            }
1499        } else {
1500            // Slow path: write the address to a local buffer, the use f.pad.
1501            // Defined recursively by using the fast path to write to the
1502            // buffer.
1503
1504            // This is the largest possible size of an IPv6 address
1505            const IPV6_BUF_LEN: usize = (4 * 8) + 7;
1506            let mut buf = [0u8; IPV6_BUF_LEN];
1507            let mut buf_slice = &mut buf[..];
1508
1509            // Note: This call to write should never fail, so unwrap is okay.
1510            write!(buf_slice, "{}", self).unwrap();
1511            let len = IPV6_BUF_LEN - buf_slice.len();
1512
1513            // This is safe because we know exactly what can be in this buffer
1514            let buf = unsafe { str::from_utf8_unchecked(&buf[..len]) };
1515            f.pad(buf)
1516        }
1517    }
1518}
1519
1520impl fmt::Debug for Ipv6Addr {
1521    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
1522        fmt::Display::fmt(self, fmt)
1523    }
1524}
1525
1526impl PartialEq<IpAddr> for Ipv6Addr {
1527    #[inline]
1528    fn eq(&self, other: &IpAddr) -> bool {
1529        match other {
1530            IpAddr::V4(_) => false,
1531            IpAddr::V6(v6) => self == v6,
1532        }
1533    }
1534}
1535
1536impl PartialEq<Ipv6Addr> for IpAddr {
1537    #[inline]
1538    fn eq(&self, other: &Ipv6Addr) -> bool {
1539        match self {
1540            IpAddr::V4(_) => false,
1541            IpAddr::V6(v6) => v6 == other,
1542        }
1543    }
1544}
1545
1546impl PartialOrd<Ipv6Addr> for IpAddr {
1547    #[inline]
1548    fn partial_cmp(&self, other: &Ipv6Addr) -> Option<Ordering> {
1549        match self {
1550            IpAddr::V4(_) => Some(Ordering::Less),
1551            IpAddr::V6(v6) => v6.partial_cmp(other),
1552        }
1553    }
1554}
1555
1556impl PartialOrd<IpAddr> for Ipv6Addr {
1557    #[inline]
1558    fn partial_cmp(&self, other: &IpAddr) -> Option<Ordering> {
1559        match other {
1560            IpAddr::V4(_) => Some(Ordering::Greater),
1561            IpAddr::V6(v6) => self.partial_cmp(v6),
1562        }
1563    }
1564}
1565
1566impl Ord for Ipv6Addr {
1567    #[inline]
1568    fn cmp(&self, other: &Ipv6Addr) -> Ordering {
1569        self.segments().cmp(&other.segments())
1570    }
1571}
1572
1573impl From<Ipv6Addr> for u128 {
1574    /// Convert an `Ipv6Addr` into a host byte order `u128`.
1575    ///
1576    /// # Examples
1577    ///
1578    /// ```
1579    /// use coreplus::net::Ipv6Addr;
1580    ///
1581    /// let addr = Ipv6Addr::new(
1582    ///     0x1020, 0x3040, 0x5060, 0x7080,
1583    ///     0x90A0, 0xB0C0, 0xD0E0, 0xF00D,
1584    /// );
1585    /// assert_eq!(0x102030405060708090A0B0C0D0E0F00D_u128, u128::from(addr));
1586    /// ```
1587    #[inline]
1588    fn from(ip: Ipv6Addr) -> u128 {
1589        let ip = ip.octets();
1590        u128::from_be_bytes(ip)
1591    }
1592}
1593impl From<u128> for Ipv6Addr {
1594    /// Convert a host byte order `u128` into an `Ipv6Addr`.
1595    ///
1596    /// # Examples
1597    ///
1598    /// ```
1599    /// use coreplus::net::Ipv6Addr;
1600    ///
1601    /// let addr = Ipv6Addr::from(0x102030405060708090A0B0C0D0E0F00D_u128);
1602    /// assert_eq!(
1603    ///     Ipv6Addr::new(
1604    ///         0x1020, 0x3040, 0x5060, 0x7080,
1605    ///         0x90A0, 0xB0C0, 0xD0E0, 0xF00D,
1606    ///     ),
1607    ///     addr);
1608    /// ```
1609    #[inline]
1610    fn from(ip: u128) -> Ipv6Addr {
1611        Ipv6Addr::from(ip.to_be_bytes())
1612    }
1613}
1614
1615impl From<[u8; 16]> for Ipv6Addr {
1616    /// Creates an `Ipv6Addr` from a sixteen element byte array.
1617    ///
1618    /// # Examples
1619    ///
1620    /// ```
1621    /// use coreplus::net::Ipv6Addr;
1622    ///
1623    /// let addr = Ipv6Addr::from([
1624    ///     25u8, 24u8, 23u8, 22u8, 21u8, 20u8, 19u8, 18u8,
1625    ///     17u8, 16u8, 15u8, 14u8, 13u8, 12u8, 11u8, 10u8,
1626    /// ]);
1627    /// assert_eq!(
1628    ///     Ipv6Addr::new(
1629    ///         0x1918, 0x1716,
1630    ///         0x1514, 0x1312,
1631    ///         0x1110, 0x0f0e,
1632    ///         0x0d0c, 0x0b0a
1633    ///     ),
1634    ///     addr
1635    /// );
1636    /// ```
1637    #[inline]
1638    fn from(octets: [u8; 16]) -> Ipv6Addr {
1639        Ipv6Addr { inner: octets }
1640    }
1641}
1642
1643impl From<[u16; 8]> for Ipv6Addr {
1644    /// Creates an `Ipv6Addr` from an eight element 16-bit array.
1645    ///
1646    /// # Examples
1647    ///
1648    /// ```
1649    /// use coreplus::net::Ipv6Addr;
1650    ///
1651    /// let addr = Ipv6Addr::from([
1652    ///     525u16, 524u16, 523u16, 522u16,
1653    ///     521u16, 520u16, 519u16, 518u16,
1654    /// ]);
1655    /// assert_eq!(
1656    ///     Ipv6Addr::new(
1657    ///         0x20d, 0x20c,
1658    ///         0x20b, 0x20a,
1659    ///         0x209, 0x208,
1660    ///         0x207, 0x206
1661    ///     ),
1662    ///     addr
1663    /// );
1664    /// ```
1665    #[inline]
1666    fn from(segments: [u16; 8]) -> Ipv6Addr {
1667        let [a, b, c, d, e, f, g, h] = segments;
1668        Ipv6Addr::new(a, b, c, d, e, f, g, h)
1669    }
1670}
1671
1672impl From<[u8; 16]> for IpAddr {
1673    /// Creates an `IpAddr::V6` from a sixteen element byte array.
1674    ///
1675    /// # Examples
1676    ///
1677    /// ```
1678    /// use coreplus::net::{IpAddr, Ipv6Addr};
1679    ///
1680    /// let addr = IpAddr::from([
1681    ///     25u8, 24u8, 23u8, 22u8, 21u8, 20u8, 19u8, 18u8,
1682    ///     17u8, 16u8, 15u8, 14u8, 13u8, 12u8, 11u8, 10u8,
1683    /// ]);
1684    /// assert_eq!(
1685    ///     IpAddr::V6(Ipv6Addr::new(
1686    ///         0x1918, 0x1716,
1687    ///         0x1514, 0x1312,
1688    ///         0x1110, 0x0f0e,
1689    ///         0x0d0c, 0x0b0a
1690    ///     )),
1691    ///     addr
1692    /// );
1693    /// ```
1694    #[inline]
1695    fn from(octets: [u8; 16]) -> IpAddr {
1696        IpAddr::V6(Ipv6Addr::from(octets))
1697    }
1698}
1699
1700impl From<[u16; 8]> for IpAddr {
1701    /// Creates an `IpAddr::V6` from an eight element 16-bit array.
1702    ///
1703    /// # Examples
1704    ///
1705    /// ```
1706    /// use coreplus::net::{IpAddr, Ipv6Addr};
1707    ///
1708    /// let addr = IpAddr::from([
1709    ///     525u16, 524u16, 523u16, 522u16,
1710    ///     521u16, 520u16, 519u16, 518u16,
1711    /// ]);
1712    /// assert_eq!(
1713    ///     IpAddr::V6(Ipv6Addr::new(
1714    ///         0x20d, 0x20c,
1715    ///         0x20b, 0x20a,
1716    ///         0x209, 0x208,
1717    ///         0x207, 0x206
1718    ///     )),
1719    ///     addr
1720    /// );
1721    /// ```
1722    #[inline]
1723    fn from(segments: [u16; 8]) -> IpAddr {
1724        IpAddr::V6(Ipv6Addr::from(segments))
1725    }
1726}
1727
1728#[cfg(feature = "std")]
1729impl From<std::net::Ipv4Addr> for Ipv4Addr {
1730    fn from(ip: std::net::Ipv4Addr) -> Self {
1731        Self::from(ip.octets())
1732    }
1733}
1734
1735#[cfg(feature = "std")]
1736impl From<std::net::Ipv4Addr> for IpAddr {
1737    fn from(ip: std::net::Ipv4Addr) -> Self {
1738        Self::from(ip.octets())
1739    }
1740}
1741
1742#[cfg(feature = "std")]
1743impl From<std::net::Ipv6Addr> for Ipv6Addr {
1744    fn from(ip: std::net::Ipv6Addr) -> Self {
1745        Self::from(ip.octets())
1746    }
1747}
1748
1749#[cfg(feature = "std")]
1750impl From<std::net::Ipv6Addr> for IpAddr {
1751    fn from(ip: std::net::Ipv6Addr) -> Self {
1752        Self::from(ip.octets())
1753    }
1754}
1755
1756#[cfg(feature = "std")]
1757impl From<std::net::IpAddr> for IpAddr {
1758    fn from(ip: std::net::IpAddr) -> Self {
1759        match ip {
1760            std::net::IpAddr::V4(ip) => ip.into(),
1761            std::net::IpAddr::V6(ip) => ip.into(),
1762        }
1763    }
1764}
1765
1766#[cfg(feature = "std")]
1767impl From<Ipv4Addr> for std::net::Ipv4Addr {
1768    fn from(ip: Ipv4Addr) -> Self {
1769        Self::from(ip.octets())
1770    }
1771}
1772
1773#[cfg(feature = "std")]
1774impl From<Ipv4Addr> for std::net::IpAddr {
1775    fn from(ip: Ipv4Addr) -> Self {
1776        Self::from(ip.octets())
1777    }
1778}
1779
1780#[cfg(feature = "std")]
1781impl From<Ipv6Addr> for std::net::Ipv6Addr {
1782    fn from(ip: Ipv6Addr) -> Self {
1783        Self::from(ip.octets())
1784    }
1785}
1786
1787#[cfg(feature = "std")]
1788impl From<Ipv6Addr> for std::net::IpAddr {
1789    fn from(ip: Ipv6Addr) -> Self {
1790        Self::from(ip.octets())
1791    }
1792}
1793
1794#[cfg(feature = "std")]
1795impl From<IpAddr> for std::net::IpAddr {
1796    fn from(ip: IpAddr) -> Self {
1797        match ip {
1798            IpAddr::V4(ip) => ip.into(),
1799            IpAddr::V6(ip) => ip.into(),
1800        }
1801    }
1802}