Skip to main content

dope_sys/io/socket/addr/
mod.rs

1//! Native socket addresses.
2
3use std::{
4    marker, mem, net, num,
5    os::fd::{AsRawFd as _, BorrowedFd},
6    ptr,
7};
8
9use crate::{Errno, io::socket::Domain, kernel};
10
11mod internet;
12pub use internet::Internet;
13
14/// Exact byte capacity of one native socket-address buffer.
15pub const CAPACITY: usize = mem::size_of::<libc::sockaddr_storage>();
16
17/// Exact byte length of the namespace prefix every address carries.
18pub const FAMILY_BYTES: usize =
19    mem::offset_of!(libc::sockaddr_storage, ss_family) + mem::size_of::<libc::sa_family_t>();
20
21/// A validated nonzero native socket-address length.
22#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
23#[repr(transparent)]
24pub struct Socklen(num::NonZero<libc::socklen_t>);
25
26impl Socklen {
27    /// Exact length of one admitted address payload.
28    const fn of<T>() -> Self {
29        const {
30            assert!(
31                mem::size_of::<T>() > 0 && mem::size_of::<T>() <= CAPACITY,
32                "address payload does not fit sockaddr_storage",
33            );
34        }
35        Self(
36            num::NonZero::<libc::socklen_t>::MIN
37                .saturating_add(mem::size_of::<T>() as libc::socklen_t - 1),
38        )
39    }
40
41    /// Validates one observed length against the exact storage capacity.
42    fn try_from_len(len: usize) -> Option<Self> {
43        if len > CAPACITY {
44            return None;
45        }
46        num::NonZero::new(len as libc::socklen_t).map(Self)
47    }
48
49    pub const fn get(self) -> usize {
50        self.0.get() as usize
51    }
52
53    pub(crate) const fn raw(self) -> libc::socklen_t {
54        self.0.get()
55    }
56}
57
58/// Native encoding of the two-byte address header selected by its backend.
59pub trait Source {
60    /// Whether every pathname must retain a trailing NUL inside the path field.
61    const UNIX_TERMINATED: bool;
62    /// Whether a leading NUL selects the native abstract namespace.
63    const UNIX_ABSTRACT: bool;
64
65    fn header(domain: Domain, len: Socklen) -> [u8; 2];
66}
67
68/// One native Unix-domain socket address with its exact path length.
69#[derive(Clone, Copy)]
70pub struct Unix {
71    raw: libc::sockaddr_un,
72    len: Socklen,
73}
74
75enum Name {
76    Path,
77    Abstract,
78}
79
80impl Unix {
81    /// Encodes one pathname, proving it fits the platform's path field.
82    pub fn from_path<S: Source>(bytes: &[u8]) -> Result<Self, PathError> {
83        Self::encode::<S>(bytes, Name::Path)
84    }
85
86    /// Encodes an abstract name without adding a trailing NUL to its identity.
87    pub fn from_name<S: Source>(bytes: &[u8]) -> Result<Self, PathError> {
88        Self::encode::<S>(bytes, Name::Abstract)
89    }
90
91    fn encode<S: Source>(bytes: &[u8], name: Name) -> Result<Self, PathError> {
92        // SAFETY: zero initializes every native address field and path byte.
93        let mut raw: libc::sockaddr_un = unsafe { mem::zeroed() };
94        let capacity = raw.sun_path.len();
95        let (prefix, suffix) = match name {
96            Name::Path => {
97                PathError::check(bytes, capacity - usize::from(S::UNIX_TERMINATED))?;
98                (0, usize::from(bytes.len() < capacity))
99            }
100            Name::Abstract => {
101                if !S::UNIX_ABSTRACT {
102                    return Err(PathError::Unsupported);
103                }
104                PathError::length(bytes.len(), capacity - 1)?;
105                (1, 0)
106            }
107        };
108        for (dst, byte) in raw.sun_path.iter_mut().skip(prefix).zip(bytes) {
109            *dst = *byte as libc::c_char;
110        }
111        let len = num::NonZero::<libc::socklen_t>::MIN.saturating_add(
112            (mem::offset_of!(libc::sockaddr_un, sun_path) + prefix + bytes.len() + suffix - 1)
113                as libc::socklen_t,
114        );
115        let len = Socklen(len);
116        // SAFETY: the native header occupies the first two initialized bytes.
117        unsafe {
118            ptr::from_mut(&mut raw)
119                .cast::<[u8; 2]>()
120                .write(S::header(Domain::Unix, len))
121        };
122        Ok(Self { raw, len })
123    }
124
125    /// Typed view of this address for one syscall.
126    #[must_use]
127    pub fn as_addr(&self) -> Addr<'_> {
128        Addr::new(&self.raw, self.len)
129    }
130}
131
132/// An address length the kernel may overwrite in place.
133#[derive(Clone, Copy, Debug)]
134#[repr(transparent)]
135pub struct Length(libc::socklen_t);
136
137/// One native address buffer with its exact length.
138#[derive(Clone, Copy)]
139#[repr(C)]
140pub struct Storage<L = Length> {
141    storage: libc::sockaddr_storage,
142    len: L,
143}
144
145impl Storage<Length> {
146    #[must_use]
147    pub fn empty() -> Self {
148        Self {
149            storage: unsafe { mem::zeroed() },
150            len: Length(CAPACITY as libc::socklen_t),
151        }
152    }
153
154    /// Exclusive view of the buffer and the length the kernel overwrites.
155    pub fn output_mut(&mut self) -> (&mut libc::sockaddr_storage, &mut libc::socklen_t) {
156        (&mut self.storage, &mut self.len.0)
157    }
158}
159
160impl Storage<Socklen> {
161    #[must_use]
162    pub fn as_addr(&self) -> Addr<'_> {
163        Addr::new(&self.storage, self.len)
164    }
165
166    /// Copies one exact native address view into a fresh buffer.
167    #[must_use]
168    pub fn from_addr(source: Addr<'_>) -> Self {
169        let (addr, len) = source.parts();
170        let mut out = Self {
171            storage: unsafe { mem::zeroed() },
172            len,
173        };
174        // SAFETY: the borrowed view retains its complete native payload; the
175        // admitted length fits this distinct destination without losing padding.
176        unsafe {
177            ptr::copy_nonoverlapping(
178                addr.cast::<u8>(),
179                ptr::from_mut(&mut out.storage).cast(),
180                len.get(),
181            )
182        };
183        out
184    }
185}
186
187impl<L> Storage<L> {
188    fn decode(&self, actual: usize) -> Result<net::SocketAddr, Error> {
189        // SAFETY: both callers admit actual <= CAPACITY; this immutable
190        // native storage retains its initialized address fields.
191        unsafe { Internet::decode(ptr::from_ref(&self.storage).cast(), actual) }
192    }
193}
194
195const _: () = {
196    assert!(CAPACITY <= libc::socklen_t::MAX as usize);
197    let fields = CAPACITY + mem::size_of::<libc::socklen_t>();
198    let align = mem::align_of::<Storage<Socklen>>();
199    assert!(mem::size_of::<Storage<Socklen>>() == fields.div_ceil(align) * align);
200};
201
202/// Exact native address decoding failure.
203#[derive(Clone, Copy, Debug, Eq, PartialEq)]
204pub enum Error {
205    Storage {
206        actual: usize,
207        capacity: usize,
208    },
209    Header {
210        actual: usize,
211        required: usize,
212    },
213    Address {
214        family: i32,
215        actual: usize,
216        required: usize,
217    },
218    Unsupported {
219        family: i32,
220        actual: usize,
221    },
222}
223
224impl Storage<Socklen> {
225    pub fn to_std(&self) -> Result<net::SocketAddr, Error> {
226        self.decode(self.len.get())
227    }
228}
229
230impl Storage<Length> {
231    pub fn to_std(&self) -> Result<net::SocketAddr, Error> {
232        let actual = self.len.0 as usize;
233        if actual > CAPACITY {
234            return Err(Error::Storage {
235                actual,
236                capacity: CAPACITY,
237            });
238        }
239        self.decode(actual)
240    }
241}
242
243/// Exact Unix-domain name validation failure.
244#[derive(Clone, Copy, Debug, Eq, PartialEq)]
245pub enum PathError {
246    Unsupported,
247    Empty,
248    Length { actual: usize, maximum: usize },
249    Nul { offset: usize },
250}
251
252impl PathError {
253    fn check(bytes: &[u8], maximum: usize) -> Result<(), Self> {
254        if bytes.is_empty() {
255            return Err(Self::Empty);
256        }
257        Self::length(bytes.len(), maximum)?;
258        if let Some(offset) = bytes.iter().position(|byte| *byte == 0) {
259            return Err(Self::Nul { offset });
260        }
261        Ok(())
262    }
263
264    fn length(actual: usize, maximum: usize) -> Result<(), Self> {
265        if actual > maximum {
266            return Err(Self::Length { actual, maximum });
267        }
268        Ok(())
269    }
270}
271
272/// Exact native address extent borrowed from its immutable owner.
273#[derive(Clone, Copy)]
274#[repr(C)]
275pub struct Addr<'a> {
276    addr: ptr::NonNull<libc::sockaddr>,
277    len: Socklen,
278    owner: marker::PhantomData<&'a libc::sockaddr>,
279}
280
281/// Exact failure while observing one descriptor's local address.
282#[derive(Clone, Copy, Debug, Eq, PartialEq)]
283pub enum Local<E> {
284    Syscall(E),
285    Length { actual: usize, capacity: usize },
286}
287
288impl<'a> Addr<'a> {
289    fn new<T>(storage: &'a T, len: Socklen) -> Self {
290        Self {
291            addr: ptr::NonNull::from(storage).cast(),
292            len,
293            owner: marker::PhantomData,
294        }
295    }
296
297    pub const fn parts(self) -> (*const libc::sockaddr, Socklen) {
298        (self.addr.as_ptr().cast_const(), self.len)
299    }
300
301    pub fn local<S: kernel::raw::Source + ?Sized, E: From<Errno>>(
302        file: BorrowedFd<'_>,
303        storage: &'a mut Storage<Length>,
304    ) -> Result<Self, Local<E>> {
305        let (address, length) = storage.output_mut();
306        *length = CAPACITY as libc::socklen_t;
307        // SAFETY: the exclusive address and length borrows retain their full
308        // capacity through getsockname, which returns zero or minus one.
309        let status =
310            unsafe { libc::getsockname(file.as_raw_fd(), ptr::from_mut(address).cast(), length) };
311        // SAFETY: getsockname returns zero or -1; failure reads its unchanged errno.
312        unsafe {
313            kernel::raw::Status::new(status).try_zero::<E>(|| kernel::raw::Code::last::<S>().into())
314        }
315        .map_err(Local::Syscall)?;
316        let actual = *length as usize;
317        let len = Socklen::try_from_len(actual).ok_or(Local::Length {
318            actual,
319            capacity: CAPACITY,
320        })?;
321        Ok(Self::new(&storage.storage, len))
322    }
323
324    pub fn try_bind<S: kernel::raw::Source + ?Sized, E: From<Errno>>(
325        self,
326        file: BorrowedFd<'_>,
327    ) -> Result<(), E> {
328        // SAFETY: the borrowed native payload retains its exact admitted extent.
329        let status = unsafe { libc::bind(file.as_raw_fd(), self.addr.as_ptr(), self.len.raw()) };
330        // SAFETY: bind returns zero or -1; failure reads its unchanged errno.
331        unsafe {
332            kernel::raw::Status::new(status).try_zero(|| kernel::raw::Code::last::<S>().into())
333        }
334    }
335
336    pub fn try_connect<S: kernel::raw::Source + ?Sized, E: From<Errno>>(
337        self,
338        file: BorrowedFd<'_>,
339    ) -> Result<(), E> {
340        // SAFETY: the borrowed native payload retains its exact admitted extent.
341        let status = unsafe { libc::connect(file.as_raw_fd(), self.addr.as_ptr(), self.len.raw()) };
342        // SAFETY: connect returns zero or -1; failure reads its unchanged errno.
343        unsafe {
344            kernel::raw::Status::new(status).try_zero(|| kernel::raw::Code::last::<S>().into())
345        }
346    }
347}