Skip to main content

sentinel_driver/types/
network.rs

1use bytes::{BufMut, BytesMut};
2use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
3
4use crate::error::{Error, Result};
5use crate::types::{FromSql, Oid, ToSql};
6
7const AF_INET: u8 = 2;
8const AF_INET6: u8 = 3;
9
10/// PostgreSQL INET type -- an IP address with optional subnet mask.
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12pub struct PgInet {
13    pub addr: IpAddr,
14    pub netmask: u8,
15}
16
17/// PostgreSQL CIDR type -- a network address (same wire format as INET with is_cidr flag).
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
19pub struct PgCidr {
20    pub addr: IpAddr,
21    pub netmask: u8,
22}
23
24/// PostgreSQL MACADDR type -- 6-byte MAC address.
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
26pub struct PgMacAddr(pub [u8; 6]);
27
28// -- INET / CIDR shared helpers --
29
30fn encode_inet(addr: &IpAddr, netmask: u8, is_cidr: bool, buf: &mut BytesMut) {
31    match addr {
32        IpAddr::V4(v4) => {
33            buf.put_u8(AF_INET);
34            buf.put_u8(netmask);
35            buf.put_u8(u8::from(is_cidr));
36            buf.put_u8(4);
37            buf.put_slice(&v4.octets());
38        }
39        IpAddr::V6(v6) => {
40            buf.put_u8(AF_INET6);
41            buf.put_u8(netmask);
42            buf.put_u8(u8::from(is_cidr));
43            buf.put_u8(16);
44            buf.put_slice(&v6.octets());
45        }
46    }
47}
48
49fn decode_inet(buf: &[u8]) -> Result<(IpAddr, u8, bool)> {
50    if buf.len() < 4 {
51        return Err(Error::Decode(format!(
52            "inet: expected at least 4 bytes, got {}",
53            buf.len()
54        )));
55    }
56
57    let family = buf[0];
58    let netmask = buf[1];
59    let is_cidr = buf[2] != 0;
60    let addr_len = buf[3] as usize;
61
62    if buf.len() < 4 + addr_len {
63        return Err(Error::Decode(format!(
64            "inet: address truncated, expected {} bytes, got {}",
65            4 + addr_len,
66            buf.len()
67        )));
68    }
69
70    let addr = match family {
71        AF_INET => {
72            if addr_len != 4 {
73                return Err(Error::Decode(format!(
74                    "inet: IPv4 address should be 4 bytes, got {addr_len}"
75                )));
76            }
77            IpAddr::V4(Ipv4Addr::new(buf[4], buf[5], buf[6], buf[7]))
78        }
79        AF_INET6 => {
80            if addr_len != 16 {
81                return Err(Error::Decode(format!(
82                    "inet: IPv6 address should be 16 bytes, got {addr_len}"
83                )));
84            }
85            let octets: [u8; 16] = buf[4..20]
86                .try_into()
87                .map_err(|_| Error::Decode("inet: IPv6 slice error".into()))?;
88            IpAddr::V6(Ipv6Addr::from(octets))
89        }
90        _ => {
91            return Err(Error::Decode(format!(
92                "inet: unknown address family {family}"
93            )));
94        }
95    };
96
97    Ok((addr, netmask, is_cidr))
98}
99
100// -- PgInet --
101
102impl ToSql for PgInet {
103    fn oid(&self) -> Oid {
104        Oid::INET
105    }
106
107    fn to_sql(&self, buf: &mut BytesMut) -> Result<()> {
108        encode_inet(&self.addr, self.netmask, false, buf);
109        Ok(())
110    }
111}
112
113impl FromSql for PgInet {
114    fn oid() -> Oid {
115        Oid::INET
116    }
117
118    fn from_sql(buf: &[u8]) -> Result<Self> {
119        let (addr, netmask, _is_cidr) = decode_inet(buf)?;
120        Ok(PgInet { addr, netmask })
121    }
122}
123
124// -- PgCidr --
125
126impl ToSql for PgCidr {
127    fn oid(&self) -> Oid {
128        Oid::CIDR
129    }
130
131    fn to_sql(&self, buf: &mut BytesMut) -> Result<()> {
132        encode_inet(&self.addr, self.netmask, true, buf);
133        Ok(())
134    }
135}
136
137impl FromSql for PgCidr {
138    fn oid() -> Oid {
139        Oid::CIDR
140    }
141
142    fn from_sql(buf: &[u8]) -> Result<Self> {
143        let (addr, netmask, _is_cidr) = decode_inet(buf)?;
144        Ok(PgCidr { addr, netmask })
145    }
146}
147
148// -- PgMacAddr --
149
150impl ToSql for PgMacAddr {
151    fn oid(&self) -> Oid {
152        Oid::MACADDR
153    }
154
155    fn to_sql(&self, buf: &mut BytesMut) -> Result<()> {
156        buf.put_slice(&self.0);
157        Ok(())
158    }
159}
160
161impl FromSql for PgMacAddr {
162    fn oid() -> Oid {
163        Oid::MACADDR
164    }
165
166    fn from_sql(buf: &[u8]) -> Result<Self> {
167        let arr: [u8; 6] = buf
168            .try_into()
169            .map_err(|_| Error::Decode(format!("macaddr: expected 6 bytes, got {}", buf.len())))?;
170        Ok(PgMacAddr(arr))
171    }
172}
173
174// -- PgMacAddr8 (EUI-64, PG 10+) --
175
176/// PostgreSQL MACADDR8 type -- 8-byte extended MAC address (EUI-64).
177#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
178pub struct PgMacAddr8(pub [u8; 8]);
179
180impl ToSql for PgMacAddr8 {
181    fn oid(&self) -> Oid {
182        Oid::MACADDR8
183    }
184
185    fn to_sql(&self, buf: &mut BytesMut) -> Result<()> {
186        buf.put_slice(&self.0);
187        Ok(())
188    }
189}
190
191impl FromSql for PgMacAddr8 {
192    fn oid() -> Oid {
193        Oid::MACADDR8
194    }
195
196    fn from_sql(buf: &[u8]) -> Result<Self> {
197        let arr: [u8; 8] = buf
198            .try_into()
199            .map_err(|_| Error::Decode(format!("macaddr8: expected 8 bytes, got {}", buf.len())))?;
200        Ok(PgMacAddr8(arr))
201    }
202}
203
204// -- std::net::IpAddr convenience impls --
205
206impl ToSql for IpAddr {
207    fn oid(&self) -> Oid {
208        Oid::INET
209    }
210
211    fn to_sql(&self, buf: &mut BytesMut) -> Result<()> {
212        let netmask = match self {
213            IpAddr::V4(_) => 32,
214            IpAddr::V6(_) => 128,
215        };
216        encode_inet(self, netmask, false, buf);
217        Ok(())
218    }
219}
220
221impl FromSql for IpAddr {
222    fn oid() -> Oid {
223        Oid::INET
224    }
225
226    fn from_sql(buf: &[u8]) -> Result<Self> {
227        let (addr, _netmask, _is_cidr) = decode_inet(buf)?;
228        Ok(addr)
229    }
230}