Skip to main content

dope_core/io/socket/
mod.rs

1use std::os::fd::{self, AsFd as _};
2
3pub use addr::{
4    Addr,
5    inet::{Inet, Listener},
6    prepared::Prepared,
7    unix::Unix,
8};
9pub use backlog::Backlog;
10use dope_sys::{
11    Cloexec, Errno, Nonblock,
12    io::{
13        socket,
14        socket::{SetupOrigin, Socket as _, addr::Local},
15    },
16};
17pub use error::Error;
18use install::adopt;
19
20use crate::{
21    Backend, driver,
22    driver::{
23        life::native::{
24            request,
25            request::{bound, permits},
26        },
27        result,
28        retained::flight,
29        route,
30        route::kind,
31    },
32    io::fd::handles,
33};
34
35mod addr;
36pub(crate) mod adoption;
37mod backlog;
38mod error;
39
40pub mod install;
41pub mod msg;
42pub mod option;
43pub mod pending;
44
45#[derive(Clone, Copy, PartialEq, Eq)]
46#[repr(transparent)]
47pub struct StreamSpec(socket::Domain);
48
49impl StreamSpec {
50    pub(crate) const fn domain(self) -> socket::Domain {
51        self.0
52    }
53}
54
55#[derive(Clone, Copy, PartialEq, Eq)]
56#[repr(u8)]
57pub enum Reuse {
58    Address,
59    Port,
60}
61
62/// One bootstrap error vocabulary that can name a reuse step.
63pub(crate) trait ReuseStep: Sized {
64    /// Step attributed when `SO_REUSEADDR` fails.
65    const ADDRESS: Self;
66    /// Step attributed when `SO_REUSEPORT` fails.
67    const PORT: Self;
68}
69
70/// Exact bootstrap step this admitted reuse option performs.
71pub(crate) const fn reuse_step<S: ReuseStep>(reuse: Reuse) -> S {
72    match reuse {
73        Reuse::Address => S::ADDRESS,
74        Reuse::Port => S::PORT,
75    }
76}
77
78#[derive(Clone, Copy)]
79pub struct Config {
80    pub reuse: Option<Reuse>,
81    pub(crate) fast_open: Option<Backlog>,
82}
83
84impl Default for Config {
85    fn default() -> Self {
86        Self {
87            reuse: Some(Reuse::Address),
88            fast_open: None,
89        }
90    }
91}
92
93impl Config {
94    pub const fn for_datagram(reuse: Option<Reuse>) -> Self {
95        Self {
96            reuse,
97            fast_open: None,
98        }
99    }
100
101    /// Builds the reuse and fast-open policy for a TCP listener.
102    /// `Some(backlog)` requires Fast Open before binding and fails creation
103    /// when the platform does not support it.
104    pub fn for_tcp(reuse: Option<Reuse>, fast: Option<Backlog>) -> Self {
105        Self {
106            reuse,
107            fast_open: fast,
108        }
109    }
110
111    pub(crate) const fn fast_open(&self) -> Option<Backlog> {
112        self.fast_open
113    }
114
115    pub(crate) const fn has_addr(&self) -> bool {
116        matches!(self.reuse, Some(Reuse::Address | Reuse::Port))
117    }
118
119    pub(crate) const fn has_port(&self) -> bool {
120        matches!(self.reuse, Some(Reuse::Port))
121    }
122}
123
124const _: () = assert!(std::mem::size_of::<Option<Reuse>>() == std::mem::size_of::<bool>());
125
126#[repr(transparent)]
127pub(crate) struct Handle {
128    fd: <Backend as socket::Prepare>::Handle,
129}
130
131impl Handle {
132    pub(crate) const fn new(fd: <Backend as socket::Prepare>::Handle) -> Self {
133        Self { fd }
134    }
135
136    pub(crate) const fn owned(&self) -> &<Backend as socket::Prepare>::Handle {
137        &self.fd
138    }
139
140    pub(crate) fn into_inner(self) -> <Backend as socket::Prepare>::Handle {
141        self.fd
142    }
143
144    pub(crate) fn check_stream(&self) -> Result<(), adoption::Error> {
145        let kind = Backend::kind::<Errno>(self.fd.as_fd()).map_err(adoption::Error::Syscall)?;
146        if !kind.is_stream() {
147            return Err(adoption::Error::Role(adopt::Error::ListenerKind {
148                actual: kind.raw(),
149            }));
150        }
151        if !Backend::is_listening::<Errno>(self.fd.as_fd()).map_err(adoption::Error::Syscall)? {
152            return Err(adoption::Error::Role(adopt::Error::ListenerState {
153                actual: 0,
154            }));
155        }
156        Ok(())
157    }
158
159    pub(crate) fn check_dgram(&self) -> Result<(), adoption::Error> {
160        let kind = Backend::kind::<Errno>(self.fd.as_fd()).map_err(adoption::Error::Syscall)?;
161        if !kind.is_datagram() {
162            return Err(adoption::Error::Role(adopt::Error::DatagramKind {
163                actual: kind.raw(),
164            }));
165        }
166        Ok(())
167    }
168
169    pub(crate) fn bind(&self, addr: &Inet) -> Result<(), Errno> {
170        addr.view()
171            .native()
172            .try_bind::<Backend, Errno>(self.fd.as_fd())
173    }
174
175    pub(crate) fn listen(&self, backlog: i32) -> Result<(), Errno> {
176        Backend::listen(self.fd.as_fd(), backlog)
177    }
178
179    pub(crate) fn try_reuse(&self, config: &Config) -> Result<(), (Reuse, Errno)> {
180        if config.has_addr() {
181            Backend::set_reuse::<Errno>(self.fd.as_fd(), socket::Reuse::Address)
182                .map_err(|error| (Reuse::Address, error))?;
183        }
184        if !config.has_port() {
185            return Ok(());
186        }
187        Backend::set_reuse::<Errno>(self.fd.as_fd(), socket::Reuse::Port)
188            .map_err(|error| (Reuse::Port, error))
189    }
190
191    pub(crate) fn local_addr(&self) -> Result<Addr, Local<Errno>> {
192        Addr::local(self.fd.as_fd())
193    }
194}
195
196const _: () = {
197    assert!(size_of::<Handle>() == size_of::<fd::OwnedFd>());
198    assert!(align_of::<Handle>() == align_of::<fd::OwnedFd>());
199    assert!(size_of::<Result<(), Errno>>() == size_of::<i32>());
200    assert!(size_of::<Result<(), (Reuse, Errno)>>() == 2 * size_of::<i32>());
201};
202
203pub struct Open<'a, 'd: 'a, ID: route::Tag> {
204    raw: request::Lowered,
205    slot: handles::SocketSlot<'d>,
206    flight: flight::Vacant<'a, 'd, ID, kind::Socket>,
207    permit: permits::Admission<'a, 'd>,
208}
209
210impl<'a, 'd, ID: route::Tag + route::Select<kind::Socket>> Open<'a, 'd, ID>
211where
212    'd: 'a,
213{
214    /// A raw descriptor cannot replace the routed socket owner.
215    pub fn try_new(
216        driver: &'a mut driver::Context<'_, 'd>,
217        slots: &'a flight::Slots<'d, ID>,
218        req: route::Bound<'d, ID, handles::SocketSlot<'d>>,
219        socket: StreamSpec,
220    ) -> Result<Self, result::SubmitError>
221    where
222        'd: 'a,
223    {
224        let (target, slot) = req.into_parts();
225        let raw = request::Lowered::socket(slot.outbound(), socket);
226        let flight = slots
227            .try_vacancy(target.operation::<kind::Socket>())
228            .map_err(result::SubmitError::from)?;
229        let permit = driver
230            .native()
231            .try_admit()
232            .map_err(result::SubmitError::queue)?;
233        Ok(Open {
234            raw,
235            slot,
236            flight,
237            permit,
238        })
239    }
240
241    pub fn commit(self) -> handles::Creation<'d> {
242        let Self {
243            raw,
244            slot,
245            flight,
246            permit,
247        } = self;
248        let entry = bound::Bound::new(raw, flight);
249        let flight = permit.submit(entry);
250        slot.begin(flight)
251    }
252}
253
254#[derive(Clone, Copy, Debug)]
255pub enum BindError {
256    Socket(crate::Errno),
257    DescriptorFlags(crate::Errno),
258    CloseOnExec(crate::Errno),
259    Bind(crate::Errno),
260    Listen(crate::Errno),
261    LocalAddress(Local<Errno>),
262    Address(Error),
263}
264
265impl BindError {
266    fn socket(error: impl Into<socket::Failure>) -> Self {
267        match error.into() {
268            socket::Failure::Socket(error) => Self::Socket(error),
269            socket::Failure::Cloexec(Cloexec::Get(error)) => Self::DescriptorFlags(error),
270            socket::Failure::Cloexec(Cloexec::Set(error)) => Self::CloseOnExec(error),
271        }
272    }
273}
274
275/// The exact step that failed while creating a socket descriptor.
276#[derive(Clone, Copy, Debug, PartialEq, Eq)]
277#[repr(u8)]
278pub enum CreationStep {
279    Socket,
280    DescriptorFlags,
281    CloseOnExec,
282    StatusFlags,
283    Nonblocking,
284    Sigpipe,
285}
286
287impl CreationStep {
288    /// Exact step and native fault of one failed descriptor creation.
289    pub(crate) const fn from_origin(origin: socket::Origin) -> (Self, crate::Errno) {
290        match origin {
291            socket::Origin::Socket(fault) => (Self::Socket, fault),
292            socket::Origin::Setup(setup) => match setup.origin() {
293                SetupOrigin::Cloexec(Cloexec::Get(error)) => (Self::DescriptorFlags, error),
294                SetupOrigin::Cloexec(Cloexec::Set(error)) => (Self::CloseOnExec, error),
295                SetupOrigin::Nonblock(Nonblock::Get(error)) => (Self::StatusFlags, error),
296                SetupOrigin::Nonblock(Nonblock::Set(error)) => (Self::Nonblocking, error),
297                SetupOrigin::Sigpipe(error) => (Self::Sigpipe, error),
298            },
299        }
300    }
301}
302
303#[derive(Clone, Copy, Debug, PartialEq, Eq)]
304pub struct Create {
305    fault: crate::Errno,
306    step: CreationStep,
307}
308
309impl Create {
310    pub const fn errno(self) -> crate::Errno {
311        self.fault
312    }
313    pub const fn step(self) -> CreationStep {
314        self.step
315    }
316
317    pub(crate) fn from_native(error: socket::Create) -> Self {
318        let (step, fault) = CreationStep::from_origin(error.origin());
319        Self { step, fault }
320    }
321}