internet 0.1.0

Network library for rust
Documentation
//! IPv6 Address types.
//!
//! As defined in [RFC 8200].
//!
//! [IETF RFC 8200]: https://datatracker.ietf.org/doc/html/rfc8200

use crate::{Buf, BufMut, BufResult, Codec, Cursor};
use core::fmt;

/// An IPv6 Address.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct Address(pub [u8; 16]);

impl Address {
    /// The unspecified address (`::`).
    pub const UNSPECIFIED: Self = Self([0; 16]);

    /// The loopback address (`::1`).
    pub const LOOPBACK: Self = Self([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);

    /// Creates a new IPv6 address from an array of octets.
    pub const fn from_octets(octets: [u8; 16]) -> Self {
        Self(octets)
    }

    /// Returns the sixteen octets of the address as an array.
    pub const fn octets(self) -> [u8; 16] {
        self.0
    }

    /// Returns `true` if this is a multicast address (`ff00::/8`).
    pub const fn is_multicast(self) -> bool {
        self.0[0] == 0xFF
    }

    /// Returns `true` if this is a link-local unicast address (`fe80::/10`).
    pub const fn is_link_local(self) -> bool {
        self.0[0] == 0xFE && (self.0[1] & 0xC0) == 0x80
    }

    /// Returns `true` if this is a unique local address (`fc00::/7`).
    pub const fn is_unique_local(self) -> bool {
        (self.0[0] & 0xFE) == 0xFC
    }

    /// Returns `true` if this is a global unicast address.
    pub const fn is_global_unicast(self) -> bool {
        !self.is_multicast() && !self.is_link_local() && !self.is_unique_local()
    }
}

#[cfg(feature = "std")]
impl From<std::net::Ipv6Addr> for Address {
    fn from(addr: std::net::Ipv6Addr) -> Self {
        Self(addr.octets())
    }
}

#[cfg(feature = "std")]
impl From<Address> for std::net::Ipv6Addr {
    fn from(addr: Address) -> Self {
        std::net::Ipv6Addr::from(addr.0)
    }
}

impl From<[u8; 16]> for Address {
    fn from(octets: [u8; 16]) -> Self {
        Self(octets)
    }
}

impl From<Address> for [u8; 16] {
    fn from(addr: Address) -> Self {
        addr.0
    }
}

impl Codec for Address {
    fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
        self.0.encode(writer, ())
    }

    fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
        Ok(Self(reader.read_array::<16>()?))
    }
}

impl fmt::Display for Address {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        #[cfg(feature = "std")]
        {
            write!(f, "{}", std::net::Ipv6Addr::from(*self))
        }
        #[cfg(not(feature = "std"))]
        {
            for (i, &byte) in self.0.iter().enumerate() {
                if i % 2 == 0 && i != 0 {
                    write!(f, ":")?;
                }
                write!(f, "{:02x}", byte)?;
            }
            Ok(())
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn address_properties() {
        assert!(Address::UNSPECIFIED.is_global_unicast() == false);
        assert!(Address::LOOPBACK.is_global_unicast() == false);

        let multicast =
            Address::from_octets([0xFF, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
        assert!(multicast.is_multicast());

        let link_local =
            Address::from_octets([0xFE, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
        assert!(link_local.is_link_local());

        let unique_local =
            Address::from_octets([0xFC, 0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
        assert!(unique_local.is_unique_local());
    }
}