Skip to main content

dope_sys/io/socket/addr/
internet.rs

1use std::{mem, net, ptr};
2
3use crate::io::socket::{
4    Domain,
5    addr::{Addr, CAPACITY, Error, FAMILY_BYTES, Socklen, Source},
6};
7
8/// One native IPv4 socket address.
9#[derive(Clone, Copy)]
10#[repr(transparent)]
11pub(super) struct Inet(libc::sockaddr_in);
12
13impl Inet {
14    /// Exact byte length of one IPv4 address payload.
15    pub(super) const BYTES: usize = mem::size_of::<libc::sockaddr_in>();
16
17    fn from_std<S: Source>(addr: net::SocketAddrV4) -> Self {
18        // SAFETY: zero initializes every byte of one platform socket address.
19        let mut raw: libc::sockaddr_in = unsafe { mem::zeroed() };
20        raw.sin_port = addr.port().to_be();
21        raw.sin_addr = libc::in_addr {
22            s_addr: u32::from_ne_bytes(addr.ip().octets()),
23        };
24        // SAFETY: the native header occupies the first two initialized bytes.
25        unsafe {
26            ptr::from_mut(&mut raw)
27                .cast::<[u8; 2]>()
28                .write(S::header(Domain::Inet4, Socklen::of::<Self>()))
29        };
30
31        Self(raw)
32    }
33
34    #[must_use]
35    fn to_std(self) -> net::SocketAddrV4 {
36        let ip = net::Ipv4Addr::from(u32::from_be(self.0.sin_addr.s_addr));
37        net::SocketAddrV4::new(ip, u16::from_be(self.0.sin_port))
38    }
39}
40
41/// One native IPv6 socket address.
42#[derive(Clone, Copy)]
43#[repr(transparent)]
44pub(super) struct Inet6(libc::sockaddr_in6);
45
46impl Inet6 {
47    fn from_std<S: Source>(addr: net::SocketAddrV6) -> Self {
48        // SAFETY: zero initializes every byte of one platform socket address.
49        let mut raw: libc::sockaddr_in6 = unsafe { mem::zeroed() };
50        raw.sin6_port = addr.port().to_be();
51        raw.sin6_flowinfo = addr.flowinfo();
52        raw.sin6_addr = libc::in6_addr {
53            s6_addr: addr.ip().octets(),
54        };
55        raw.sin6_scope_id = addr.scope_id();
56        // SAFETY: the native header occupies the first two initialized bytes.
57        unsafe {
58            ptr::from_mut(&mut raw)
59                .cast::<[u8; 2]>()
60                .write(S::header(Domain::Inet6, Socklen::of::<Self>()))
61        };
62
63        Self(raw)
64    }
65
66    #[must_use]
67    fn to_std(self) -> net::SocketAddrV6 {
68        net::SocketAddrV6::new(
69            net::Ipv6Addr::from(self.0.sin6_addr.s6_addr),
70            u16::from_be(self.0.sin6_port),
71            self.0.sin6_flowinfo,
72            self.0.sin6_scope_id,
73        )
74    }
75}
76
77/// Compact IPv4 or IPv6 storage retaining its selected native length.
78#[derive(Clone, Copy)]
79pub struct Internet {
80    storage: Storage,
81    len: Socklen,
82}
83
84#[derive(Clone, Copy)]
85#[repr(C)]
86union Storage {
87    v4: Inet,
88    v6: Inet6,
89}
90
91impl Internet {
92    /// Decodes one native address without copying its borrowed storage.
93    pub fn try_parse(bytes: &[u8]) -> Result<net::SocketAddr, Error> {
94        let actual = bytes.len();
95        if actual > CAPACITY {
96            return Err(Error::Storage {
97                actual,
98                capacity: CAPACITY,
99            });
100        }
101        // SAFETY: the immutable slice retains its initialized, admitted extent.
102        unsafe { Self::decode(bytes.as_ptr(), actual) }
103    }
104
105    /// # Safety
106    /// `bytes` must retain `actual` immutable native-address bytes, `actual <= CAPACITY`,
107    /// with initialized native fields; native ABI padding need not be initialized.
108    pub(super) unsafe fn decode(bytes: *const u8, actual: usize) -> Result<net::SocketAddr, Error> {
109        if actual < FAMILY_BYTES {
110            return Err(Error::Header {
111                actual,
112                required: FAMILY_BYTES,
113            });
114        }
115        // SAFETY: header admission covers the native family field;
116        // unaligned input is accepted without exposing native padding.
117        let family = unsafe {
118            bytes
119                .add(mem::offset_of!(libc::sockaddr_storage, ss_family))
120                .cast::<libc::sa_family_t>()
121                .read_unaligned()
122        } as libc::c_int;
123
124        // SAFETY: both native payloads have integer-only fields. `read`
125        // checks their full extent before reading either selected payload.
126        Ok(unsafe {
127            match family {
128                libc::AF_INET => read::<Inet>(bytes, actual, family)?.to_std().into(),
129                libc::AF_INET6 => read::<Inet6>(bytes, actual, family)?.to_std().into(),
130                other => {
131                    return Err(Error::Unsupported {
132                        family: other,
133                        actual,
134                    });
135                }
136            }
137        })
138    }
139
140    pub fn from_std<S: Source>(address: net::SocketAddr) -> Self {
141        match address {
142            net::SocketAddr::V4(address) => Self {
143                storage: Storage {
144                    v4: Inet::from_std::<S>(address),
145                },
146                len: Socklen::of::<Inet>(),
147            },
148            net::SocketAddr::V6(address) => Self {
149                storage: Storage {
150                    v6: Inet6::from_std::<S>(address),
151                },
152                len: Socklen::of::<Inet6>(),
153            },
154        }
155    }
156
157    pub fn as_addr(&self) -> Addr<'_> {
158        Addr::new(&self.storage, self.len)
159    }
160
161    pub fn domain(&self) -> Domain {
162        if self.len.get() == Inet::BYTES {
163            Domain::Inet4
164        } else {
165            Domain::Inet6
166        }
167    }
168}
169
170/// # Safety
171/// `bytes` must retain `actual` bytes. If the extent fits `T`, its initialized
172/// fields must form a valid `T`; native padding is never exposed as bytes.
173unsafe fn read<T: Copy>(bytes: *const u8, actual: usize, family: i32) -> Result<T, Error> {
174    let required = mem::size_of::<T>();
175    if actual < required {
176        return Err(Error::Address {
177            family,
178            actual,
179            required,
180        });
181    }
182    // SAFETY: admission proves the complete caller-validated native payload.
183    Ok(unsafe { bytes.cast::<T>().read_unaligned() })
184}
185
186const _: () = assert!(mem::size_of::<Internet>() <= 32);