Skip to main content

dope_sys/io/socket/
mod.rs

1use std::{
2    mem, net, num,
3    os::fd::{AsFd, AsRawFd as _, BorrowedFd, OwnedFd},
4    ptr,
5};
6
7use libc::socklen_t;
8use option::fast;
9
10use crate::{
11    Cloexec, Errno, Nonblock,
12    io::{self, fd},
13    kernel::{self, errno},
14};
15
16pub mod accept;
17pub mod addr;
18pub mod gso;
19pub mod msg;
20pub mod option;
21pub mod raw;
22pub mod record;
23pub mod route;
24
25/// Exact backlog bound the host admits for one listening socket.
26pub const BACKLOG: i32 = libc::SOMAXCONN;
27
28/// Exact synchronous connect failure.
29#[derive(Clone, Copy, Debug, Eq, PartialEq)]
30#[repr(transparent)]
31pub struct Connect(Errno);
32
33impl Connect {
34    pub const fn errno(self) -> Errno {
35        self.0
36    }
37}
38
39impl From<Errno> for Connect {
40    fn from(errno: Errno) -> Self {
41        Self(errno)
42    }
43}
44
45const _: () = assert!(size_of::<Connect>() == size_of::<Errno>());
46
47/// Exact failure to create and initialize one socket descriptor.
48#[derive(Clone, Copy, PartialEq, Eq, Debug)]
49#[repr(C)]
50pub struct Create {
51    fault: Errno,
52    kind: Option<SetupKind>,
53}
54
55/// Concrete leaf retained by one compact creation failure.
56pub enum Origin {
57    Socket(Errno),
58    Setup(Setup),
59}
60
61impl Create {
62    /// Records failure of the descriptor-creation syscall itself.
63    pub const fn socket(fault: Errno) -> Self {
64        Self { fault, kind: None }
65    }
66
67    /// Records the exact descriptor setup operation that failed after creation.
68    pub const fn setup(error: Setup) -> Self {
69        Self {
70            fault: error.fault,
71            kind: Some(error.kind),
72        }
73    }
74
75    pub const fn origin(self) -> Origin {
76        match self.kind {
77            Some(kind) => Origin::Setup(Setup {
78                fault: self.fault,
79                kind,
80            }),
81            None => Origin::Socket(self.fault),
82        }
83    }
84}
85
86const _: () = assert!(mem::size_of::<Create>() == 2 * mem::size_of::<u32>());
87
88#[derive(Clone, Copy, PartialEq, Eq)]
89pub enum Domain {
90    Inet4,
91    Inet6,
92    Unix,
93}
94
95impl Domain {
96    pub fn for_addr(addr: &net::SocketAddr) -> Self {
97        if addr.is_ipv4() {
98            Self::Inet4
99        } else {
100            Self::Inet6
101        }
102    }
103
104    pub const fn raw(self) -> libc::c_int {
105        use libc::{AF_INET, AF_INET6};
106        match self {
107            Self::Inet4 => AF_INET,
108            Self::Inet6 => AF_INET6,
109            Self::Unix => libc::AF_UNIX,
110        }
111    }
112}
113
114/// One observed `SO_TYPE` value of an adopted descriptor.
115#[derive(Clone, Copy, Debug, Eq, PartialEq)]
116#[repr(transparent)]
117pub struct Type(libc::c_int);
118
119impl Type {
120    /// Whether this descriptor is one connected byte stream.
121    pub const fn is_stream(self) -> bool {
122        self.0 == libc::SOCK_STREAM
123    }
124
125    /// Whether this descriptor is one datagram socket.
126    pub const fn is_datagram(self) -> bool {
127        self.0 == libc::SOCK_DGRAM
128    }
129
130    /// Raw observed value, for exact diagnostics.
131    pub const fn raw(self) -> i32 {
132        self.0
133    }
134}
135
136/// One half-close direction for [`Socket::shutdown`].
137#[derive(Clone, Copy, Debug, Eq, PartialEq)]
138#[repr(transparent)]
139pub struct How(libc::c_int);
140
141impl How {
142    pub const READ: Self = Self(libc::SHUT_RD);
143    pub const WRITE: Self = Self(libc::SHUT_WR);
144    pub const BOTH: Self = Self(libc::SHUT_RDWR);
145
146    pub const fn raw(self) -> libc::c_int {
147        self.0
148    }
149}
150
151/// One requested address reuse admitted before binding.
152#[derive(Clone, Copy, Debug, Eq, PartialEq)]
153pub enum Reuse {
154    Address,
155    Port,
156}
157
158/// Exact failure while opening a close-on-exec socket.
159#[derive(Clone, Copy, Debug, PartialEq, Eq)]
160pub enum Failure {
161    Socket(Errno),
162    Cloexec(Cloexec),
163}
164
165impl From<Errno> for Failure {
166    fn from(error: Errno) -> Self {
167        Self::Socket(error)
168    }
169}
170
171impl From<Cloexec> for Failure {
172    fn from(error: Cloexec) -> Self {
173        Self::Cloexec(error)
174    }
175}
176
177#[derive(Clone, Copy, PartialEq, Eq)]
178pub enum Kind {
179    Stream,
180    Dgram,
181}
182
183impl Kind {
184    pub const fn raw(self) -> libc::c_int {
185        use libc::{SOCK_DGRAM, SOCK_STREAM};
186        match self {
187            Self::Stream => SOCK_STREAM,
188            Self::Dgram => SOCK_DGRAM,
189        }
190    }
191}
192
193/// Native socket ownership and fast-open support before driver installation.
194pub trait Prepare: Socket {
195    type Handle: AsFd;
196    type Fast;
197
198    fn fast_open(depth: Option<num::NonZero<i32>>) -> Result<Option<Self::Fast>, fast::Error>;
199    fn open(domain: Domain, kind: Kind) -> Result<Self::Handle, Create>;
200    fn listener(domain: Domain) -> Result<Self::Handle, Create>;
201    fn adopt(file: OwnedFd) -> Result<Self::Handle, Setup>;
202    /// Transfers the prepared socket's descriptor without changing its native configuration.
203    fn into_fd(handle: Self::Handle) -> OwnedFd;
204    fn set_fast(file: BorrowedFd<'_>, fast: Self::Fast) -> Result<(), Errno>;
205}
206
207/// Exact socket descriptor setup failure.
208#[derive(Clone, Copy, PartialEq, Eq, Debug)]
209#[repr(C)]
210pub struct Setup {
211    fault: Errno,
212    kind: SetupKind,
213}
214
215/// Concrete leaf retained by one compact setup failure.
216pub enum SetupOrigin {
217    Cloexec(Cloexec),
218    Nonblock(Nonblock),
219    Sigpipe(Errno),
220}
221
222#[derive(Clone, Copy, Debug, PartialEq, Eq)]
223#[repr(u8)]
224enum SetupKind {
225    GetFd,
226    SetFd,
227    GetFlags,
228    SetFlags,
229    Sigpipe,
230}
231
232impl Setup {
233    pub const fn cloexec(error: Cloexec) -> Self {
234        match error {
235            Cloexec::Get(fault) => Self {
236                fault,
237                kind: SetupKind::GetFd,
238            },
239            Cloexec::Set(fault) => Self {
240                fault,
241                kind: SetupKind::SetFd,
242            },
243        }
244    }
245
246    pub const fn nonblock(error: Nonblock) -> Self {
247        match error {
248            Nonblock::Get(fault) => Self {
249                fault,
250                kind: SetupKind::GetFlags,
251            },
252            Nonblock::Set(fault) => Self {
253                fault,
254                kind: SetupKind::SetFlags,
255            },
256        }
257    }
258
259    pub const fn sigpipe(fault: Errno) -> Self {
260        Self {
261            fault,
262            kind: SetupKind::Sigpipe,
263        }
264    }
265
266    pub const fn origin(self) -> SetupOrigin {
267        match self.kind {
268            SetupKind::GetFd => SetupOrigin::Cloexec(Cloexec::Get(self.fault)),
269            SetupKind::SetFd => SetupOrigin::Cloexec(Cloexec::Set(self.fault)),
270            SetupKind::GetFlags => SetupOrigin::Nonblock(Nonblock::Get(self.fault)),
271            SetupKind::SetFlags => SetupOrigin::Nonblock(Nonblock::Set(self.fault)),
272            SetupKind::Sigpipe => SetupOrigin::Sigpipe(self.fault),
273        }
274    }
275}
276
277const _: () = assert!(mem::size_of::<Setup>() == 2 * mem::size_of::<u32>());
278
279/// Native socket creation and asynchronous tuning capability.
280pub trait Socket: errno::Source {
281    const MAX_IOVECS: usize;
282    type Error;
283
284    fn socket(domain: libc::c_int, kind: libc::c_int) -> Result<OwnedFd, Self::Error>;
285    /// Creates exclusively owned local socket endpoints.
286    fn pair() -> Result<(OwnedFd, OwnedFd), record::Error>;
287
288    /// Creates one datagram descriptor without changing its descriptor flags.
289    fn datagram<E: From<Errno>>(domain: Domain) -> Result<OwnedFd, E> {
290        let protocol = match domain {
291            Domain::Inet4 | Domain::Inet6 => libc::IPPROTO_UDP,
292            Domain::Unix => 0,
293        };
294        Self::try_open(domain.raw(), libc::SOCK_DGRAM, protocol)
295    }
296
297    fn try_open<E: From<Errno>>(
298        domain: libc::c_int,
299        kind: libc::c_int,
300        protocol: libc::c_int,
301    ) -> Result<OwnedFd, E> {
302        let raw = unsafe { libc::socket(domain, kind, protocol) };
303        unsafe {
304            <OwnedFd as fd::raw::Adopt>::try_adopt(raw, || kernel::raw::Code::last::<Self>().into())
305        }
306    }
307
308    fn listen<E: From<Errno>>(file: BorrowedFd<'_>, backlog: libc::c_int) -> Result<(), E> {
309        // SAFETY: listen returns exactly zero or minus one; errno is read immediately.
310        unsafe {
311            kernel::raw::Status::new(libc::listen(file.as_raw_fd(), backlog))
312                .try_zero(|| kernel::raw::Code::last::<Self>().into())
313        }
314    }
315
316    /// Half-closes one socket in the requested direction.
317    fn shutdown<E: From<Errno>>(file: BorrowedFd<'_>, how: How) -> Result<(), E> {
318        // SAFETY: shutdown returns exactly zero or minus one; errno is read immediately.
319        unsafe {
320            kernel::raw::Status::new(libc::shutdown(file.as_raw_fd(), how.raw()))
321                .try_zero(|| kernel::raw::Code::last::<Self>().into())
322        }
323    }
324
325    /// Observes the connected peer into caller-owned native address storage.
326    fn peer_addr<E: From<Errno>>(
327        file: BorrowedFd<'_>,
328        address: &mut addr::Storage,
329    ) -> Result<(), E> {
330        let (storage, len) = address.output_mut();
331        *len = mem::size_of_val(storage) as socklen_t;
332        // SAFETY: both exclusive output borrows retain their full native extents.
333        let raw =
334            unsafe { libc::getpeername(file.as_raw_fd(), ptr::from_mut(storage).cast(), len) };
335        // SAFETY: getpeername returns zero or -1; errno is captured immediately.
336        unsafe {
337            kernel::raw::Status::new(raw).try_zero(|| kernel::raw::Code::last::<Self>().into())
338        }
339    }
340
341    /// Makes closing this exclusively owned connection send a reset.
342    fn reset_on_close<E: From<Errno>>(file: BorrowedFd<'_>) -> Result<(), E> {
343        let linger = libc::linger {
344            l_onoff: 1,
345            l_linger: 0,
346        };
347        // SAFETY: the live descriptor and complete native value outlive the call.
348        let raw = unsafe {
349            libc::setsockopt(
350                file.as_raw_fd(),
351                libc::SOL_SOCKET,
352                libc::SO_LINGER,
353                ptr::from_ref(&linger).cast(),
354                mem::size_of_val(&linger) as socklen_t,
355            )
356        };
357        // SAFETY: setsockopt returns zero or -1; errno is captured immediately.
358        unsafe {
359            kernel::raw::Status::new(raw).try_zero(|| kernel::raw::Code::last::<Self>().into())
360        }
361    }
362
363    /// Observes the socket type of one adopted descriptor.
364    fn kind<E: From<Errno>>(file: BorrowedFd<'_>) -> Result<Type, E> {
365        Self::get_option::<{ libc::SO_TYPE }, E>(file).map(Type)
366    }
367
368    /// Whether one adopted descriptor is already listening.
369    fn is_listening<E: From<Errno>>(file: BorrowedFd<'_>) -> Result<bool, E> {
370        Ok(Self::get_option::<{ libc::SO_ACCEPTCONN }, E>(file)? != 0)
371    }
372
373    /// Admits the requested address reuse on one socket.
374    fn set_reuse<E: From<Errno>>(file: BorrowedFd<'_>, reuse: Reuse) -> Result<(), E> {
375        let option = match reuse {
376            Reuse::Address => libc::SO_REUSEADDR,
377            Reuse::Port => libc::SO_REUSEPORT,
378        };
379        Self::set_option(file, libc::SOL_SOCKET, option, &1)
380    }
381
382    /// Sends one byte string without raising the termination signal.
383    fn send_quiet<'a, E: From<Errno>>(
384        file: BorrowedFd<'_>,
385        input: &'a [u8],
386    ) -> Result<&'a [u8], E> {
387        Self::send::<{ libc::MSG_NOSIGNAL }, E>(file, input)
388    }
389
390    /// Sends without waiting or raising the termination signal.
391    fn send_now<'a, E: From<Errno>>(file: BorrowedFd<'_>, input: &'a [u8]) -> Result<&'a [u8], E> {
392        Self::send::<{ libc::MSG_DONTWAIT | libc::MSG_NOSIGNAL }, E>(file, input)
393    }
394
395    fn get_option<const OPTION: libc::c_int, E: From<Errno>>(
396        file: BorrowedFd<'_>,
397    ) -> Result<libc::c_int, E> {
398        const {
399            assert!(
400                OPTION == libc::SO_TYPE
401                    || OPTION == libc::SO_ACCEPTCONN
402                    || OPTION == libc::SO_ERROR
403            );
404        }
405        let mut value = 0;
406        let mut len = mem::size_of::<libc::c_int>() as socklen_t;
407        let raw = unsafe {
408            libc::getsockopt(
409                file.as_raw_fd(),
410                libc::SOL_SOCKET,
411                OPTION,
412                ptr::from_mut(&mut value).cast(),
413                &mut len,
414            )
415        };
416        // SAFETY: getsockopt returns exactly zero or minus one.
417        unsafe {
418            kernel::raw::Status::new(raw).try_zero::<E>(|| kernel::raw::Code::last::<Self>().into())
419        }?;
420        Ok(value)
421    }
422
423    fn set_option<E: From<Errno>>(
424        file: BorrowedFd<'_>,
425        level: libc::c_int,
426        option: libc::c_int,
427        value: &libc::c_int,
428    ) -> Result<(), E> {
429        let raw = unsafe {
430            libc::setsockopt(
431                file.as_raw_fd(),
432                level,
433                option,
434                ptr::from_ref(value).cast(),
435                mem::size_of::<libc::c_int>() as socklen_t,
436            )
437        };
438        // SAFETY: setsockopt returns exactly zero or minus one.
439        unsafe {
440            kernel::raw::Status::new(raw).try_zero(|| kernel::raw::Code::last::<Self>().into())
441        }
442    }
443
444    fn send<'a, const FLAGS: libc::c_int, E: From<Errno>>(
445        file: BorrowedFd<'_>,
446        input: &'a [u8],
447    ) -> Result<&'a [u8], E> {
448        const { assert!(FLAGS & !(libc::MSG_NOSIGNAL | libc::MSG_DONTWAIT) == 0) };
449        let raw =
450            unsafe { libc::send(file.as_raw_fd(), input.as_ptr().cast(), input.len(), FLAGS) };
451        // SAFETY: send returns minus one or a count within the supplied input.
452        unsafe {
453            io::raw::Buffer::try_native(input, raw, || kernel::raw::Code::last::<Self>().into())
454        }
455    }
456
457    fn recv<'a, const FLAGS: libc::c_int, E: From<Errno>>(
458        file: BorrowedFd<'_>,
459        output: &'a mut [u8],
460    ) -> Result<&'a mut [u8], E> {
461        const { assert!(FLAGS & !(libc::MSG_DONTWAIT | libc::MSG_PEEK | libc::MSG_WAITALL) == 0) };
462        let raw = unsafe {
463            libc::recv(
464                file.as_raw_fd(),
465                output.as_mut_ptr().cast(),
466                output.len(),
467                FLAGS,
468            )
469        };
470        // SAFETY: the admitted flags exclude MSG_TRUNC, so recv cannot report a
471        // successful count beyond the supplied output capacity.
472        unsafe {
473            io::raw::Buffer::try_native(output, raw, || kernel::raw::Code::last::<Self>().into())
474        }
475    }
476
477    fn recv_now<'a, E: From<Errno>>(
478        file: BorrowedFd<'_>,
479        output: &'a mut [u8],
480    ) -> Result<&'a mut [u8], E> {
481        Self::recv::<{ libc::MSG_DONTWAIT }, E>(file, output)
482    }
483
484    /// Sends one datagram to the retained native destination.
485    fn send_to<'a, E: From<Errno>>(
486        file: BorrowedFd<'_>,
487        input: &'a [u8],
488        address: addr::Addr<'_>,
489    ) -> Result<&'a [u8], E> {
490        let (address, len) = address.parts();
491        // SAFETY: the input and admitted address retain their complete extents.
492        let raw = unsafe {
493            libc::sendto(
494                file.as_raw_fd(),
495                input.as_ptr().cast(),
496                input.len(),
497                libc::MSG_NOSIGNAL,
498                address,
499                len.raw(),
500            )
501        };
502        // SAFETY: sendto returns -1 or a count within this exact input extent.
503        unsafe {
504            io::raw::Buffer::try_native(input, raw, || kernel::raw::Code::last::<Self>().into())
505        }
506    }
507
508    /// Receives one datagram and its source into caller-owned storage.
509    fn recv_from<'a, E: From<Errno>>(
510        file: BorrowedFd<'_>,
511        output: &'a mut [u8],
512        address: &mut addr::Storage,
513    ) -> Result<&'a mut [u8], E> {
514        let (storage, len) = address.output_mut();
515        *len = mem::size_of_val(storage) as socklen_t;
516        // SAFETY: the exclusive output and address borrows retain their full extents.
517        let raw = unsafe {
518            libc::recvfrom(
519                file.as_raw_fd(),
520                output.as_mut_ptr().cast(),
521                output.len(),
522                0,
523                ptr::from_mut(storage).cast(),
524                len,
525            )
526        };
527        // SAFETY: without MSG_TRUNC, recvfrom returns -1 or initializes at most output.len().
528        unsafe {
529            io::raw::Buffer::try_native(output, raw, || kernel::raw::Code::last::<Self>().into())
530        }
531    }
532
533    fn try_pair<E: From<Errno>>(kind: libc::c_int) -> Result<(OwnedFd, OwnedFd), E> {
534        let mut fds = [-1; 2];
535        let raw = unsafe { libc::socketpair(libc::AF_UNIX, kind, 0, fds.as_mut_ptr()) };
536        // SAFETY: socketpair just returned on this thread and `fds` is its exact destination.
537        unsafe {
538            fd::raw::Pair::new(fds).try_adopt::<E>(raw, || kernel::raw::Code::last::<Self>().into())
539        }
540    }
541}