Skip to main content

internet/ietf/ipv4/encoding/
address.rs

1//! IPv4 Address and Network types.
2//!
3//! As defined in [RFC 791].
4//!
5//! [IETF RFC 791]: https://datatracker.ietf.org/doc/html/rfc791
6
7use crate::{Buf, BufMut, BufResult, Codec, Cursor};
8use core::fmt;
9
10/// An IPv4 Address.
11#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
12#[repr(transparent)]
13pub struct Address(pub [u8; 4]);
14
15impl Address {
16    /// The unspecified address (`0.0.0.0`).
17    pub const UNSPECIFIED: Self = Self([0, 0, 0, 0]);
18
19    /// The broadcast address (`255.255.255.255`).
20    pub const BROADCAST: Self = Self([255, 255, 0, 0]);
21
22    /// The loopback address (`127.0.0.1`).
23    pub const LOOPBACK: Self = Self([127, 0, 0, 1]);
24
25    /// Creates a new IPv4 address from four octets.
26    pub const fn new(a: u8, b: u8, c: u8, d: u8) -> Self {
27        Self([a, b, c, d])
28    }
29
30    /// Creates a new IPv4 address from an array of octets.
31    pub const fn from_octets(octets: [u8; 4]) -> Self {
32        Self(octets)
33    }
34
35    /// Returns the four octets of the address as an array.
36    pub const fn octets(self) -> [u8; 4] {
37        self.0
38    }
39
40    /// Creates a new IPv4 address from a 32-bit big-endian integer.
41    pub const fn from_bits(bits: u32) -> Self {
42        Self(bits.to_be_bytes())
43    }
44
45    /// Returns the address as a 32-bit big-endian integer.
46    pub const fn to_bits(self) -> u32 {
47        u32::from_be_bytes(self.0)
48    }
49
50    /// Returns `true` if this is a private address (10.x.x.x, 172.16-31.x.x, 192.168.x.x).
51    pub const fn is_private(self) -> bool {
52        self.0[0] == 10
53            || (self.0[0] == 172 && (self.0[1] & 0xF0) == 16)
54            || (self.0[0] == 192 && self.0[1] == 168)
55    }
56
57    /// Returns `true` if this is a link-local address (169.254.x.x).
58    pub const fn is_link_local(self) -> bool {
59        self.0[0] == 169 && self.0[1] == 254
60    }
61
62    /// Returns `true` if this is a multicast address (224.0.0.0 to 239.255.255.255).
63    pub const fn is_multicast(self) -> bool {
64        (self.0[0] & 0xF0) == 224
65    }
66
67    /// Returns `true` if this is a documentation address (192.0.2.x, 198.51.100.x, 203.0.113.x).
68    pub const fn is_documentation(self) -> bool {
69        matches!(
70            (self.0[0], self.0[1], self.0[2]),
71            (192, 0, 2) | (198, 51, 100) | (203, 0, 113)
72        )
73    }
74}
75
76#[cfg(feature = "std")]
77impl From<std::net::Ipv4Addr> for Address {
78    fn from(addr: std::net::Ipv4Addr) -> Self {
79        Self(addr.octets())
80    }
81}
82
83#[cfg(feature = "std")]
84impl From<Address> for std::net::Ipv4Addr {
85    fn from(addr: Address) -> Self {
86        std::net::Ipv4Addr::from(addr.0)
87    }
88}
89
90impl From<[u8; 4]> for Address {
91    fn from(octets: [u8; 4]) -> Self {
92        Self(octets)
93    }
94}
95
96impl From<Address> for [u8; 4] {
97    fn from(addr: Address) -> Self {
98        addr.0
99    }
100}
101
102impl From<u32> for Address {
103    fn from(value: u32) -> Self {
104        Self(value.to_be_bytes())
105    }
106}
107
108impl From<Address> for u32 {
109    fn from(addr: Address) -> Self {
110        addr.to_bits()
111    }
112}
113
114impl Codec for Address {
115    fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
116        self.0.encode(writer, ())
117    }
118
119    fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
120        Ok(Self(reader.read_array::<4>()?))
121    }
122}
123
124impl fmt::Display for Address {
125    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126        write!(f, "{}.{}.{}.{}", self.0[0], self.0[1], self.0[2], self.0[3])
127    }
128}
129
130#[cfg(test)]
131mod tests {
132    use super::*;
133
134    #[test]
135    fn address_properties() {
136        assert!(Address::new(10, 0, 0, 1).is_private());
137        assert!(Address::new(192, 168, 1, 1).is_private());
138        assert!(Address::new(169, 254, 1, 1).is_link_local());
139        assert!(Address::new(224, 0, 0, 1).is_multicast());
140        assert!(Address::new(192, 0, 2, 1).is_documentation());
141    }
142}