Skip to main content

agentos_kernel/
socket_table.rs

1use crate::poll::{PollEvents, POLLERR, POLLHUP, POLLIN, POLLOUT};
2use crate::vfs::normalize_path;
3use std::collections::{BTreeMap, BTreeSet, VecDeque};
4use std::error::Error;
5use std::fmt;
6use std::net::{Ipv4Addr, Ipv6Addr};
7use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
8use std::sync::{Arc, Mutex, MutexGuard};
9use std::time::Instant;
10
11pub type SocketId = u64;
12pub type SocketResult<T> = Result<T, SocketTableError>;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum SocketReadinessKind {
16    Data,
17    Accept,
18}
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub struct SocketReadiness {
22    pub socket_id: SocketId,
23    pub kind: SocketReadinessKind,
24}
25
26type SocketReadinessSink = Arc<dyn Fn(SocketReadiness) + Send + Sync + 'static>;
27
28#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
29pub struct SocketReadTraceSnapshot {
30    pub socket_record_clone_calls: u64,
31    pub socket_record_clone_us: u64,
32    pub read_recv_calls: u64,
33    pub read_recv_bytes: u64,
34    pub read_recv_chunks: u64,
35    pub read_recv_copy_us: u64,
36}
37
38struct SocketReadTraceCounters {
39    socket_record_clone_calls: AtomicU64,
40    socket_record_clone_us: AtomicU64,
41    read_recv_calls: AtomicU64,
42    read_recv_bytes: AtomicU64,
43    read_recv_chunks: AtomicU64,
44    read_recv_copy_us: AtomicU64,
45}
46
47impl SocketReadTraceCounters {
48    const fn new() -> Self {
49        Self {
50            socket_record_clone_calls: AtomicU64::new(0),
51            socket_record_clone_us: AtomicU64::new(0),
52            read_recv_calls: AtomicU64::new(0),
53            read_recv_bytes: AtomicU64::new(0),
54            read_recv_chunks: AtomicU64::new(0),
55            read_recv_copy_us: AtomicU64::new(0),
56        }
57    }
58}
59
60static SOCKET_READ_TRACE_ENABLED: AtomicBool = AtomicBool::new(false);
61static SOCKET_READ_TRACE_COUNTERS: SocketReadTraceCounters = SocketReadTraceCounters::new();
62
63pub fn set_socket_read_trace_enabled(enabled: bool) {
64    SOCKET_READ_TRACE_ENABLED.store(enabled, Ordering::Relaxed);
65}
66
67pub fn reset_socket_read_trace() {
68    for counter in [
69        &SOCKET_READ_TRACE_COUNTERS.socket_record_clone_calls,
70        &SOCKET_READ_TRACE_COUNTERS.socket_record_clone_us,
71        &SOCKET_READ_TRACE_COUNTERS.read_recv_calls,
72        &SOCKET_READ_TRACE_COUNTERS.read_recv_bytes,
73        &SOCKET_READ_TRACE_COUNTERS.read_recv_chunks,
74        &SOCKET_READ_TRACE_COUNTERS.read_recv_copy_us,
75    ] {
76        counter.store(0, Ordering::Relaxed);
77    }
78}
79
80pub fn socket_read_trace_snapshot() -> SocketReadTraceSnapshot {
81    SocketReadTraceSnapshot {
82        socket_record_clone_calls: SOCKET_READ_TRACE_COUNTERS
83            .socket_record_clone_calls
84            .load(Ordering::Relaxed),
85        socket_record_clone_us: SOCKET_READ_TRACE_COUNTERS
86            .socket_record_clone_us
87            .load(Ordering::Relaxed),
88        read_recv_calls: SOCKET_READ_TRACE_COUNTERS
89            .read_recv_calls
90            .load(Ordering::Relaxed),
91        read_recv_bytes: SOCKET_READ_TRACE_COUNTERS
92            .read_recv_bytes
93            .load(Ordering::Relaxed),
94        read_recv_chunks: SOCKET_READ_TRACE_COUNTERS
95            .read_recv_chunks
96            .load(Ordering::Relaxed),
97        read_recv_copy_us: SOCKET_READ_TRACE_COUNTERS
98            .read_recv_copy_us
99            .load(Ordering::Relaxed),
100    }
101}
102
103#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
104pub struct InetSocketAddress {
105    host: String,
106    port: u16,
107}
108
109impl InetSocketAddress {
110    pub fn new(host: impl Into<String>, port: u16) -> Self {
111        Self {
112            host: host.into(),
113            port,
114        }
115    }
116
117    pub fn host(&self) -> &str {
118        &self.host
119    }
120
121    pub const fn port(&self) -> u16 {
122        self.port
123    }
124}
125
126#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
127pub enum SocketDomain {
128    Inet,
129    Inet6,
130    Unix,
131}
132
133#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
134pub enum SocketType {
135    Stream,
136    Datagram,
137}
138
139#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
140pub enum SocketState {
141    Created,
142    Bound,
143    Listening,
144    Connected,
145}
146
147impl SocketState {
148    pub const fn counts_as_listener(self) -> bool {
149        matches!(self, Self::Listening)
150    }
151
152    pub const fn counts_as_connection(self) -> bool {
153        matches!(self, Self::Connected)
154    }
155}
156
157#[derive(Debug, Clone, Copy, PartialEq, Eq)]
158pub enum SocketShutdown {
159    Read,
160    Write,
161    Both,
162}
163
164#[derive(Debug, Clone, Copy, PartialEq, Eq)]
165pub enum DatagramSocketOption {
166    ReuseAddr,
167    ReusePort,
168    Broadcast,
169}
170
171#[derive(Debug, Clone, Copy, PartialEq, Eq)]
172pub struct SocketSpec {
173    pub domain: SocketDomain,
174    pub socket_type: SocketType,
175}
176
177impl SocketSpec {
178    pub const fn new(domain: SocketDomain, socket_type: SocketType) -> Self {
179        Self {
180            domain,
181            socket_type,
182        }
183    }
184
185    pub const fn tcp() -> Self {
186        Self::new(SocketDomain::Inet, SocketType::Stream)
187    }
188
189    pub const fn udp() -> Self {
190        Self::new(SocketDomain::Inet, SocketType::Datagram)
191    }
192
193    pub const fn unix_stream() -> Self {
194        Self::new(SocketDomain::Unix, SocketType::Stream)
195    }
196
197    pub const fn unix_datagram() -> Self {
198        Self::new(SocketDomain::Unix, SocketType::Datagram)
199    }
200}
201
202#[derive(Debug, Clone, PartialEq, Eq)]
203pub struct SocketRecord {
204    id: SocketId,
205    owner_pid: u32,
206    spec: SocketSpec,
207    state: SocketState,
208    local_address: Option<InetSocketAddress>,
209    peer_address: Option<InetSocketAddress>,
210    local_unix_path: Option<String>,
211    peer_unix_path: Option<String>,
212    listener_state: Option<ListenerState>,
213    connection_state: Option<ConnectionState>,
214    datagram_state: Option<DatagramState>,
215}
216
217impl SocketRecord {
218    pub const fn id(&self) -> SocketId {
219        self.id
220    }
221
222    pub const fn owner_pid(&self) -> u32 {
223        self.owner_pid
224    }
225
226    pub const fn spec(&self) -> SocketSpec {
227        self.spec
228    }
229
230    pub const fn state(&self) -> SocketState {
231        self.state
232    }
233
234    pub fn local_address(&self) -> Option<&InetSocketAddress> {
235        self.local_address.as_ref()
236    }
237
238    pub fn peer_address(&self) -> Option<&InetSocketAddress> {
239        self.peer_address.as_ref()
240    }
241
242    pub fn local_unix_path(&self) -> Option<&str> {
243        self.local_unix_path.as_deref()
244    }
245
246    pub fn peer_unix_path(&self) -> Option<&str> {
247        self.peer_unix_path.as_deref()
248    }
249
250    pub fn listen_backlog(&self) -> Option<usize> {
251        self.listener_state.as_ref().map(|state| state.backlog)
252    }
253
254    pub fn pending_accept_count(&self) -> usize {
255        self.listener_state
256            .as_ref()
257            .map(|state| state.pending_accepts.len())
258            .unwrap_or(0)
259    }
260
261    pub fn peer_socket_id(&self) -> Option<SocketId> {
262        self.connection_state
263            .as_ref()
264            .and_then(|state| state.peer_socket_id)
265    }
266
267    pub fn buffered_read_bytes(&self) -> usize {
268        self.connection_state
269            .as_ref()
270            .map(ConnectionState::buffered_len)
271            .unwrap_or(0)
272    }
273
274    pub fn read_shutdown(&self) -> bool {
275        self.connection_state
276            .as_ref()
277            .map(|state| state.read_shutdown)
278            .unwrap_or(false)
279    }
280
281    pub fn write_shutdown(&self) -> bool {
282        self.connection_state
283            .as_ref()
284            .map(|state| state.write_shutdown)
285            .unwrap_or(false)
286    }
287
288    pub fn peer_write_shutdown(&self) -> bool {
289        self.connection_state
290            .as_ref()
291            .map(|state| state.peer_write_shutdown)
292            .unwrap_or(false)
293    }
294
295    pub fn queued_datagrams(&self) -> usize {
296        self.datagram_state
297            .as_ref()
298            .map(|state| state.recv_queue.len())
299            .unwrap_or(0)
300    }
301
302    pub fn queued_datagram_bytes(&self) -> usize {
303        self.datagram_state
304            .as_ref()
305            .map(|state| datagram_queue_bytes(&state.recv_queue))
306            .unwrap_or(0)
307    }
308
309    pub fn reuse_address(&self) -> bool {
310        self.datagram_state
311            .as_ref()
312            .map(|state| state.reuse_addr)
313            .unwrap_or(false)
314    }
315
316    pub fn reuse_port(&self) -> bool {
317        self.datagram_state
318            .as_ref()
319            .map(|state| state.reuse_port)
320            .unwrap_or(false)
321    }
322
323    pub fn broadcast_enabled(&self) -> bool {
324        self.datagram_state
325            .as_ref()
326            .map(|state| state.broadcast)
327            .unwrap_or(false)
328    }
329
330    pub fn multicast_membership_count(&self) -> usize {
331        self.datagram_state
332            .as_ref()
333            .map(|state| state.multicast_memberships.len())
334            .unwrap_or(0)
335    }
336
337    pub fn has_multicast_membership(&self, membership: &SocketMulticastMembership) -> bool {
338        self.datagram_state
339            .as_ref()
340            .map(|state| state.multicast_memberships.contains(membership))
341            .unwrap_or(false)
342    }
343}
344
345#[derive(Debug, Clone, PartialEq, Eq)]
346pub struct ReceivedDatagram {
347    source_address: Option<InetSocketAddress>,
348    payload: Vec<u8>,
349}
350
351impl ReceivedDatagram {
352    pub fn source_address(&self) -> Option<&InetSocketAddress> {
353        self.source_address.as_ref()
354    }
355
356    pub fn payload(&self) -> &[u8] {
357        &self.payload
358    }
359
360    pub fn into_parts(self) -> (Option<InetSocketAddress>, Vec<u8>) {
361        (self.source_address, self.payload)
362    }
363}
364
365#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
366pub struct SocketTableSnapshot {
367    pub sockets: usize,
368    pub listeners: usize,
369    pub connections: usize,
370    pub buffered_bytes: usize,
371    pub datagram_queue_len: usize,
372}
373
374#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
375pub struct SocketMulticastMembership {
376    group_address: String,
377    interface_address: Option<String>,
378}
379
380impl SocketMulticastMembership {
381    pub fn new(group_address: impl Into<String>, interface_address: Option<String>) -> Self {
382        Self {
383            group_address: group_address.into(),
384            interface_address,
385        }
386    }
387
388    pub fn group_address(&self) -> &str {
389        &self.group_address
390    }
391
392    pub fn interface_address(&self) -> Option<&str> {
393        self.interface_address.as_deref()
394    }
395}
396
397#[derive(Debug, Clone, PartialEq, Eq)]
398pub struct SocketTableError {
399    code: &'static str,
400    message: String,
401}
402
403impl SocketTableError {
404    pub fn code(&self) -> &'static str {
405        self.code
406    }
407
408    fn not_found(socket_id: SocketId) -> Self {
409        Self {
410            code: "ENOENT",
411            message: format!("no such socket {socket_id}"),
412        }
413    }
414
415    fn invalid_argument(message: impl Into<String>) -> Self {
416        Self {
417            code: "EINVAL",
418            message: message.into(),
419        }
420    }
421
422    fn address_in_use(message: impl Into<String>) -> Self {
423        Self {
424            code: "EADDRINUSE",
425            message: message.into(),
426        }
427    }
428
429    fn address_not_available(message: impl Into<String>) -> Self {
430        Self {
431            code: "EADDRNOTAVAIL",
432            message: message.into(),
433        }
434    }
435
436    fn not_found_address(message: impl Into<String>) -> Self {
437        Self {
438            code: "ECONNREFUSED",
439            message: message.into(),
440        }
441    }
442
443    fn would_block(message: impl Into<String>) -> Self {
444        Self {
445            code: "EAGAIN",
446            message: message.into(),
447        }
448    }
449
450    fn not_connected(message: impl Into<String>) -> Self {
451        Self {
452            code: "ENOTCONN",
453            message: message.into(),
454        }
455    }
456
457    fn broken_pipe(message: impl Into<String>) -> Self {
458        Self {
459            code: "EPIPE",
460            message: message.into(),
461        }
462    }
463}
464
465impl fmt::Display for SocketTableError {
466    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
467        write!(f, "{}: {}", self.code, self.message)
468    }
469}
470
471impl Error for SocketTableError {}
472
473#[derive(Debug, Default)]
474struct SocketTableState {
475    sockets: BTreeMap<SocketId, SocketRecord>,
476    by_owner: BTreeMap<u32, BTreeSet<SocketId>>,
477    bound_inet_streams: BTreeMap<InetSocketAddress, SocketId>,
478    bound_inet_datagrams: BTreeMap<InetSocketAddress, BTreeSet<SocketId>>,
479    bound_unix_streams: BTreeMap<String, SocketId>,
480    multicast_groups: BTreeMap<SocketMulticastMembership, BTreeSet<SocketId>>,
481    next_socket_id: SocketId,
482}
483
484#[derive(Debug, Clone, PartialEq, Eq)]
485struct ListenerState {
486    backlog: usize,
487    pending_accepts: VecDeque<PendingConnection>,
488}
489
490#[derive(Debug, Clone, PartialEq, Eq, Default)]
491struct ConnectionState {
492    peer_socket_id: Option<SocketId>,
493    recv_buffer: VecDeque<Vec<u8>>,
494    recv_buffer_len: usize,
495    read_shutdown: bool,
496    write_shutdown: bool,
497    peer_write_shutdown: bool,
498}
499
500impl ConnectionState {
501    fn buffered_len(&self) -> usize {
502        self.recv_buffer_len
503    }
504
505    fn has_buffered_data(&self) -> bool {
506        self.recv_buffer_len > 0
507    }
508
509    fn push_recv(&mut self, data: &[u8]) {
510        if data.is_empty() {
511            return;
512        }
513        self.recv_buffer.push_back(data.to_vec());
514        self.recv_buffer_len = self.recv_buffer_len.saturating_add(data.len());
515    }
516
517    fn read_recv(&mut self, max_bytes: usize) -> Option<Vec<u8>> {
518        if max_bytes == 0 {
519            return Some(Vec::new());
520        }
521        if self.recv_buffer_len == 0 {
522            return None;
523        }
524
525        let read_len = self.recv_buffer_len.min(max_bytes);
526        self.recv_buffer_len -= read_len;
527
528        let mut remaining = read_len;
529        let mut chunks = 0usize;
530        let trace_started = SOCKET_READ_TRACE_ENABLED
531            .load(Ordering::Relaxed)
532            .then(Instant::now);
533        let mut out = Vec::with_capacity(read_len);
534        while remaining > 0 {
535            let mut chunk = self.recv_buffer.pop_front()?;
536            chunks += 1;
537            if chunk.len() <= remaining {
538                remaining -= chunk.len();
539                out.extend_from_slice(&chunk);
540                continue;
541            }
542
543            let tail = chunk.split_off(remaining);
544            out.extend_from_slice(&chunk);
545            self.recv_buffer.push_front(tail);
546            remaining = 0;
547        }
548        if let Some(started) = trace_started {
549            SOCKET_READ_TRACE_COUNTERS
550                .read_recv_calls
551                .fetch_add(1, Ordering::Relaxed);
552            SOCKET_READ_TRACE_COUNTERS.read_recv_bytes.fetch_add(
553                u64::try_from(read_len).unwrap_or(u64::MAX),
554                Ordering::Relaxed,
555            );
556            SOCKET_READ_TRACE_COUNTERS
557                .read_recv_chunks
558                .fetch_add(u64::try_from(chunks).unwrap_or(u64::MAX), Ordering::Relaxed);
559            SOCKET_READ_TRACE_COUNTERS.read_recv_copy_us.fetch_add(
560                u64::try_from(started.elapsed().as_micros()).unwrap_or(u64::MAX),
561                Ordering::Relaxed,
562            );
563        }
564        Some(out)
565    }
566
567    fn clear_recv(&mut self) {
568        self.recv_buffer.clear();
569        self.recv_buffer_len = 0;
570    }
571}
572
573#[derive(Debug, Clone, PartialEq, Eq)]
574struct PendingConnection {
575    peer_address: Option<InetSocketAddress>,
576    peer_unix_path: Option<String>,
577    accepted_socket_id: Option<SocketId>,
578}
579
580#[derive(Debug, Clone, PartialEq, Eq, Default)]
581struct DatagramState {
582    recv_queue: VecDeque<QueuedDatagram>,
583    reuse_addr: bool,
584    reuse_port: bool,
585    broadcast: bool,
586    multicast_memberships: BTreeSet<SocketMulticastMembership>,
587}
588
589#[derive(Debug, Clone, PartialEq, Eq)]
590struct QueuedDatagram {
591    source_address: Option<InetSocketAddress>,
592    payload: Vec<u8>,
593}
594
595struct SocketTableInner {
596    state: Mutex<SocketTableState>,
597    readiness_sink: Mutex<Option<SocketReadinessSink>>,
598}
599
600impl Default for SocketTableInner {
601    fn default() -> Self {
602        Self {
603            state: Mutex::new(SocketTableState::default()),
604            readiness_sink: Mutex::new(None),
605        }
606    }
607}
608
609impl fmt::Debug for SocketTableInner {
610    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
611        f.debug_struct("SocketTableInner")
612            .field("state", &self.state)
613            .finish_non_exhaustive()
614    }
615}
616
617#[derive(Debug, Clone, Default)]
618pub struct SocketTable {
619    inner: Arc<SocketTableInner>,
620}
621
622impl SocketTable {
623    pub fn new() -> Self {
624        Self::default()
625    }
626
627    pub fn set_readiness_sink<F>(&self, sink: Option<F>)
628    where
629        F: Fn(SocketReadiness) + Send + Sync + 'static,
630    {
631        let mut target = lock_or_recover(&self.inner.readiness_sink);
632        *target = sink.map(|sink| Arc::new(sink) as SocketReadinessSink);
633    }
634
635    fn emit_readiness(&self, readiness: Option<SocketReadiness>) {
636        let Some(readiness) = readiness else {
637            return;
638        };
639        let sink = lock_or_recover(&self.inner.readiness_sink).clone();
640        if let Some(sink) = sink {
641            sink(readiness);
642        }
643    }
644
645    pub fn allocate(&self, owner_pid: u32, spec: SocketSpec) -> SocketRecord {
646        self.allocate_with_state(owner_pid, spec, SocketState::Created)
647    }
648
649    pub fn allocate_with_state(
650        &self,
651        owner_pid: u32,
652        spec: SocketSpec,
653        state: SocketState,
654    ) -> SocketRecord {
655        let mut table = lock_or_recover(&self.inner.state);
656        let socket_id = next_socket_id(&mut table);
657        let record = SocketRecord {
658            id: socket_id,
659            owner_pid,
660            spec,
661            state,
662            local_address: None,
663            peer_address: None,
664            local_unix_path: None,
665            peer_unix_path: None,
666            listener_state: None,
667            connection_state: default_connection_state(spec, state),
668            datagram_state: default_datagram_state(spec),
669        };
670        table.sockets.insert(socket_id, record.clone());
671        table
672            .by_owner
673            .entry(owner_pid)
674            .or_default()
675            .insert(socket_id);
676        record
677    }
678
679    pub fn get(&self, socket_id: SocketId) -> Option<SocketRecord> {
680        lock_or_recover(&self.inner.state)
681            .sockets
682            .get(&socket_id)
683            .cloned()
684    }
685
686    pub fn records_for_owner(&self, owner_pid: u32) -> Vec<SocketRecord> {
687        let table = lock_or_recover(&self.inner.state);
688        let Some(socket_ids) = table.by_owner.get(&owner_pid) else {
689            return Vec::new();
690        };
691        socket_ids
692            .iter()
693            .filter_map(|socket_id| table.sockets.get(socket_id).cloned())
694            .collect()
695    }
696
697    pub fn update_state(
698        &self,
699        socket_id: SocketId,
700        new_state: SocketState,
701    ) -> SocketResult<SocketRecord> {
702        let mut table = lock_or_recover(&self.inner.state);
703        let record = table
704            .sockets
705            .get_mut(&socket_id)
706            .ok_or_else(|| SocketTableError::not_found(socket_id))?;
707        validate_state_transition(record.state, new_state)?;
708        record.state = new_state;
709        if new_state != SocketState::Listening {
710            record.listener_state = None;
711        }
712        if new_state == SocketState::Connected && supports_connection_lifecycle(record.spec) {
713            record
714                .connection_state
715                .get_or_insert_with(ConnectionState::default);
716        } else if new_state != SocketState::Connected {
717            record.connection_state = None;
718        }
719        Ok(record.clone())
720    }
721
722    pub fn bind_inet(
723        &self,
724        socket_id: SocketId,
725        address: InetSocketAddress,
726    ) -> SocketResult<SocketRecord> {
727        let mut address = normalize_inet_address(address);
728        let mut table = lock_or_recover(&self.inner.state);
729        let existing = table
730            .sockets
731            .get(&socket_id)
732            .cloned()
733            .ok_or_else(|| SocketTableError::not_found(socket_id))?;
734        if !supports_inet_bind(existing.spec) {
735            return Err(SocketTableError::invalid_argument(format!(
736                "socket {socket_id} is not an INET socket"
737            )));
738        }
739        // POSIX bind(port=0) assigns a free ephemeral port (kernel-owned, so the
740        // converged browser path does not need a host port allocator).
741        if address.port() == 0 {
742            let port = assign_ephemeral_inet_port(&table, existing.spec, address.host())?;
743            address = normalize_inet_address(InetSocketAddress::new(address.host(), port));
744        }
745        let conflicting_ids =
746            lookup_conflicting_bound_inet_socket_ids(&table, existing.spec, &address);
747        if has_incompatible_inet_bind_conflict(&table, &existing, &conflicting_ids) {
748            return Err(SocketTableError::address_in_use(format!(
749                "address {}:{} is already bound",
750                address.host(),
751                address.port()
752            )));
753        }
754        let cloned = {
755            let record = table
756                .sockets
757                .get_mut(&socket_id)
758                .ok_or_else(|| SocketTableError::not_found(socket_id))?;
759
760            match record.state {
761                SocketState::Created => {}
762                SocketState::Bound if record.local_address.as_ref() == Some(&address) => {
763                    return Ok(record.clone());
764                }
765                SocketState::Bound | SocketState::Listening | SocketState::Connected => {
766                    return Err(SocketTableError::invalid_argument(format!(
767                        "socket {socket_id} cannot bind in state {:?}",
768                        record.state
769                    )));
770                }
771            }
772
773            record.local_address = Some(address.clone());
774            record.peer_address = None;
775            record.local_unix_path = None;
776            record.peer_unix_path = None;
777            record.listener_state = None;
778            record.connection_state = None;
779            record.state = SocketState::Bound;
780            record.clone()
781        };
782        register_bound_inet_socket(&mut table, cloned.spec, address, socket_id);
783        Ok(cloned)
784    }
785
786    pub fn set_datagram_socket_option(
787        &self,
788        socket_id: SocketId,
789        option: DatagramSocketOption,
790        enabled: bool,
791    ) -> SocketResult<SocketRecord> {
792        let mut table = lock_or_recover(&self.inner.state);
793        let record = table
794            .sockets
795            .get_mut(&socket_id)
796            .ok_or_else(|| SocketTableError::not_found(socket_id))?;
797        let datagram_state = datagram_state_mut(record)?;
798
799        match option {
800            DatagramSocketOption::ReuseAddr => datagram_state.reuse_addr = enabled,
801            DatagramSocketOption::ReusePort => datagram_state.reuse_port = enabled,
802            DatagramSocketOption::Broadcast => datagram_state.broadcast = enabled,
803        }
804
805        Ok(record.clone())
806    }
807
808    pub fn add_multicast_membership(
809        &self,
810        socket_id: SocketId,
811        membership: SocketMulticastMembership,
812    ) -> SocketResult<SocketRecord> {
813        let mut table = lock_or_recover(&self.inner.state);
814        let normalized_membership = {
815            let record = table
816                .sockets
817                .get(&socket_id)
818                .ok_or_else(|| SocketTableError::not_found(socket_id))?;
819            validate_multicast_socket(record)?;
820            normalize_multicast_membership(record.spec, membership)?
821        };
822
823        let cloned = {
824            let record = table
825                .sockets
826                .get_mut(&socket_id)
827                .ok_or_else(|| SocketTableError::not_found(socket_id))?;
828            let datagram_state = datagram_state_mut(record)?;
829            datagram_state
830                .multicast_memberships
831                .insert(normalized_membership.clone());
832            record.clone()
833        };
834
835        table
836            .multicast_groups
837            .entry(normalized_membership)
838            .or_default()
839            .insert(socket_id);
840        Ok(cloned)
841    }
842
843    pub fn drop_multicast_membership(
844        &self,
845        socket_id: SocketId,
846        membership: SocketMulticastMembership,
847    ) -> SocketResult<SocketRecord> {
848        let mut table = lock_or_recover(&self.inner.state);
849        let normalized_membership = {
850            let record = table
851                .sockets
852                .get(&socket_id)
853                .ok_or_else(|| SocketTableError::not_found(socket_id))?;
854            validate_multicast_socket(record)?;
855            normalize_multicast_membership(record.spec, membership)?
856        };
857
858        let cloned = {
859            let record = table
860                .sockets
861                .get_mut(&socket_id)
862                .ok_or_else(|| SocketTableError::not_found(socket_id))?;
863            let datagram_state = datagram_state_mut(record)?;
864            if !datagram_state
865                .multicast_memberships
866                .remove(&normalized_membership)
867            {
868                return Err(SocketTableError::address_not_available(format!(
869                    "socket {socket_id} has not joined multicast group {}",
870                    normalized_membership.group_address()
871                )));
872            }
873            record.clone()
874        };
875
876        if let Some(members) = table.multicast_groups.get_mut(&normalized_membership) {
877            members.remove(&socket_id);
878            if members.is_empty() {
879                table.multicast_groups.remove(&normalized_membership);
880            }
881        }
882
883        Ok(cloned)
884    }
885
886    pub fn bind_unix(
887        &self,
888        socket_id: SocketId,
889        path: impl Into<String>,
890    ) -> SocketResult<SocketRecord> {
891        let path = normalize_unix_socket_path(path.into())?;
892        let mut table = lock_or_recover(&self.inner.state);
893        let existing = table
894            .sockets
895            .get(&socket_id)
896            .cloned()
897            .ok_or_else(|| SocketTableError::not_found(socket_id))?;
898        if !supports_unix_stream_lifecycle(existing.spec) {
899            return Err(SocketTableError::invalid_argument(format!(
900                "socket {socket_id} is not a Unix stream socket"
901            )));
902        }
903        let existing_id = table.bound_unix_streams.get(&path).copied();
904        let cloned = {
905            let record = table
906                .sockets
907                .get_mut(&socket_id)
908                .ok_or_else(|| SocketTableError::not_found(socket_id))?;
909
910            if let Some(bound_socket_id) = existing_id {
911                if bound_socket_id != socket_id {
912                    return Err(SocketTableError::address_in_use(format!(
913                        "path {path} is already bound"
914                    )));
915                }
916            }
917
918            match record.state {
919                SocketState::Created => {}
920                SocketState::Bound if record.local_unix_path.as_deref() == Some(path.as_str()) => {
921                    return Ok(record.clone());
922                }
923                SocketState::Bound | SocketState::Listening | SocketState::Connected => {
924                    return Err(SocketTableError::invalid_argument(format!(
925                        "socket {socket_id} cannot bind in state {:?}",
926                        record.state
927                    )));
928                }
929            }
930
931            record.local_address = None;
932            record.peer_address = None;
933            record.local_unix_path = Some(path.clone());
934            record.peer_unix_path = None;
935            record.listener_state = None;
936            record.connection_state = None;
937            record.state = SocketState::Bound;
938            record.clone()
939        };
940        table.bound_unix_streams.insert(path, socket_id);
941        Ok(cloned)
942    }
943
944    pub fn listen(&self, socket_id: SocketId, backlog: usize) -> SocketResult<SocketRecord> {
945        if backlog == 0 {
946            return Err(SocketTableError::invalid_argument(
947                "listener backlog must be greater than zero",
948            ));
949        }
950
951        let mut table = lock_or_recover(&self.inner.state);
952        let record = table
953            .sockets
954            .get_mut(&socket_id)
955            .ok_or_else(|| SocketTableError::not_found(socket_id))?;
956
957        if !supports_listener_lifecycle(record.spec) {
958            return Err(SocketTableError::invalid_argument(format!(
959                "socket {socket_id} is not a stream socket"
960            )));
961        }
962        if record.state != SocketState::Bound || !has_bound_endpoint(record) {
963            return Err(SocketTableError::invalid_argument(format!(
964                "socket {socket_id} must be bound before listen"
965            )));
966        }
967
968        record.state = SocketState::Listening;
969        record.listener_state = Some(ListenerState {
970            backlog,
971            pending_accepts: VecDeque::new(),
972        });
973        Ok(record.clone())
974    }
975
976    pub fn enqueue_incoming_tcp_connection(
977        &self,
978        listener_socket_id: SocketId,
979        peer_address: InetSocketAddress,
980    ) -> SocketResult<()> {
981        let readiness = {
982            let mut table = lock_or_recover(&self.inner.state);
983            let record = table
984                .sockets
985                .get_mut(&listener_socket_id)
986                .ok_or_else(|| SocketTableError::not_found(listener_socket_id))?;
987
988            if record.state != SocketState::Listening {
989                return Err(SocketTableError::invalid_argument(format!(
990                    "socket {listener_socket_id} is not listening"
991                )));
992            }
993
994            let listener_state = record.listener_state.as_mut().ok_or_else(|| {
995                SocketTableError::invalid_argument(format!(
996                    "socket {listener_socket_id} has no listener state"
997                ))
998            })?;
999
1000            if listener_state.pending_accepts.len() >= listener_state.backlog {
1001                return Err(SocketTableError::would_block(format!(
1002                    "listener {listener_socket_id} backlog is full"
1003                )));
1004            }
1005
1006            let was_empty = listener_state.pending_accepts.is_empty();
1007            listener_state.pending_accepts.push_back(PendingConnection {
1008                peer_address: Some(peer_address),
1009                peer_unix_path: None,
1010                accepted_socket_id: None,
1011            });
1012            was_empty.then_some(SocketReadiness {
1013                socket_id: listener_socket_id,
1014                kind: SocketReadinessKind::Accept,
1015            })
1016        };
1017        self.emit_readiness(readiness);
1018        Ok(())
1019    }
1020
1021    pub fn accept(&self, listener_socket_id: SocketId) -> SocketResult<SocketRecord> {
1022        let mut table = lock_or_recover(&self.inner.state);
1023        let (owner_pid, spec, local_address, local_unix_path, pending) = {
1024            let record = table
1025                .sockets
1026                .get_mut(&listener_socket_id)
1027                .ok_or_else(|| SocketTableError::not_found(listener_socket_id))?;
1028
1029            if record.state != SocketState::Listening {
1030                return Err(SocketTableError::invalid_argument(format!(
1031                    "socket {listener_socket_id} is not listening"
1032                )));
1033            }
1034
1035            let listener_state = record.listener_state.as_mut().ok_or_else(|| {
1036                SocketTableError::invalid_argument(format!(
1037                    "socket {listener_socket_id} has no listener state"
1038                ))
1039            })?;
1040            let pending = listener_state.pending_accepts.pop_front().ok_or_else(|| {
1041                SocketTableError::would_block(format!(
1042                    "listener {listener_socket_id} has no pending connections"
1043                ))
1044            })?;
1045
1046            (
1047                record.owner_pid,
1048                record.spec,
1049                record.local_address.clone(),
1050                record.local_unix_path.clone(),
1051                pending,
1052            )
1053        };
1054
1055        if let Some(accepted_socket_id) = pending.accepted_socket_id {
1056            return table
1057                .sockets
1058                .get(&accepted_socket_id)
1059                .cloned()
1060                .ok_or_else(|| SocketTableError::not_found(accepted_socket_id));
1061        }
1062
1063        let socket_id = next_socket_id(&mut table);
1064        let record = SocketRecord {
1065            id: socket_id,
1066            owner_pid,
1067            spec,
1068            state: SocketState::Connected,
1069            local_address,
1070            peer_address: pending.peer_address,
1071            local_unix_path,
1072            peer_unix_path: pending.peer_unix_path,
1073            listener_state: None,
1074            connection_state: default_connection_state(spec, SocketState::Connected),
1075            datagram_state: default_datagram_state(spec),
1076        };
1077        table.sockets.insert(socket_id, record.clone());
1078        table
1079            .by_owner
1080            .entry(owner_pid)
1081            .or_default()
1082            .insert(socket_id);
1083        Ok(record)
1084    }
1085
1086    pub fn connect_pair(
1087        &self,
1088        socket_id: SocketId,
1089        peer_socket_id: SocketId,
1090    ) -> SocketResult<(SocketRecord, SocketRecord)> {
1091        if socket_id == peer_socket_id {
1092            return Err(SocketTableError::invalid_argument(
1093                "socket cannot connect to itself",
1094            ));
1095        }
1096
1097        let mut table = lock_or_recover(&self.inner.state);
1098        let mut socket = table
1099            .sockets
1100            .remove(&socket_id)
1101            .ok_or_else(|| SocketTableError::not_found(socket_id))?;
1102        let Some(mut peer) = table.sockets.remove(&peer_socket_id) else {
1103            table.sockets.insert(socket_id, socket);
1104            return Err(SocketTableError::not_found(peer_socket_id));
1105        };
1106
1107        if let Err(error) = validate_connect_pair(&socket, &peer) {
1108            table.sockets.insert(socket_id, socket);
1109            table.sockets.insert(peer_socket_id, peer);
1110            return Err(error);
1111        }
1112
1113        socket.state = SocketState::Connected;
1114        socket.peer_address = peer.local_address.clone();
1115        socket.peer_unix_path = peer.local_unix_path.clone();
1116        socket.listener_state = None;
1117        socket.connection_state = Some(ConnectionState {
1118            peer_socket_id: Some(peer_socket_id),
1119            ..ConnectionState::default()
1120        });
1121
1122        peer.state = SocketState::Connected;
1123        peer.peer_address = socket.local_address.clone();
1124        peer.peer_unix_path = socket.local_unix_path.clone();
1125        peer.listener_state = None;
1126        peer.connection_state = Some(ConnectionState {
1127            peer_socket_id: Some(socket_id),
1128            ..ConnectionState::default()
1129        });
1130
1131        let socket_clone = socket.clone();
1132        let peer_clone = peer.clone();
1133        table.sockets.insert(socket_id, socket);
1134        table.sockets.insert(peer_socket_id, peer);
1135        Ok((socket_clone, peer_clone))
1136    }
1137
1138    pub fn find_bound_inet_socket(
1139        &self,
1140        spec: SocketSpec,
1141        address: &InetSocketAddress,
1142    ) -> Option<SocketRecord> {
1143        let address = normalize_inet_address(address.clone());
1144        let table = lock_or_recover(&self.inner.state);
1145        let socket_id = lookup_bound_inet_socket(&table, spec, &address)?;
1146        table.sockets.get(&socket_id).cloned()
1147    }
1148
1149    pub fn connect_to_bound_inet_stream(
1150        &self,
1151        socket_id: SocketId,
1152        target_address: InetSocketAddress,
1153    ) -> SocketResult<()> {
1154        let target_address = normalize_inet_address(target_address);
1155        let (result, readiness) = {
1156            let mut table = lock_or_recover(&self.inner.state);
1157            let listener_socket_id =
1158                lookup_bound_inet_socket_in_table(&table.bound_inet_streams, &target_address)
1159                    .ok_or_else(|| {
1160                        SocketTableError::not_found_address(format!(
1161                            "no listening socket bound at {}:{}",
1162                            target_address.host(),
1163                            target_address.port()
1164                        ))
1165                    })?;
1166
1167            if socket_id == listener_socket_id {
1168                return Err(SocketTableError::invalid_argument(
1169                    "socket cannot connect to its own listening endpoint",
1170                ));
1171            }
1172
1173            let mut client = table
1174                .sockets
1175                .remove(&socket_id)
1176                .ok_or_else(|| SocketTableError::not_found(socket_id))?;
1177            let mut accept_was_empty = false;
1178            let result = (|| {
1179                // Validate the listener and confirm backlog capacity BEFORE consuming a
1180                // socket id. The id counter is monotonic (saturating_add) and never
1181                // reclaims, so allocating an id before this check leaks one on every
1182                // rejected connect (for example when the backlog is full).
1183                {
1184                    let listener = table
1185                        .sockets
1186                        .get(&listener_socket_id)
1187                        .ok_or_else(|| SocketTableError::not_found(listener_socket_id))?;
1188                    validate_connect_to_listener(&client, listener)?;
1189
1190                    let listener_state = listener.listener_state.as_ref().ok_or_else(|| {
1191                        SocketTableError::invalid_argument(format!(
1192                            "socket {listener_socket_id} has no listener state"
1193                        ))
1194                    })?;
1195                    if listener_state.pending_accepts.len() >= listener_state.backlog {
1196                        return Err(SocketTableError::would_block(format!(
1197                            "listener {listener_socket_id} backlog is full"
1198                        )));
1199                    }
1200                }
1201
1202                // Capacity confirmed: only now is it safe to consume a socket id.
1203                let accepted_socket_id = next_socket_id(&mut table);
1204                let listener = table
1205                    .sockets
1206                    .get_mut(&listener_socket_id)
1207                    .ok_or_else(|| SocketTableError::not_found(listener_socket_id))?;
1208                let listener_state = listener.listener_state.as_mut().ok_or_else(|| {
1209                    SocketTableError::invalid_argument(format!(
1210                        "socket {listener_socket_id} has no listener state"
1211                    ))
1212                })?;
1213
1214                let accepted = SocketRecord {
1215                    id: accepted_socket_id,
1216                    owner_pid: listener.owner_pid,
1217                    spec: listener.spec,
1218                    state: SocketState::Connected,
1219                    local_address: listener.local_address.clone(),
1220                    peer_address: client.local_address.clone(),
1221                    local_unix_path: None,
1222                    peer_unix_path: None,
1223                    listener_state: None,
1224                    connection_state: Some(ConnectionState {
1225                        peer_socket_id: Some(socket_id),
1226                        ..ConnectionState::default()
1227                    }),
1228                    datagram_state: default_datagram_state(listener.spec),
1229                };
1230
1231                accept_was_empty = listener_state.pending_accepts.is_empty();
1232                listener_state.pending_accepts.push_back(PendingConnection {
1233                    peer_address: client.local_address.clone(),
1234                    peer_unix_path: None,
1235                    accepted_socket_id: Some(accepted_socket_id),
1236                });
1237
1238                client.state = SocketState::Connected;
1239                client.peer_address = listener.local_address.clone();
1240                client.peer_unix_path = None;
1241                client.listener_state = None;
1242                client.connection_state = Some(ConnectionState {
1243                    peer_socket_id: Some(accepted_socket_id),
1244                    ..ConnectionState::default()
1245                });
1246
1247                Ok(accepted)
1248            })();
1249
1250            let result = match result {
1251                Ok(accepted) => {
1252                    let accepted_socket_id = accepted.id;
1253                    table.sockets.insert(socket_id, client);
1254                    table.sockets.insert(accepted_socket_id, accepted.clone());
1255                    table
1256                        .by_owner
1257                        .entry(accepted.owner_pid)
1258                        .or_default()
1259                        .insert(accepted_socket_id);
1260                    Ok(())
1261                }
1262                Err(error) => {
1263                    table.sockets.insert(socket_id, client);
1264                    Err(error)
1265                }
1266            };
1267            let readiness = result.is_ok().then_some(()).and_then(|()| {
1268                accept_was_empty.then_some(SocketReadiness {
1269                    socket_id: listener_socket_id,
1270                    kind: SocketReadinessKind::Accept,
1271                })
1272            });
1273            (result, readiness)
1274        };
1275        self.emit_readiness(readiness);
1276        result
1277    }
1278
1279    pub fn find_bound_unix_socket(&self, path: &str) -> Option<SocketRecord> {
1280        let path = normalize_unix_socket_path(path).ok()?;
1281        let table = lock_or_recover(&self.inner.state);
1282        let socket_id = table.bound_unix_streams.get(&path).copied()?;
1283        table.sockets.get(&socket_id).cloned()
1284    }
1285
1286    pub fn connect_to_bound_unix_stream(
1287        &self,
1288        socket_id: SocketId,
1289        target_path: impl Into<String>,
1290    ) -> SocketResult<()> {
1291        let target_path = normalize_unix_socket_path(target_path.into())?;
1292        let (result, readiness) = {
1293            let mut table = lock_or_recover(&self.inner.state);
1294            let listener_socket_id = table
1295                .bound_unix_streams
1296                .get(&target_path)
1297                .copied()
1298                .ok_or_else(|| {
1299                    SocketTableError::not_found_address(format!(
1300                        "no listening socket bound at path {target_path}"
1301                    ))
1302                })?;
1303
1304            if socket_id == listener_socket_id {
1305                return Err(SocketTableError::invalid_argument(
1306                    "socket cannot connect to its own listening endpoint",
1307                ));
1308            }
1309
1310            let mut client = table
1311                .sockets
1312                .remove(&socket_id)
1313                .ok_or_else(|| SocketTableError::not_found(socket_id))?;
1314            let mut accept_was_empty = false;
1315            let result = (|| {
1316                // Validate the listener and confirm backlog capacity BEFORE consuming a
1317                // socket id. The id counter is monotonic (saturating_add) and never
1318                // reclaims, so allocating an id before this check leaks one on every
1319                // rejected connect (for example when the backlog is full).
1320                {
1321                    let listener = table
1322                        .sockets
1323                        .get(&listener_socket_id)
1324                        .ok_or_else(|| SocketTableError::not_found(listener_socket_id))?;
1325                    validate_connect_to_listener(&client, listener)?;
1326
1327                    let listener_state = listener.listener_state.as_ref().ok_or_else(|| {
1328                        SocketTableError::invalid_argument(format!(
1329                            "socket {listener_socket_id} has no listener state"
1330                        ))
1331                    })?;
1332                    if listener_state.pending_accepts.len() >= listener_state.backlog {
1333                        return Err(SocketTableError::would_block(format!(
1334                            "listener {listener_socket_id} backlog is full"
1335                        )));
1336                    }
1337                }
1338
1339                // Capacity confirmed: only now is it safe to consume a socket id.
1340                let accepted_socket_id = next_socket_id(&mut table);
1341                let listener = table
1342                    .sockets
1343                    .get_mut(&listener_socket_id)
1344                    .ok_or_else(|| SocketTableError::not_found(listener_socket_id))?;
1345                let listener_state = listener.listener_state.as_mut().ok_or_else(|| {
1346                    SocketTableError::invalid_argument(format!(
1347                        "socket {listener_socket_id} has no listener state"
1348                    ))
1349                })?;
1350
1351                let accepted = SocketRecord {
1352                    id: accepted_socket_id,
1353                    owner_pid: listener.owner_pid,
1354                    spec: listener.spec,
1355                    state: SocketState::Connected,
1356                    local_address: None,
1357                    peer_address: None,
1358                    local_unix_path: listener.local_unix_path.clone(),
1359                    peer_unix_path: client.local_unix_path.clone(),
1360                    listener_state: None,
1361                    connection_state: Some(ConnectionState {
1362                        peer_socket_id: Some(socket_id),
1363                        ..ConnectionState::default()
1364                    }),
1365                    datagram_state: default_datagram_state(listener.spec),
1366                };
1367
1368                accept_was_empty = listener_state.pending_accepts.is_empty();
1369                listener_state.pending_accepts.push_back(PendingConnection {
1370                    peer_address: None,
1371                    peer_unix_path: client.local_unix_path.clone(),
1372                    accepted_socket_id: Some(accepted_socket_id),
1373                });
1374
1375                client.state = SocketState::Connected;
1376                client.peer_address = None;
1377                client.peer_unix_path = listener.local_unix_path.clone();
1378                client.listener_state = None;
1379                client.connection_state = Some(ConnectionState {
1380                    peer_socket_id: Some(accepted_socket_id),
1381                    ..ConnectionState::default()
1382                });
1383
1384                Ok(accepted)
1385            })();
1386
1387            let result = match result {
1388                Ok(accepted) => {
1389                    let accepted_socket_id = accepted.id;
1390                    table.sockets.insert(socket_id, client);
1391                    table.sockets.insert(accepted_socket_id, accepted.clone());
1392                    table
1393                        .by_owner
1394                        .entry(accepted.owner_pid)
1395                        .or_default()
1396                        .insert(accepted_socket_id);
1397                    Ok(())
1398                }
1399                Err(error) => {
1400                    table.sockets.insert(socket_id, client);
1401                    Err(error)
1402                }
1403            };
1404            let readiness = result.is_ok().then_some(()).and_then(|()| {
1405                accept_was_empty.then_some(SocketReadiness {
1406                    socket_id: listener_socket_id,
1407                    kind: SocketReadinessKind::Accept,
1408                })
1409            });
1410            (result, readiness)
1411        };
1412        self.emit_readiness(readiness);
1413        result
1414    }
1415
1416    pub fn send_to_bound_udp_socket(
1417        &self,
1418        socket_id: SocketId,
1419        target_address: InetSocketAddress,
1420        data: &[u8],
1421    ) -> SocketResult<usize> {
1422        let target_address = normalize_inet_address(target_address);
1423        let readiness = {
1424            let mut table = lock_or_recover(&self.inner.state);
1425            let sender = table
1426                .sockets
1427                .get(&socket_id)
1428                .cloned()
1429                .ok_or_else(|| SocketTableError::not_found(socket_id))?;
1430            validate_bound_udp_sender(&sender)?;
1431
1432            let receiver_socket_id = lookup_bound_inet_datagram_socket_in_table(
1433                &table.bound_inet_datagrams,
1434                &target_address,
1435            )
1436            .ok_or_else(|| {
1437                SocketTableError::not_found_address(format!(
1438                    "no UDP socket bound at {}:{}",
1439                    target_address.host(),
1440                    target_address.port()
1441                ))
1442            })?;
1443            let receiver = table
1444                .sockets
1445                .get_mut(&receiver_socket_id)
1446                .ok_or_else(|| SocketTableError::not_found(receiver_socket_id))?;
1447            validate_bound_udp_receiver(receiver)?;
1448
1449            let datagram_state = receiver.datagram_state.as_mut().ok_or_else(|| {
1450                SocketTableError::invalid_argument(format!(
1451                    "socket {receiver_socket_id} does not support datagrams"
1452                ))
1453            })?;
1454            let was_empty = datagram_state.recv_queue.is_empty();
1455            datagram_state.recv_queue.push_back(QueuedDatagram {
1456                source_address: sender.local_address.clone(),
1457                payload: data.to_vec(),
1458            });
1459            was_empty.then_some(SocketReadiness {
1460                socket_id: receiver_socket_id,
1461                kind: SocketReadinessKind::Data,
1462            })
1463        };
1464        self.emit_readiness(readiness);
1465        Ok(data.len())
1466    }
1467
1468    pub fn check_send_to_bound_udp_socket(
1469        &self,
1470        socket_id: SocketId,
1471        target_address: InetSocketAddress,
1472    ) -> SocketResult<()> {
1473        let target_address = normalize_inet_address(target_address);
1474        let table = lock_or_recover(&self.inner.state);
1475        let sender = table
1476            .sockets
1477            .get(&socket_id)
1478            .ok_or_else(|| SocketTableError::not_found(socket_id))?;
1479        validate_bound_udp_sender(sender)?;
1480
1481        let receiver_socket_id = lookup_bound_inet_datagram_socket_in_table(
1482            &table.bound_inet_datagrams,
1483            &target_address,
1484        )
1485        .ok_or_else(|| {
1486            SocketTableError::not_found_address(format!(
1487                "no UDP socket bound at {}:{}",
1488                target_address.host(),
1489                target_address.port()
1490            ))
1491        })?;
1492        let receiver = table
1493            .sockets
1494            .get(&receiver_socket_id)
1495            .ok_or_else(|| SocketTableError::not_found(receiver_socket_id))?;
1496        validate_bound_udp_receiver(receiver)?;
1497        Ok(())
1498    }
1499
1500    pub fn recv_datagram(
1501        &self,
1502        socket_id: SocketId,
1503        max_bytes: usize,
1504    ) -> SocketResult<Option<ReceivedDatagram>> {
1505        let mut table = lock_or_recover(&self.inner.state);
1506        let record = table
1507            .sockets
1508            .get_mut(&socket_id)
1509            .ok_or_else(|| SocketTableError::not_found(socket_id))?;
1510        validate_bound_udp_receiver(record)?;
1511
1512        let datagram_state = record.datagram_state.as_mut().ok_or_else(|| {
1513            SocketTableError::invalid_argument(format!(
1514                "socket {socket_id} does not support datagrams"
1515            ))
1516        })?;
1517        let Some(datagram) = datagram_state.recv_queue.pop_front() else {
1518            return Err(SocketTableError::would_block(format!(
1519                "socket {socket_id} has no queued datagrams"
1520            )));
1521        };
1522
1523        let payload = if datagram.payload.len() > max_bytes {
1524            datagram.payload[..max_bytes].to_vec()
1525        } else {
1526            datagram.payload
1527        };
1528        Ok(Some(ReceivedDatagram {
1529            source_address: datagram.source_address,
1530            payload,
1531        }))
1532    }
1533
1534    pub fn poll(&self, socket_id: SocketId, requested: PollEvents) -> SocketResult<PollEvents> {
1535        let table = lock_or_recover(&self.inner.state);
1536        let record = table
1537            .sockets
1538            .get(&socket_id)
1539            .ok_or_else(|| SocketTableError::not_found(socket_id))?;
1540
1541        let mut events = PollEvents::empty();
1542        match record.state {
1543            SocketState::Listening => {
1544                if requested.intersects(POLLIN) && record.pending_accept_count() > 0 {
1545                    events |= POLLIN;
1546                }
1547            }
1548            SocketState::Connected => {
1549                let connection = record.connection_state.as_ref().ok_or_else(|| {
1550                    SocketTableError::not_connected(format!("socket {socket_id} is not connected"))
1551                })?;
1552                let peer = connection
1553                    .peer_socket_id
1554                    .and_then(|peer_socket_id| table.sockets.get(&peer_socket_id));
1555
1556                if requested.intersects(POLLIN) && connection.has_buffered_data() {
1557                    events |= POLLIN;
1558                }
1559                if connection.peer_write_shutdown || peer.is_none() {
1560                    events |= POLLHUP;
1561                }
1562
1563                if requested.intersects(POLLOUT) && !connection.write_shutdown {
1564                    if peer
1565                        .and_then(|peer| peer.connection_state.as_ref())
1566                        .map(|peer_connection| peer_connection.read_shutdown)
1567                        .unwrap_or(true)
1568                    {
1569                        events |= POLLERR;
1570                    } else {
1571                        events |= POLLOUT;
1572                    }
1573                }
1574            }
1575            SocketState::Bound if supports_inet_datagram_lifecycle(record.spec) => {
1576                let datagram_state = record.datagram_state.as_ref().ok_or_else(|| {
1577                    SocketTableError::invalid_argument(format!(
1578                        "socket {socket_id} does not support datagrams"
1579                    ))
1580                })?;
1581                if requested.intersects(POLLIN) && !datagram_state.recv_queue.is_empty() {
1582                    events |= POLLIN;
1583                }
1584                if requested.intersects(POLLOUT) {
1585                    events |= POLLOUT;
1586                }
1587            }
1588            SocketState::Created | SocketState::Bound => {}
1589        }
1590
1591        Ok(events)
1592    }
1593
1594    pub fn write(&self, socket_id: SocketId, data: &[u8]) -> SocketResult<usize> {
1595        let readiness = {
1596            let mut table = lock_or_recover(&self.inner.state);
1597            let record = table
1598                .sockets
1599                .get(&socket_id)
1600                .cloned()
1601                .ok_or_else(|| SocketTableError::not_found(socket_id))?;
1602            let connection = record.connection_state.as_ref().ok_or_else(|| {
1603                SocketTableError::not_connected(format!("socket {socket_id} is not connected"))
1604            })?;
1605            if record.state != SocketState::Connected {
1606                return Err(SocketTableError::not_connected(format!(
1607                    "socket {socket_id} is not connected"
1608                )));
1609            }
1610            if connection.write_shutdown {
1611                return Err(SocketTableError::broken_pipe(format!(
1612                    "socket {socket_id} write side is shut down"
1613                )));
1614            }
1615
1616            let peer_socket_id = connection.peer_socket_id.ok_or_else(|| {
1617                SocketTableError::broken_pipe(format!("socket {socket_id} peer is closed"))
1618            })?;
1619            let peer = table.sockets.get_mut(&peer_socket_id).ok_or_else(|| {
1620                SocketTableError::broken_pipe(format!("socket {socket_id} peer is closed"))
1621            })?;
1622            let peer_connection = peer.connection_state.as_mut().ok_or_else(|| {
1623                SocketTableError::broken_pipe(format!("socket {socket_id} peer is closed"))
1624            })?;
1625            if peer_connection.read_shutdown {
1626                return Err(SocketTableError::broken_pipe(format!(
1627                    "socket {peer_socket_id} read side is shut down"
1628                )));
1629            }
1630
1631            let was_empty = !peer_connection.has_buffered_data();
1632            peer_connection.push_recv(data);
1633            (was_empty && !data.is_empty()).then_some(SocketReadiness {
1634                socket_id: peer_socket_id,
1635                kind: SocketReadinessKind::Data,
1636            })
1637        };
1638        self.emit_readiness(readiness);
1639        Ok(data.len())
1640    }
1641
1642    pub fn check_write(&self, socket_id: SocketId) -> SocketResult<()> {
1643        let table = lock_or_recover(&self.inner.state);
1644        let record = table
1645            .sockets
1646            .get(&socket_id)
1647            .ok_or_else(|| SocketTableError::not_found(socket_id))?;
1648        let connection = record.connection_state.as_ref().ok_or_else(|| {
1649            SocketTableError::not_connected(format!("socket {socket_id} is not connected"))
1650        })?;
1651        if record.state != SocketState::Connected {
1652            return Err(SocketTableError::not_connected(format!(
1653                "socket {socket_id} is not connected"
1654            )));
1655        }
1656        if connection.write_shutdown {
1657            return Err(SocketTableError::broken_pipe(format!(
1658                "socket {socket_id} write side is shut down"
1659            )));
1660        }
1661
1662        let peer_socket_id = connection.peer_socket_id.ok_or_else(|| {
1663            SocketTableError::broken_pipe(format!("socket {socket_id} peer is closed"))
1664        })?;
1665        let peer = table.sockets.get(&peer_socket_id).ok_or_else(|| {
1666            SocketTableError::broken_pipe(format!("socket {socket_id} peer is closed"))
1667        })?;
1668        let peer_connection = peer.connection_state.as_ref().ok_or_else(|| {
1669            SocketTableError::broken_pipe(format!("socket {socket_id} peer is closed"))
1670        })?;
1671        if peer_connection.read_shutdown {
1672            return Err(SocketTableError::broken_pipe(format!(
1673                "socket {peer_socket_id} read side is shut down"
1674            )));
1675        }
1676
1677        Ok(())
1678    }
1679
1680    pub fn read(&self, socket_id: SocketId, max_bytes: usize) -> SocketResult<Option<Vec<u8>>> {
1681        if max_bytes == 0 {
1682            return Ok(Some(Vec::new()));
1683        }
1684
1685        let mut table = lock_or_recover(&self.inner.state);
1686        let record_ref = table
1687            .sockets
1688            .get(&socket_id)
1689            .ok_or_else(|| SocketTableError::not_found(socket_id))?;
1690        let clone_started = SOCKET_READ_TRACE_ENABLED
1691            .load(Ordering::Relaxed)
1692            .then(Instant::now);
1693        let record = record_ref.clone();
1694        if let Some(started) = clone_started {
1695            SOCKET_READ_TRACE_COUNTERS
1696                .socket_record_clone_calls
1697                .fetch_add(1, Ordering::Relaxed);
1698            SOCKET_READ_TRACE_COUNTERS.socket_record_clone_us.fetch_add(
1699                u64::try_from(started.elapsed().as_micros()).unwrap_or(u64::MAX),
1700                Ordering::Relaxed,
1701            );
1702        }
1703        if record.state != SocketState::Connected {
1704            return Err(SocketTableError::not_connected(format!(
1705                "socket {socket_id} is not connected"
1706            )));
1707        }
1708
1709        let connection = record.connection_state.as_ref().ok_or_else(|| {
1710            SocketTableError::not_connected(format!("socket {socket_id} is not connected"))
1711        })?;
1712        if connection.read_shutdown {
1713            return Ok(None);
1714        }
1715        if connection.has_buffered_data() {
1716            let record = table
1717                .sockets
1718                .get_mut(&socket_id)
1719                .ok_or_else(|| SocketTableError::not_found(socket_id))?;
1720            let connection = record.connection_state.as_mut().ok_or_else(|| {
1721                SocketTableError::not_connected(format!("socket {socket_id} is not connected"))
1722            })?;
1723            return Ok(connection.read_recv(max_bytes));
1724        }
1725
1726        let peer_open = connection
1727            .peer_socket_id
1728            .map(|peer_socket_id| table.sockets.contains_key(&peer_socket_id))
1729            .unwrap_or(false);
1730        if connection.peer_write_shutdown || !peer_open {
1731            return Ok(None);
1732        }
1733
1734        Err(SocketTableError::would_block(format!(
1735            "socket {socket_id} has no readable data"
1736        )))
1737    }
1738
1739    pub fn shutdown(&self, socket_id: SocketId, how: SocketShutdown) -> SocketResult<SocketRecord> {
1740        let mut table = lock_or_recover(&self.inner.state);
1741        let record = table
1742            .sockets
1743            .remove(&socket_id)
1744            .ok_or_else(|| SocketTableError::not_found(socket_id))?;
1745
1746        if record.state != SocketState::Connected {
1747            table.sockets.insert(socket_id, record);
1748            return Err(SocketTableError::not_connected(format!(
1749                "socket {socket_id} is not connected"
1750            )));
1751        }
1752
1753        let Some(mut connection) = record.connection_state.clone() else {
1754            table.sockets.insert(socket_id, record);
1755            return Err(SocketTableError::not_connected(format!(
1756                "socket {socket_id} is not connected"
1757            )));
1758        };
1759
1760        if matches!(how, SocketShutdown::Read | SocketShutdown::Both) {
1761            connection.clear_recv();
1762            connection.read_shutdown = true;
1763        }
1764        if matches!(how, SocketShutdown::Write | SocketShutdown::Both) {
1765            connection.write_shutdown = true;
1766            if let Some(peer_socket_id) = connection.peer_socket_id {
1767                if let Some(peer) = table.sockets.get_mut(&peer_socket_id) {
1768                    if let Some(peer_connection) = peer.connection_state.as_mut() {
1769                        peer_connection.peer_write_shutdown = true;
1770                    }
1771                }
1772            }
1773        }
1774
1775        let mut record = record;
1776        record.connection_state = Some(connection);
1777        let cloned = record.clone();
1778        table.sockets.insert(socket_id, record);
1779        Ok(cloned)
1780    }
1781
1782    pub fn remove(&self, socket_id: SocketId) -> SocketResult<SocketRecord> {
1783        let mut table = lock_or_recover(&self.inner.state);
1784        remove_socket(&mut table, socket_id).ok_or_else(|| SocketTableError::not_found(socket_id))
1785    }
1786
1787    pub fn remove_all_for_pid(&self, owner_pid: u32) -> Vec<SocketRecord> {
1788        let mut table = lock_or_recover(&self.inner.state);
1789        let Some(socket_ids) = table.by_owner.remove(&owner_pid) else {
1790            return Vec::new();
1791        };
1792
1793        socket_ids
1794            .into_iter()
1795            .filter_map(|socket_id| remove_socket(&mut table, socket_id))
1796            .collect()
1797    }
1798
1799    pub fn snapshot(&self) -> SocketTableSnapshot {
1800        let table = lock_or_recover(&self.inner.state);
1801        let mut snapshot = SocketTableSnapshot {
1802            sockets: table.sockets.len(),
1803            ..SocketTableSnapshot::default()
1804        };
1805        for record in table.sockets.values() {
1806            if record.state.counts_as_listener() {
1807                snapshot.listeners += 1;
1808            }
1809            if record.state.counts_as_connection() {
1810                snapshot.connections += 1;
1811            }
1812            if let Some(connection) = &record.connection_state {
1813                snapshot.buffered_bytes = snapshot
1814                    .buffered_bytes
1815                    .saturating_add(connection.buffered_len());
1816            }
1817            if let Some(datagram_state) = &record.datagram_state {
1818                snapshot.datagram_queue_len = snapshot
1819                    .datagram_queue_len
1820                    .saturating_add(datagram_state.recv_queue.len());
1821                snapshot.buffered_bytes = snapshot
1822                    .buffered_bytes
1823                    .saturating_add(datagram_queue_bytes(&datagram_state.recv_queue));
1824            }
1825        }
1826        snapshot
1827    }
1828}
1829
1830fn datagram_queue_bytes(queue: &VecDeque<QueuedDatagram>) -> usize {
1831    queue
1832        .iter()
1833        .map(|datagram| datagram.payload.len())
1834        .sum::<usize>()
1835}
1836
1837fn next_socket_id(table: &mut SocketTableState) -> SocketId {
1838    if table.next_socket_id == 0 {
1839        table.next_socket_id = 1;
1840    }
1841    let socket_id = table.next_socket_id;
1842    table.next_socket_id = table.next_socket_id.saturating_add(1);
1843    socket_id
1844}
1845
1846fn validate_state_transition(current: SocketState, next: SocketState) -> SocketResult<()> {
1847    if current == SocketState::Connected && next != SocketState::Connected {
1848        return Err(SocketTableError::invalid_argument(format!(
1849            "invalid socket state transition from {current:?} to {next:?}"
1850        )));
1851    }
1852    Ok(())
1853}
1854
1855fn validate_connect_pair(socket: &SocketRecord, peer: &SocketRecord) -> SocketResult<()> {
1856    if !supports_connection_lifecycle(socket.spec) {
1857        return Err(SocketTableError::invalid_argument(format!(
1858            "socket {} does not support stream connections",
1859            socket.id
1860        )));
1861    }
1862    if !supports_connection_lifecycle(peer.spec) {
1863        return Err(SocketTableError::invalid_argument(format!(
1864            "socket {} does not support stream connections",
1865            peer.id
1866        )));
1867    }
1868    if !matches!(socket.state, SocketState::Created | SocketState::Bound) {
1869        return Err(SocketTableError::invalid_argument(format!(
1870            "socket {} cannot connect in state {:?}",
1871            socket.id, socket.state
1872        )));
1873    }
1874    if !matches!(peer.state, SocketState::Created | SocketState::Bound) {
1875        return Err(SocketTableError::invalid_argument(format!(
1876            "socket {} cannot connect in state {:?}",
1877            peer.id, peer.state
1878        )));
1879    }
1880    Ok(())
1881}
1882
1883fn default_connection_state(spec: SocketSpec, state: SocketState) -> Option<ConnectionState> {
1884    if state == SocketState::Connected && supports_connection_lifecycle(spec) {
1885        Some(ConnectionState::default())
1886    } else {
1887        None
1888    }
1889}
1890
1891fn default_datagram_state(spec: SocketSpec) -> Option<DatagramState> {
1892    if supports_inet_datagram_lifecycle(spec) {
1893        Some(DatagramState::default())
1894    } else {
1895        None
1896    }
1897}
1898
1899fn supports_connection_lifecycle(spec: SocketSpec) -> bool {
1900    matches!(spec.socket_type, SocketType::Stream)
1901}
1902
1903fn supports_listener_lifecycle(spec: SocketSpec) -> bool {
1904    matches!(spec.socket_type, SocketType::Stream)
1905        && matches!(
1906            spec.domain,
1907            SocketDomain::Inet | SocketDomain::Inet6 | SocketDomain::Unix
1908        )
1909}
1910
1911fn supports_inet_bind(spec: SocketSpec) -> bool {
1912    matches!(spec.domain, SocketDomain::Inet | SocketDomain::Inet6)
1913        && matches!(spec.socket_type, SocketType::Stream | SocketType::Datagram)
1914}
1915
1916fn supports_unix_stream_lifecycle(spec: SocketSpec) -> bool {
1917    matches!(spec.socket_type, SocketType::Stream) && matches!(spec.domain, SocketDomain::Unix)
1918}
1919
1920fn supports_inet_stream_lifecycle(spec: SocketSpec) -> bool {
1921    matches!(spec.socket_type, SocketType::Stream)
1922        && matches!(spec.domain, SocketDomain::Inet | SocketDomain::Inet6)
1923}
1924
1925fn supports_inet_datagram_lifecycle(spec: SocketSpec) -> bool {
1926    matches!(spec.socket_type, SocketType::Datagram)
1927        && matches!(spec.domain, SocketDomain::Inet | SocketDomain::Inet6)
1928}
1929
1930/// Pick a free ephemeral port for `bind(port=0)`, scanning the IANA dynamic
1931/// range and skipping any already in conflict for `spec`/`host`.
1932fn assign_ephemeral_inet_port(
1933    table: &SocketTableState,
1934    spec: SocketSpec,
1935    host: &str,
1936) -> SocketResult<u16> {
1937    const EPHEMERAL_START: u16 = 49152;
1938    const EPHEMERAL_END: u16 = 65535;
1939    for port in EPHEMERAL_START..=EPHEMERAL_END {
1940        let candidate = normalize_inet_address(InetSocketAddress::new(host, port));
1941        if lookup_conflicting_bound_inet_socket_ids(table, spec, &candidate).is_empty() {
1942            return Ok(port);
1943        }
1944    }
1945    Err(SocketTableError::address_in_use(
1946        "no free ephemeral port available",
1947    ))
1948}
1949
1950fn lookup_conflicting_bound_inet_socket_ids(
1951    table: &SocketTableState,
1952    spec: SocketSpec,
1953    address: &InetSocketAddress,
1954) -> Vec<SocketId> {
1955    if supports_inet_stream_lifecycle(spec) {
1956        table
1957            .bound_inet_streams
1958            .iter()
1959            .find_map(|(bound_address, socket_id)| {
1960                inet_stream_bind_addresses_overlap(bound_address, address).then_some(*socket_id)
1961            })
1962            .into_iter()
1963            .collect()
1964    } else if supports_inet_datagram_lifecycle(spec) {
1965        table
1966            .bound_inet_datagrams
1967            .iter()
1968            .filter(|(bound_address, _)| inet_stream_bind_addresses_overlap(bound_address, address))
1969            .flat_map(|(_, socket_ids)| socket_ids.iter().copied())
1970            .collect()
1971    } else {
1972        Vec::new()
1973    }
1974}
1975
1976fn lookup_bound_inet_socket(
1977    table: &SocketTableState,
1978    spec: SocketSpec,
1979    address: &InetSocketAddress,
1980) -> Option<SocketId> {
1981    if supports_inet_stream_lifecycle(spec) {
1982        lookup_bound_inet_socket_in_table(&table.bound_inet_streams, address)
1983    } else if supports_inet_datagram_lifecycle(spec) {
1984        lookup_bound_inet_datagram_socket_in_table(&table.bound_inet_datagrams, address)
1985    } else {
1986        None
1987    }
1988}
1989
1990fn inet_stream_bind_addresses_overlap(
1991    existing: &InetSocketAddress,
1992    requested: &InetSocketAddress,
1993) -> bool {
1994    if existing == requested {
1995        return true;
1996    }
1997
1998    wildcard_inet_address(existing).as_ref() == Some(requested)
1999        || wildcard_inet_address(requested).as_ref() == Some(existing)
2000}
2001
2002fn lookup_bound_inet_socket_in_table(
2003    sockets: &BTreeMap<InetSocketAddress, SocketId>,
2004    address: &InetSocketAddress,
2005) -> Option<SocketId> {
2006    sockets.get(address).copied().or_else(|| {
2007        wildcard_inet_address(address).and_then(|wildcard| sockets.get(&wildcard).copied())
2008    })
2009}
2010
2011fn lookup_bound_inet_datagram_socket_in_table(
2012    sockets: &BTreeMap<InetSocketAddress, BTreeSet<SocketId>>,
2013    address: &InetSocketAddress,
2014) -> Option<SocketId> {
2015    sockets
2016        .get(address)
2017        .and_then(|socket_ids| socket_ids.first().copied())
2018        .or_else(|| {
2019            wildcard_inet_address(address).and_then(|wildcard| {
2020                sockets
2021                    .get(&wildcard)
2022                    .and_then(|socket_ids| socket_ids.first().copied())
2023            })
2024        })
2025}
2026
2027fn register_bound_inet_socket(
2028    table: &mut SocketTableState,
2029    spec: SocketSpec,
2030    address: InetSocketAddress,
2031    socket_id: SocketId,
2032) {
2033    if supports_inet_stream_lifecycle(spec) {
2034        table.bound_inet_streams.insert(address, socket_id);
2035    } else if supports_inet_datagram_lifecycle(spec) {
2036        table
2037            .bound_inet_datagrams
2038            .entry(address)
2039            .or_default()
2040            .insert(socket_id);
2041    }
2042}
2043
2044fn validate_connect_to_listener(
2045    client: &SocketRecord,
2046    listener: &SocketRecord,
2047) -> SocketResult<()> {
2048    if !supports_connection_lifecycle(client.spec) {
2049        return Err(SocketTableError::invalid_argument(format!(
2050            "socket {} does not support stream connections",
2051            client.id
2052        )));
2053    }
2054    if !supports_listener_lifecycle(listener.spec) {
2055        return Err(SocketTableError::invalid_argument(format!(
2056            "socket {} is not a stream listener",
2057            listener.id
2058        )));
2059    }
2060    if !matches!(client.state, SocketState::Created | SocketState::Bound) {
2061        return Err(SocketTableError::invalid_argument(format!(
2062            "socket {} cannot connect in state {:?}",
2063            client.id, client.state
2064        )));
2065    }
2066    if listener.state != SocketState::Listening {
2067        return Err(SocketTableError::invalid_argument(format!(
2068            "socket {} is not listening",
2069            listener.id
2070        )));
2071    }
2072    Ok(())
2073}
2074
2075fn has_bound_endpoint(record: &SocketRecord) -> bool {
2076    record.local_address.is_some() || record.local_unix_path.is_some()
2077}
2078
2079fn validate_bound_udp_sender(sender: &SocketRecord) -> SocketResult<()> {
2080    if !supports_inet_datagram_lifecycle(sender.spec) {
2081        return Err(SocketTableError::invalid_argument(format!(
2082            "socket {} is not an INET datagram socket",
2083            sender.id
2084        )));
2085    }
2086    if sender.state != SocketState::Bound || sender.local_address.is_none() {
2087        return Err(SocketTableError::invalid_argument(format!(
2088            "socket {} must be bound before sending datagrams",
2089            sender.id
2090        )));
2091    }
2092    Ok(())
2093}
2094
2095fn validate_bound_udp_receiver(receiver: &SocketRecord) -> SocketResult<()> {
2096    if !supports_inet_datagram_lifecycle(receiver.spec) {
2097        return Err(SocketTableError::invalid_argument(format!(
2098            "socket {} is not an INET datagram socket",
2099            receiver.id
2100        )));
2101    }
2102    if receiver.state != SocketState::Bound || receiver.local_address.is_none() {
2103        return Err(SocketTableError::invalid_argument(format!(
2104            "socket {} must be bound to receive datagrams",
2105            receiver.id
2106        )));
2107    }
2108    Ok(())
2109}
2110
2111fn datagram_state_mut(record: &mut SocketRecord) -> SocketResult<&mut DatagramState> {
2112    if !supports_inet_datagram_lifecycle(record.spec) {
2113        return Err(SocketTableError::invalid_argument(format!(
2114            "socket {} is not an INET datagram socket",
2115            record.id
2116        )));
2117    }
2118    record.datagram_state.as_mut().ok_or_else(|| {
2119        SocketTableError::invalid_argument(format!(
2120            "socket {} does not support datagrams",
2121            record.id
2122        ))
2123    })
2124}
2125
2126fn validate_multicast_socket(record: &SocketRecord) -> SocketResult<()> {
2127    validate_bound_udp_receiver(record)?;
2128    if record.spec.domain != SocketDomain::Inet {
2129        return Err(SocketTableError::invalid_argument(format!(
2130            "socket {} multicast membership is only implemented for IPv4 datagrams",
2131            record.id
2132        )));
2133    }
2134    Ok(())
2135}
2136
2137fn normalize_multicast_membership(
2138    spec: SocketSpec,
2139    membership: SocketMulticastMembership,
2140) -> SocketResult<SocketMulticastMembership> {
2141    let group_address = membership.group_address.trim().to_ascii_lowercase();
2142    let interface_address = membership
2143        .interface_address
2144        .map(|value| value.trim().to_ascii_lowercase())
2145        .filter(|value| !value.is_empty());
2146
2147    match spec.domain {
2148        SocketDomain::Inet => {
2149            let parsed = group_address.parse::<Ipv4Addr>().map_err(|_| {
2150                SocketTableError::invalid_argument(format!(
2151                    "invalid IPv4 multicast address {group_address}"
2152                ))
2153            })?;
2154            if !parsed.is_multicast() {
2155                return Err(SocketTableError::invalid_argument(format!(
2156                    "address {group_address} is not an IPv4 multicast group"
2157                )));
2158            }
2159        }
2160        SocketDomain::Inet6 => {
2161            let parsed = group_address.parse::<Ipv6Addr>().map_err(|_| {
2162                SocketTableError::invalid_argument(format!(
2163                    "invalid IPv6 multicast address {group_address}"
2164                ))
2165            })?;
2166            if !parsed.is_multicast() {
2167                return Err(SocketTableError::invalid_argument(format!(
2168                    "address {group_address} is not an IPv6 multicast group"
2169                )));
2170            }
2171        }
2172        SocketDomain::Unix => {
2173            return Err(SocketTableError::invalid_argument(
2174                "unix sockets do not support multicast membership",
2175            ));
2176        }
2177    }
2178
2179    Ok(SocketMulticastMembership::new(
2180        group_address,
2181        interface_address,
2182    ))
2183}
2184
2185fn has_incompatible_inet_bind_conflict(
2186    table: &SocketTableState,
2187    record: &SocketRecord,
2188    conflicting_ids: &[SocketId],
2189) -> bool {
2190    conflicting_ids.iter().any(|conflicting_id| {
2191        if *conflicting_id == record.id {
2192            return false;
2193        }
2194
2195        let Some(existing) = table.sockets.get(conflicting_id) else {
2196            return false;
2197        };
2198
2199        if supports_inet_datagram_lifecycle(record.spec) {
2200            !inet_datagram_bind_shares_port(record, existing)
2201        } else {
2202            true
2203        }
2204    })
2205}
2206
2207fn inet_datagram_bind_shares_port(requested: &SocketRecord, existing: &SocketRecord) -> bool {
2208    (requested.reuse_port() && existing.reuse_port())
2209        || (requested.reuse_address() && existing.reuse_address())
2210}
2211
2212fn remove_socket(table: &mut SocketTableState, socket_id: SocketId) -> Option<SocketRecord> {
2213    let record = table.sockets.remove(&socket_id)?;
2214    unregister_bound_socket(table, &record);
2215    unregister_multicast_memberships(table, &record);
2216    if let Some(listener_state) = record.listener_state.as_ref() {
2217        let pending_socket_ids = listener_state
2218            .pending_accepts
2219            .iter()
2220            .filter_map(|pending| pending.accepted_socket_id)
2221            .collect::<Vec<_>>();
2222        for pending_socket_id in pending_socket_ids {
2223            let _ = remove_socket(table, pending_socket_id);
2224        }
2225    }
2226    if let Some(connection) = record.connection_state.as_ref() {
2227        if let Some(peer_socket_id) = connection.peer_socket_id {
2228            if let Some(peer) = table.sockets.get_mut(&peer_socket_id) {
2229                if let Some(peer_connection) = peer.connection_state.as_mut() {
2230                    if peer_connection.peer_socket_id == Some(socket_id) {
2231                        peer_connection.peer_socket_id = None;
2232                    }
2233                    peer_connection.peer_write_shutdown = true;
2234                }
2235            }
2236        }
2237    }
2238    if let Some(owner_sockets) = table.by_owner.get_mut(&record.owner_pid) {
2239        owner_sockets.remove(&socket_id);
2240        if owner_sockets.is_empty() {
2241            table.by_owner.remove(&record.owner_pid);
2242        }
2243    }
2244    Some(record)
2245}
2246
2247fn unregister_bound_socket(table: &mut SocketTableState, record: &SocketRecord) {
2248    let Some(address) = record.local_address.as_ref() else {
2249        if supports_unix_stream_lifecycle(record.spec) {
2250            if let Some(path) = record.local_unix_path.as_ref() {
2251                if table.bound_unix_streams.get(path).copied() == Some(record.id) {
2252                    table.bound_unix_streams.remove(path);
2253                }
2254            }
2255        }
2256        return;
2257    };
2258    if supports_inet_stream_lifecycle(record.spec)
2259        && table.bound_inet_streams.get(address).copied() == Some(record.id)
2260    {
2261        table.bound_inet_streams.remove(address);
2262    }
2263    if supports_inet_datagram_lifecycle(record.spec) {
2264        if let Some(socket_ids) = table.bound_inet_datagrams.get_mut(address) {
2265            socket_ids.remove(&record.id);
2266            if socket_ids.is_empty() {
2267                table.bound_inet_datagrams.remove(address);
2268            }
2269        }
2270    }
2271}
2272
2273fn unregister_multicast_memberships(table: &mut SocketTableState, record: &SocketRecord) {
2274    let Some(datagram_state) = record.datagram_state.as_ref() else {
2275        return;
2276    };
2277
2278    for membership in &datagram_state.multicast_memberships {
2279        if let Some(socket_ids) = table.multicast_groups.get_mut(membership) {
2280            socket_ids.remove(&record.id);
2281            if socket_ids.is_empty() {
2282                table.multicast_groups.remove(membership);
2283            }
2284        }
2285    }
2286}
2287
2288fn normalize_inet_address(address: InetSocketAddress) -> InetSocketAddress {
2289    match address.host().to_ascii_lowercase().as_str() {
2290        "localhost" => InetSocketAddress::new("127.0.0.1", address.port()),
2291        _ => address,
2292    }
2293}
2294
2295fn wildcard_inet_address(address: &InetSocketAddress) -> Option<InetSocketAddress> {
2296    match address.host() {
2297        "127.0.0.1" => Some(InetSocketAddress::new("0.0.0.0", address.port())),
2298        "::1" => Some(InetSocketAddress::new("::", address.port())),
2299        _ => None,
2300    }
2301}
2302
2303fn normalize_unix_socket_path(path: impl AsRef<str>) -> SocketResult<String> {
2304    let normalized = normalize_path(path.as_ref());
2305    if normalized == "/" {
2306        return Err(SocketTableError::invalid_argument(
2307            "unix socket path must not be empty or root",
2308        ));
2309    }
2310    Ok(normalized)
2311}
2312
2313fn lock_or_recover<'a, T>(mutex: &'a Mutex<T>) -> MutexGuard<'a, T> {
2314    match mutex.lock() {
2315        Ok(guard) => guard,
2316        Err(poisoned) => poisoned.into_inner(),
2317    }
2318}
2319
2320#[cfg(test)]
2321mod tests {
2322    use super::*;
2323
2324    /// Reads the monotonic socket-id counter without advancing it, so a test can
2325    /// observe whether a code path consumed an id.
2326    fn peek_next_socket_id(table: &SocketTable) -> SocketId {
2327        lock_or_recover(&table.inner.state).next_socket_id
2328    }
2329
2330    #[test]
2331    fn full_backlog_unix_connect_does_not_consume_socket_id() {
2332        let table = SocketTable::new();
2333        let path = "/tmp/leak-test/server.sock";
2334
2335        let listener = table.allocate(1, SocketSpec::unix_stream());
2336        table
2337            .bind_unix(listener.id, path)
2338            .expect("bind unix listener");
2339        table.listen(listener.id, 1).expect("listen with backlog 1");
2340
2341        // Fill the only backlog slot with one pending connection.
2342        let first = table.allocate(2, SocketSpec::unix_stream());
2343        table
2344            .connect_to_bound_unix_stream(first.id, path)
2345            .expect("first connect fills the backlog");
2346
2347        // A second connect must be rejected because the backlog is full, and it
2348        // must NOT consume a socket id (the counter is monotonic and never reclaims).
2349        let second = table.allocate(2, SocketSpec::unix_stream());
2350        let before = peek_next_socket_id(&table);
2351        let error = table
2352            .connect_to_bound_unix_stream(second.id, path)
2353            .expect_err("full-backlog connect must fail");
2354        assert_eq!(error.code(), "EAGAIN");
2355        let after = peek_next_socket_id(&table);
2356
2357        assert_eq!(
2358            before, after,
2359            "full-backlog unix connect leaked a socket id (counter advanced from {before} to {after})"
2360        );
2361    }
2362
2363    #[test]
2364    fn full_backlog_inet_connect_does_not_consume_socket_id() {
2365        let table = SocketTable::new();
2366        let target = InetSocketAddress::new("127.0.0.1", 49222);
2367
2368        let listener = table.allocate(1, SocketSpec::tcp());
2369        table
2370            .bind_inet(listener.id, target.clone())
2371            .expect("bind inet listener");
2372        table.listen(listener.id, 1).expect("listen with backlog 1");
2373
2374        // Fill the only backlog slot with one pending connection.
2375        let first = table.allocate(2, SocketSpec::tcp());
2376        table
2377            .connect_to_bound_inet_stream(first.id, target.clone())
2378            .expect("first connect fills the backlog");
2379
2380        // A second connect must be rejected because the backlog is full, and it
2381        // must NOT consume a socket id (the counter is monotonic and never reclaims).
2382        let second = table.allocate(2, SocketSpec::tcp());
2383        let before = peek_next_socket_id(&table);
2384        let error = table
2385            .connect_to_bound_inet_stream(second.id, target)
2386            .expect_err("full-backlog connect must fail");
2387        assert_eq!(error.code(), "EAGAIN");
2388        let after = peek_next_socket_id(&table);
2389
2390        assert_eq!(
2391            before, after,
2392            "full-backlog inet connect leaked a socket id (counter advanced from {before} to {after})"
2393        );
2394    }
2395}