Skip to main content

agentos_kernel/
socket_table.rs

1use crate::fd_table::TransferredFd;
2use crate::poll::{PollEvents, POLLERR, POLLHUP, POLLIN, POLLOUT};
3use crate::vfs::normalize_path;
4#[cfg(not(target_arch = "wasm32"))]
5use agentos_runtime::accounting::{Reservation, ResourceClass, ResourceLedger};
6use std::any::Any;
7use std::collections::{BTreeMap, BTreeSet, VecDeque};
8use std::error::Error;
9use std::fmt;
10use std::net::{Ipv4Addr, Ipv6Addr};
11use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
12use std::sync::{Arc, Mutex, MutexGuard};
13use std::time::Instant;
14
15pub type SocketId = u64;
16pub type SocketResult<T> = Result<T, SocketTableError>;
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum SocketReadinessKind {
20    Data,
21    Accept,
22    Hangup,
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub struct SocketReadiness {
27    pub socket_id: SocketId,
28    pub kind: SocketReadinessKind,
29}
30
31type SocketReadinessSink = Arc<dyn Fn(SocketReadiness) + Send + Sync + 'static>;
32
33#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
34pub struct SocketReadTraceSnapshot {
35    pub socket_record_clone_calls: u64,
36    pub socket_record_clone_us: u64,
37    pub read_recv_calls: u64,
38    pub read_recv_bytes: u64,
39    pub read_recv_chunks: u64,
40    pub read_recv_copy_us: u64,
41}
42
43struct SocketReadTraceCounters {
44    socket_record_clone_calls: AtomicU64,
45    socket_record_clone_us: AtomicU64,
46    read_recv_calls: AtomicU64,
47    read_recv_bytes: AtomicU64,
48    read_recv_chunks: AtomicU64,
49    read_recv_copy_us: AtomicU64,
50}
51
52impl SocketReadTraceCounters {
53    const fn new() -> Self {
54        Self {
55            socket_record_clone_calls: AtomicU64::new(0),
56            socket_record_clone_us: AtomicU64::new(0),
57            read_recv_calls: AtomicU64::new(0),
58            read_recv_bytes: AtomicU64::new(0),
59            read_recv_chunks: AtomicU64::new(0),
60            read_recv_copy_us: AtomicU64::new(0),
61        }
62    }
63}
64
65static SOCKET_READ_TRACE_ENABLED: AtomicBool = AtomicBool::new(false);
66static SOCKET_READ_TRACE_COUNTERS: SocketReadTraceCounters = SocketReadTraceCounters::new();
67
68pub fn set_socket_read_trace_enabled(enabled: bool) {
69    SOCKET_READ_TRACE_ENABLED.store(enabled, Ordering::Relaxed);
70}
71
72pub fn reset_socket_read_trace() {
73    for counter in [
74        &SOCKET_READ_TRACE_COUNTERS.socket_record_clone_calls,
75        &SOCKET_READ_TRACE_COUNTERS.socket_record_clone_us,
76        &SOCKET_READ_TRACE_COUNTERS.read_recv_calls,
77        &SOCKET_READ_TRACE_COUNTERS.read_recv_bytes,
78        &SOCKET_READ_TRACE_COUNTERS.read_recv_chunks,
79        &SOCKET_READ_TRACE_COUNTERS.read_recv_copy_us,
80    ] {
81        counter.store(0, Ordering::Relaxed);
82    }
83}
84
85pub fn socket_read_trace_snapshot() -> SocketReadTraceSnapshot {
86    SocketReadTraceSnapshot {
87        socket_record_clone_calls: SOCKET_READ_TRACE_COUNTERS
88            .socket_record_clone_calls
89            .load(Ordering::Relaxed),
90        socket_record_clone_us: SOCKET_READ_TRACE_COUNTERS
91            .socket_record_clone_us
92            .load(Ordering::Relaxed),
93        read_recv_calls: SOCKET_READ_TRACE_COUNTERS
94            .read_recv_calls
95            .load(Ordering::Relaxed),
96        read_recv_bytes: SOCKET_READ_TRACE_COUNTERS
97            .read_recv_bytes
98            .load(Ordering::Relaxed),
99        read_recv_chunks: SOCKET_READ_TRACE_COUNTERS
100            .read_recv_chunks
101            .load(Ordering::Relaxed),
102        read_recv_copy_us: SOCKET_READ_TRACE_COUNTERS
103            .read_recv_copy_us
104            .load(Ordering::Relaxed),
105    }
106}
107
108#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
109pub struct InetSocketAddress {
110    host: String,
111    port: u16,
112}
113
114impl InetSocketAddress {
115    pub fn new(host: impl Into<String>, port: u16) -> Self {
116        Self {
117            host: host.into(),
118            port,
119        }
120    }
121
122    pub fn host(&self) -> &str {
123        &self.host
124    }
125
126    pub const fn port(&self) -> u16 {
127        self.port
128    }
129}
130
131#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
132pub enum SocketDomain {
133    Inet,
134    Inet6,
135    Unix,
136}
137
138#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
139pub enum SocketType {
140    Stream,
141    Datagram,
142    SeqPacket,
143}
144
145#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
146pub enum SocketState {
147    Created,
148    Bound,
149    Listening,
150    Connected,
151}
152
153impl SocketState {
154    pub const fn counts_as_listener(self) -> bool {
155        matches!(self, Self::Listening)
156    }
157
158    pub const fn counts_as_connection(self) -> bool {
159        matches!(self, Self::Connected)
160    }
161}
162
163#[derive(Debug, Clone, Copy, PartialEq, Eq)]
164pub enum SocketShutdown {
165    Read,
166    Write,
167    Both,
168}
169
170#[derive(Debug, Clone, Copy, PartialEq, Eq)]
171pub enum DatagramSocketOption {
172    ReuseAddr,
173    ReusePort,
174    Broadcast,
175}
176
177#[derive(Debug, Clone, Copy, PartialEq, Eq)]
178pub struct SocketSpec {
179    pub domain: SocketDomain,
180    pub socket_type: SocketType,
181}
182
183impl SocketSpec {
184    pub const fn new(domain: SocketDomain, socket_type: SocketType) -> Self {
185        Self {
186            domain,
187            socket_type,
188        }
189    }
190
191    pub const fn tcp() -> Self {
192        Self::new(SocketDomain::Inet, SocketType::Stream)
193    }
194
195    pub const fn udp() -> Self {
196        Self::new(SocketDomain::Inet, SocketType::Datagram)
197    }
198
199    pub const fn unix_stream() -> Self {
200        Self::new(SocketDomain::Unix, SocketType::Stream)
201    }
202
203    pub const fn unix_datagram() -> Self {
204        Self::new(SocketDomain::Unix, SocketType::Datagram)
205    }
206
207    pub const fn unix_seqpacket() -> Self {
208        Self::new(SocketDomain::Unix, SocketType::SeqPacket)
209    }
210}
211
212#[derive(Debug, Clone, PartialEq, Eq)]
213pub struct SocketRecord {
214    id: SocketId,
215    owner_pid: u32,
216    spec: SocketSpec,
217    state: SocketState,
218    local_address: Option<InetSocketAddress>,
219    peer_address: Option<InetSocketAddress>,
220    local_unix_path: Option<String>,
221    peer_unix_path: Option<String>,
222    listener_state: Option<ListenerState>,
223    connection_state: Option<ConnectionState>,
224    datagram_state: Option<DatagramState>,
225}
226
227impl SocketRecord {
228    pub const fn id(&self) -> SocketId {
229        self.id
230    }
231
232    pub const fn owner_pid(&self) -> u32 {
233        self.owner_pid
234    }
235
236    pub const fn spec(&self) -> SocketSpec {
237        self.spec
238    }
239
240    pub const fn state(&self) -> SocketState {
241        self.state
242    }
243
244    pub fn local_address(&self) -> Option<&InetSocketAddress> {
245        self.local_address.as_ref()
246    }
247
248    pub fn peer_address(&self) -> Option<&InetSocketAddress> {
249        self.peer_address.as_ref()
250    }
251
252    pub fn local_unix_path(&self) -> Option<&str> {
253        self.local_unix_path.as_deref()
254    }
255
256    pub fn peer_unix_path(&self) -> Option<&str> {
257        self.peer_unix_path.as_deref()
258    }
259
260    pub fn listen_backlog(&self) -> Option<usize> {
261        self.listener_state.as_ref().map(|state| state.backlog)
262    }
263
264    pub fn pending_accept_count(&self) -> usize {
265        self.listener_state
266            .as_ref()
267            .map(|state| state.pending_accepts.len())
268            .unwrap_or(0)
269    }
270
271    pub fn peer_socket_id(&self) -> Option<SocketId> {
272        self.connection_state
273            .as_ref()
274            .and_then(|state| state.peer_socket_id)
275    }
276
277    pub fn buffered_read_bytes(&self) -> usize {
278        self.connection_state
279            .as_ref()
280            .map(ConnectionState::buffered_len)
281            .unwrap_or(0)
282    }
283
284    pub fn read_shutdown(&self) -> bool {
285        self.connection_state
286            .as_ref()
287            .map(|state| state.read_shutdown)
288            .unwrap_or(false)
289    }
290
291    pub fn write_shutdown(&self) -> bool {
292        self.connection_state
293            .as_ref()
294            .map(|state| state.write_shutdown)
295            .unwrap_or(false)
296    }
297
298    pub fn peer_write_shutdown(&self) -> bool {
299        self.connection_state
300            .as_ref()
301            .map(|state| state.peer_write_shutdown)
302            .unwrap_or(false)
303    }
304
305    pub fn queued_datagrams(&self) -> usize {
306        self.datagram_state
307            .as_ref()
308            .map(|state| state.recv_queue.len())
309            .unwrap_or_else(|| {
310                self.connection_state
311                    .as_ref()
312                    .filter(|_| self.spec.socket_type != SocketType::Stream)
313                    .map(|state| state.recv_buffer.len())
314                    .unwrap_or(0)
315            })
316    }
317
318    pub fn queued_datagram_bytes(&self) -> usize {
319        self.datagram_state
320            .as_ref()
321            .map(|state| datagram_queue_bytes(&state.recv_queue))
322            .unwrap_or(0)
323    }
324
325    pub fn reuse_address(&self) -> bool {
326        self.datagram_state
327            .as_ref()
328            .map(|state| state.reuse_addr)
329            .unwrap_or(false)
330    }
331
332    pub fn reuse_port(&self) -> bool {
333        self.datagram_state
334            .as_ref()
335            .map(|state| state.reuse_port)
336            .unwrap_or(false)
337    }
338
339    pub fn broadcast_enabled(&self) -> bool {
340        self.datagram_state
341            .as_ref()
342            .map(|state| state.broadcast)
343            .unwrap_or(false)
344    }
345
346    pub fn multicast_membership_count(&self) -> usize {
347        self.datagram_state
348            .as_ref()
349            .map(|state| state.multicast_memberships.len())
350            .unwrap_or(0)
351    }
352
353    pub fn has_multicast_membership(&self, membership: &SocketMulticastMembership) -> bool {
354        self.datagram_state
355            .as_ref()
356            .map(|state| state.multicast_memberships.contains(membership))
357            .unwrap_or(false)
358    }
359}
360
361#[derive(Debug, Clone, PartialEq, Eq)]
362pub struct ReceivedDatagram {
363    source_address: Option<InetSocketAddress>,
364    payload: Vec<u8>,
365}
366
367pub type OpaqueTransferredRight = Arc<dyn Any + Send + Sync + 'static>;
368
369#[derive(Clone)]
370pub enum TransferredSocketRight {
371    Fd(TransferredFd),
372    Opaque(OpaqueTransferredRight),
373}
374
375impl fmt::Debug for TransferredSocketRight {
376    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
377        match self {
378            Self::Fd(fd) => f.debug_tuple("Fd").field(&fd.description_id()).finish(),
379            Self::Opaque(resource) => f
380                .debug_tuple("Opaque")
381                .field(&(Arc::as_ptr(resource) as *const ()))
382                .finish(),
383        }
384    }
385}
386
387impl PartialEq for TransferredSocketRight {
388    fn eq(&self, other: &Self) -> bool {
389        match (self, other) {
390            (Self::Fd(left), Self::Fd(right)) => left == right,
391            (Self::Opaque(left), Self::Opaque(right)) => Arc::ptr_eq(left, right),
392            _ => false,
393        }
394    }
395}
396
397impl Eq for TransferredSocketRight {}
398
399#[derive(Debug)]
400pub struct ReceivedSocketMessage {
401    pub payload: Vec<u8>,
402    pub rights: Vec<TransferredSocketRight>,
403    pub truncated: bool,
404    pub full_length: usize,
405}
406
407impl ReceivedDatagram {
408    pub fn source_address(&self) -> Option<&InetSocketAddress> {
409        self.source_address.as_ref()
410    }
411
412    pub fn payload(&self) -> &[u8] {
413        &self.payload
414    }
415
416    pub fn into_parts(self) -> (Option<InetSocketAddress>, Vec<u8>) {
417        (self.source_address, self.payload)
418    }
419}
420
421/// Native sidecar handoff that transfers the kernel queue's exact resource
422/// ownership with the datagram. Standalone callers may keep using
423/// `recv_datagram`, whose boundary releases queue ownership immediately.
424#[cfg(not(target_arch = "wasm32"))]
425pub type DatagramReservations = (Reservation, Reservation, Reservation, Reservation);
426
427#[cfg(not(target_arch = "wasm32"))]
428#[derive(Debug)]
429pub struct ChargedReceivedDatagram {
430    datagram: ReceivedDatagram,
431    reservations: Option<DatagramReservations>,
432}
433
434#[cfg(not(target_arch = "wasm32"))]
435impl ChargedReceivedDatagram {
436    pub fn into_parts(
437        self,
438    ) -> (
439        Option<InetSocketAddress>,
440        Vec<u8>,
441        Option<DatagramReservations>,
442    ) {
443        let (source_address, payload) = self.datagram.into_parts();
444        (source_address, payload, self.reservations)
445    }
446}
447
448#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
449pub struct SocketTableSnapshot {
450    pub sockets: usize,
451    pub listeners: usize,
452    pub connections: usize,
453    pub buffered_bytes: usize,
454    pub datagram_queue_len: usize,
455}
456
457#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
458pub struct SocketMulticastMembership {
459    group_address: String,
460    interface_address: Option<String>,
461}
462
463impl SocketMulticastMembership {
464    pub fn new(group_address: impl Into<String>, interface_address: Option<String>) -> Self {
465        Self {
466            group_address: group_address.into(),
467            interface_address,
468        }
469    }
470
471    pub fn group_address(&self) -> &str {
472        &self.group_address
473    }
474
475    pub fn interface_address(&self) -> Option<&str> {
476        self.interface_address.as_deref()
477    }
478}
479
480#[derive(Debug, Clone, PartialEq, Eq)]
481pub struct SocketTableError {
482    code: &'static str,
483    message: String,
484}
485
486impl SocketTableError {
487    pub fn code(&self) -> &'static str {
488        self.code
489    }
490
491    fn not_found(socket_id: SocketId) -> Self {
492        Self {
493            code: "ENOENT",
494            message: format!("no such socket {socket_id}"),
495        }
496    }
497
498    fn invalid_argument(message: impl Into<String>) -> Self {
499        Self {
500            code: "EINVAL",
501            message: message.into(),
502        }
503    }
504
505    fn address_in_use(message: impl Into<String>) -> Self {
506        Self {
507            code: "EADDRINUSE",
508            message: message.into(),
509        }
510    }
511
512    fn address_not_available(message: impl Into<String>) -> Self {
513        Self {
514            code: "EADDRNOTAVAIL",
515            message: message.into(),
516        }
517    }
518
519    fn not_found_address(message: impl Into<String>) -> Self {
520        Self {
521            code: "ECONNREFUSED",
522            message: message.into(),
523        }
524    }
525
526    fn would_block(message: impl Into<String>) -> Self {
527        Self {
528            code: "EAGAIN",
529            message: message.into(),
530        }
531    }
532
533    fn not_connected(message: impl Into<String>) -> Self {
534        Self {
535            code: "ENOTCONN",
536            message: message.into(),
537        }
538    }
539
540    fn broken_pipe(message: impl Into<String>) -> Self {
541        Self {
542            code: "EPIPE",
543            message: message.into(),
544        }
545    }
546
547    #[cfg(not(target_arch = "wasm32"))]
548    fn resource_limit(error: agentos_runtime::accounting::LimitError) -> Self {
549        Self {
550            code: "EAGAIN",
551            message: error.to_string(),
552        }
553    }
554
555    #[cfg(not(target_arch = "wasm32"))]
556    fn accounting_invariant(message: impl Into<String>) -> Self {
557        Self {
558            code: "EIO",
559            message: format!(
560                "ERR_AGENTOS_RESOURCE_ACCOUNTING_INVARIANT: {}",
561                message.into()
562            ),
563        }
564    }
565
566    fn id_exhausted() -> Self {
567        Self {
568            code: "EMFILE",
569            message: String::from(
570                "ERR_AGENTOS_SOCKET_ID_EXHAUSTED: VM kernel socket id space exhausted",
571            ),
572        }
573    }
574}
575
576impl fmt::Display for SocketTableError {
577    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
578        write!(f, "{}: {}", self.code, self.message)
579    }
580}
581
582impl Error for SocketTableError {}
583
584#[derive(Debug, Default)]
585struct SocketTableState {
586    sockets: BTreeMap<SocketId, SocketRecord>,
587    by_owner: BTreeMap<u32, BTreeSet<SocketId>>,
588    bound_inet_streams: BTreeMap<InetSocketAddress, SocketId>,
589    bound_inet_datagrams: BTreeMap<InetSocketAddress, BTreeSet<SocketId>>,
590    bound_unix_streams: BTreeMap<String, SocketId>,
591    multicast_groups: BTreeMap<SocketMulticastMembership, BTreeSet<SocketId>>,
592    next_socket_id: SocketId,
593    #[cfg(not(target_arch = "wasm32"))]
594    retained_resources: BTreeMap<SocketId, RetainedSocketResources>,
595}
596
597/// Exact ownership for bytes and datagrams retained by one kernel socket.
598///
599/// Socket and connection count reservations intentionally live in the shared
600/// capability registry. Keeping only queue storage here avoids charging the
601/// same kernel-backed capability twice while still making queue admission and
602/// mutation one operation.
603#[cfg(not(target_arch = "wasm32"))]
604#[derive(Debug, Default)]
605struct RetainedSocketResources {
606    buffered_bytes: VecDeque<Reservation>,
607    datagrams: VecDeque<Reservation>,
608    udp_bytes: VecDeque<Reservation>,
609    udp_datagrams: VecDeque<Reservation>,
610}
611
612#[derive(Debug, Clone, PartialEq, Eq)]
613struct ListenerState {
614    backlog: usize,
615    pending_accepts: VecDeque<PendingConnection>,
616}
617
618#[derive(Debug, Clone, PartialEq, Eq, Default)]
619struct ConnectionState {
620    peer_socket_id: Option<SocketId>,
621    recv_buffer: VecDeque<RecvChunk>,
622    recv_buffer_len: usize,
623    read_shutdown: bool,
624    write_shutdown: bool,
625    peer_write_shutdown: bool,
626}
627
628#[derive(Debug, Clone, PartialEq, Eq)]
629struct RecvChunk {
630    data: Vec<u8>,
631    rights: Vec<TransferredSocketRight>,
632}
633
634impl ConnectionState {
635    fn buffered_len(&self) -> usize {
636        self.recv_buffer_len
637    }
638
639    fn has_buffered_data(&self) -> bool {
640        !self.recv_buffer.is_empty()
641    }
642
643    fn push_recv(&mut self, data: &[u8], rights: Vec<TransferredSocketRight>) {
644        if data.is_empty() && rights.is_empty() {
645            return;
646        }
647        self.recv_buffer.push_back(RecvChunk {
648            data: data.to_vec(),
649            rights,
650        });
651        self.recv_buffer_len = self.recv_buffer_len.saturating_add(data.len());
652    }
653
654    fn read_recv(&mut self, max_bytes: usize, message_oriented: bool) -> Option<Vec<u8>> {
655        self.read_recv_message(max_bytes, message_oriented)
656            .map(|message| message.payload)
657    }
658
659    fn read_recv_message(
660        &mut self,
661        max_bytes: usize,
662        message_oriented: bool,
663    ) -> Option<ReceivedSocketMessage> {
664        if self.recv_buffer.is_empty() {
665            return None;
666        }
667
668        if message_oriented {
669            let chunk = self.recv_buffer.pop_front()?;
670            self.recv_buffer_len = self.recv_buffer_len.saturating_sub(chunk.data.len());
671            let truncated = chunk.data.len() > max_bytes;
672            let full_length = chunk.data.len();
673            return Some(ReceivedSocketMessage {
674                payload: chunk.data[..chunk.data.len().min(max_bytes)].to_vec(),
675                rights: chunk.rights,
676                truncated,
677                full_length,
678            });
679        }
680
681        if max_bytes == 0 {
682            let chunk = self.recv_buffer.front_mut()?;
683            return Some(ReceivedSocketMessage {
684                payload: Vec::new(),
685                rights: std::mem::take(&mut chunk.rights),
686                truncated: false,
687                full_length: 0,
688            });
689        }
690
691        let read_len = self.recv_buffer_len.min(max_bytes);
692        self.recv_buffer_len -= read_len;
693
694        let mut remaining = read_len;
695        let mut chunks = 0usize;
696        let trace_started = SOCKET_READ_TRACE_ENABLED
697            .load(Ordering::Relaxed)
698            .then(Instant::now);
699        let mut out = Vec::with_capacity(read_len);
700        let mut rights = Vec::new();
701        while remaining > 0 {
702            let mut chunk = self.recv_buffer.pop_front()?;
703            chunks += 1;
704            rights.append(&mut chunk.rights);
705            if chunk.data.len() <= remaining {
706                remaining -= chunk.data.len();
707                out.extend_from_slice(&chunk.data);
708                continue;
709            }
710
711            let tail = chunk.data.split_off(remaining);
712            out.extend_from_slice(&chunk.data);
713            self.recv_buffer.push_front(RecvChunk {
714                data: tail,
715                rights: Vec::new(),
716            });
717            remaining = 0;
718        }
719        if let Some(started) = trace_started {
720            SOCKET_READ_TRACE_COUNTERS
721                .read_recv_calls
722                .fetch_add(1, Ordering::Relaxed);
723            SOCKET_READ_TRACE_COUNTERS.read_recv_bytes.fetch_add(
724                u64::try_from(read_len).unwrap_or(u64::MAX),
725                Ordering::Relaxed,
726            );
727            SOCKET_READ_TRACE_COUNTERS
728                .read_recv_chunks
729                .fetch_add(u64::try_from(chunks).unwrap_or(u64::MAX), Ordering::Relaxed);
730            SOCKET_READ_TRACE_COUNTERS.read_recv_copy_us.fetch_add(
731                u64::try_from(started.elapsed().as_micros()).unwrap_or(u64::MAX),
732                Ordering::Relaxed,
733            );
734        }
735        Some(ReceivedSocketMessage {
736            payload: out,
737            rights,
738            truncated: false,
739            full_length: read_len,
740        })
741    }
742
743    fn peek_recv_message(
744        &self,
745        max_bytes: usize,
746        message_oriented: bool,
747    ) -> Option<ReceivedSocketMessage> {
748        let first = self.recv_buffer.front()?;
749        if message_oriented {
750            return Some(ReceivedSocketMessage {
751                payload: first.data[..first.data.len().min(max_bytes)].to_vec(),
752                rights: first.rights.clone(),
753                truncated: first.data.len() > max_bytes,
754                full_length: first.data.len(),
755            });
756        }
757
758        let read_len = self.recv_buffer_len.min(max_bytes);
759        let mut payload = Vec::with_capacity(read_len);
760        let mut rights = Vec::new();
761        let mut remaining = read_len;
762        for (index, chunk) in self.recv_buffer.iter().enumerate() {
763            if index > 0 && remaining == 0 {
764                break;
765            }
766            rights.extend(chunk.rights.iter().cloned());
767            let take = remaining.min(chunk.data.len());
768            payload.extend_from_slice(&chunk.data[..take]);
769            remaining -= take;
770        }
771        Some(ReceivedSocketMessage {
772            payload,
773            rights,
774            truncated: false,
775            full_length: read_len,
776        })
777    }
778
779    fn clear_recv(&mut self) {
780        self.recv_buffer.clear();
781        self.recv_buffer_len = 0;
782    }
783}
784
785#[derive(Debug, Clone, PartialEq, Eq)]
786struct PendingConnection {
787    peer_address: Option<InetSocketAddress>,
788    peer_unix_path: Option<String>,
789    accepted_socket_id: Option<SocketId>,
790}
791
792#[derive(Debug, Clone, PartialEq, Eq, Default)]
793struct DatagramState {
794    recv_queue: VecDeque<QueuedDatagram>,
795    reuse_addr: bool,
796    reuse_port: bool,
797    broadcast: bool,
798    multicast_memberships: BTreeSet<SocketMulticastMembership>,
799}
800
801#[derive(Debug, Clone, PartialEq, Eq)]
802struct QueuedDatagram {
803    source_address: Option<InetSocketAddress>,
804    payload: Vec<u8>,
805}
806
807struct SocketTableInner {
808    state: Mutex<SocketTableState>,
809    readiness_sink: Mutex<Option<SocketReadinessSink>>,
810    #[cfg(not(target_arch = "wasm32"))]
811    resource_ledger: Mutex<Option<Arc<ResourceLedger>>>,
812}
813
814impl Default for SocketTableInner {
815    fn default() -> Self {
816        Self {
817            state: Mutex::new(SocketTableState::default()),
818            readiness_sink: Mutex::new(None),
819            #[cfg(not(target_arch = "wasm32"))]
820            resource_ledger: Mutex::new(None),
821        }
822    }
823}
824
825impl fmt::Debug for SocketTableInner {
826    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
827        f.debug_struct("SocketTableInner")
828            .field("state", &self.state)
829            .finish_non_exhaustive()
830    }
831}
832
833#[derive(Debug, Clone, Default)]
834pub struct SocketTable {
835    inner: Arc<SocketTableInner>,
836}
837
838impl SocketTable {
839    pub fn new() -> Self {
840        Self::default()
841    }
842
843    /// Install the VM ledger before any socket is created. The table owns
844    /// reservations only for data physically retained in kernel queues.
845    #[cfg(not(target_arch = "wasm32"))]
846    pub fn set_resource_ledger(&self, ledger: Arc<ResourceLedger>) -> SocketResult<()> {
847        let table = lock_or_recover(&self.inner.state);
848        if !table.sockets.is_empty() {
849            return Err(SocketTableError::invalid_argument(
850                "socket resource ledger must be installed before socket creation",
851            ));
852        }
853        let mut target = lock_or_recover(&self.inner.resource_ledger);
854        if let Some(current) = target.as_ref() {
855            if Arc::ptr_eq(current, &ledger) {
856                return Ok(());
857            }
858            return Err(SocketTableError::invalid_argument(
859                "socket resource ledger is already installed",
860            ));
861        }
862        *target = Some(ledger);
863        Ok(())
864    }
865
866    #[cfg(not(target_arch = "wasm32"))]
867    pub fn has_resource_ledger(&self) -> bool {
868        lock_or_recover(&self.inner.resource_ledger).is_some()
869    }
870
871    #[cfg(target_arch = "wasm32")]
872    pub const fn has_resource_ledger(&self) -> bool {
873        false
874    }
875
876    #[cfg(not(target_arch = "wasm32"))]
877    fn resource_ledger(&self) -> Option<Arc<ResourceLedger>> {
878        lock_or_recover(&self.inner.resource_ledger).clone()
879    }
880
881    #[cfg(not(target_arch = "wasm32"))]
882    pub fn buffered_byte_capacity_available(&self) -> bool {
883        self.resource_ledger()
884            .is_none_or(|ledger| ledger.capacity_available(ResourceClass::BufferedBytes, 1))
885    }
886
887    #[cfg(not(target_arch = "wasm32"))]
888    pub fn datagram_capacity_available(&self) -> bool {
889        self.resource_ledger().is_none_or(|ledger| {
890            ledger.capacity_available(ResourceClass::Datagrams, 1)
891                && ledger.capacity_available(ResourceClass::UdpDatagrams, 1)
892        })
893    }
894
895    #[cfg(not(target_arch = "wasm32"))]
896    fn reserve_buffered_bytes(&self, amount: usize) -> SocketResult<Option<Reservation>> {
897        if amount == 0 {
898            return Ok(None);
899        }
900        self.resource_ledger()
901            .map(|ledger| {
902                ledger
903                    .reserve(ResourceClass::BufferedBytes, amount)
904                    .map_err(SocketTableError::resource_limit)
905            })
906            .transpose()
907    }
908
909    #[cfg(not(target_arch = "wasm32"))]
910    fn reserve_datagram(
911        &self,
912        amount: usize,
913    ) -> SocketResult<Option<(Reservation, Reservation, Reservation, Reservation)>> {
914        let Some(ledger) = self.resource_ledger() else {
915            return Ok(None);
916        };
917        let bytes = ledger
918            .reserve(ResourceClass::BufferedBytes, amount)
919            .map_err(SocketTableError::resource_limit)?;
920        let datagram = ledger
921            .reserve(ResourceClass::Datagrams, 1)
922            .map_err(SocketTableError::resource_limit)?;
923        let udp_bytes = ledger
924            .reserve(ResourceClass::UdpBytes, amount)
925            .map_err(SocketTableError::resource_limit)?;
926        let udp_datagram = ledger
927            .reserve(ResourceClass::UdpDatagrams, 1)
928            .map_err(SocketTableError::resource_limit)?;
929        Ok(Some((bytes, datagram, udp_bytes, udp_datagram)))
930    }
931
932    pub fn set_readiness_sink<F>(&self, sink: Option<F>)
933    where
934        F: Fn(SocketReadiness) + Send + Sync + 'static,
935    {
936        let mut target = lock_or_recover(&self.inner.readiness_sink);
937        *target = sink.map(|sink| Arc::new(sink) as SocketReadinessSink);
938    }
939
940    fn emit_readiness(&self, readiness: Option<SocketReadiness>) {
941        let Some(readiness) = readiness else {
942            return;
943        };
944        let sink = lock_or_recover(&self.inner.readiness_sink).clone();
945        if let Some(sink) = sink {
946            sink(readiness);
947        }
948    }
949
950    pub fn allocate(&self, owner_pid: u32, spec: SocketSpec) -> SocketResult<SocketRecord> {
951        self.allocate_with_state(owner_pid, spec, SocketState::Created)
952    }
953
954    pub fn allocate_with_state(
955        &self,
956        owner_pid: u32,
957        spec: SocketSpec,
958        state: SocketState,
959    ) -> SocketResult<SocketRecord> {
960        let mut table = lock_or_recover(&self.inner.state);
961        let socket_id = next_socket_id(&mut table)?;
962        let record = SocketRecord {
963            id: socket_id,
964            owner_pid,
965            spec,
966            state,
967            local_address: None,
968            peer_address: None,
969            local_unix_path: None,
970            peer_unix_path: None,
971            listener_state: None,
972            connection_state: default_connection_state(spec, state),
973            datagram_state: default_datagram_state(spec),
974        };
975        table.sockets.insert(socket_id, record.clone());
976        table
977            .by_owner
978            .entry(owner_pid)
979            .or_default()
980            .insert(socket_id);
981        Ok(record)
982    }
983
984    pub fn get(&self, socket_id: SocketId) -> Option<SocketRecord> {
985        lock_or_recover(&self.inner.state)
986            .sockets
987            .get(&socket_id)
988            .cloned()
989    }
990
991    pub fn reassign_owner(&self, socket_id: SocketId, owner_pid: u32) -> SocketResult<()> {
992        let mut table = lock_or_recover(&self.inner.state);
993        let previous_owner = table
994            .sockets
995            .get(&socket_id)
996            .ok_or_else(|| SocketTableError::not_found(socket_id))?
997            .owner_pid;
998        if previous_owner == owner_pid {
999            return Ok(());
1000        }
1001        if let Some(ids) = table.by_owner.get_mut(&previous_owner) {
1002            ids.remove(&socket_id);
1003            if ids.is_empty() {
1004                table.by_owner.remove(&previous_owner);
1005            }
1006        }
1007        table
1008            .by_owner
1009            .entry(owner_pid)
1010            .or_default()
1011            .insert(socket_id);
1012        table
1013            .sockets
1014            .get_mut(&socket_id)
1015            .expect("socket checked before owner reassignment")
1016            .owner_pid = owner_pid;
1017        Ok(())
1018    }
1019
1020    pub fn records_for_owner(&self, owner_pid: u32) -> Vec<SocketRecord> {
1021        let table = lock_or_recover(&self.inner.state);
1022        let Some(socket_ids) = table.by_owner.get(&owner_pid) else {
1023            return Vec::new();
1024        };
1025        socket_ids
1026            .iter()
1027            .filter_map(|socket_id| table.sockets.get(socket_id).cloned())
1028            .collect()
1029    }
1030
1031    pub fn update_state(
1032        &self,
1033        socket_id: SocketId,
1034        new_state: SocketState,
1035    ) -> SocketResult<SocketRecord> {
1036        let mut table = lock_or_recover(&self.inner.state);
1037        let record = table
1038            .sockets
1039            .get_mut(&socket_id)
1040            .ok_or_else(|| SocketTableError::not_found(socket_id))?;
1041        validate_state_transition(record.state, new_state)?;
1042        record.state = new_state;
1043        if new_state != SocketState::Listening {
1044            record.listener_state = None;
1045        }
1046        if new_state == SocketState::Connected && supports_connection_lifecycle(record.spec) {
1047            record
1048                .connection_state
1049                .get_or_insert_with(ConnectionState::default);
1050        } else if new_state != SocketState::Connected {
1051            record.connection_state = None;
1052        }
1053        Ok(record.clone())
1054    }
1055
1056    pub fn bind_inet(
1057        &self,
1058        socket_id: SocketId,
1059        address: InetSocketAddress,
1060    ) -> SocketResult<SocketRecord> {
1061        let mut address = normalize_inet_address(address);
1062        let mut table = lock_or_recover(&self.inner.state);
1063        let existing = table
1064            .sockets
1065            .get(&socket_id)
1066            .cloned()
1067            .ok_or_else(|| SocketTableError::not_found(socket_id))?;
1068        if !supports_inet_bind(existing.spec) {
1069            return Err(SocketTableError::invalid_argument(format!(
1070                "socket {socket_id} is not an INET socket"
1071            )));
1072        }
1073        // POSIX bind(port=0) assigns a free ephemeral port (kernel-owned, so the
1074        // converged browser path does not need a host port allocator).
1075        if address.port() == 0 {
1076            let port = assign_ephemeral_inet_port(&table, existing.spec, address.host())?;
1077            address = normalize_inet_address(InetSocketAddress::new(address.host(), port));
1078        }
1079        let conflicting_ids =
1080            lookup_conflicting_bound_inet_socket_ids(&table, existing.spec, &address);
1081        if has_incompatible_inet_bind_conflict(&table, &existing, &conflicting_ids) {
1082            return Err(SocketTableError::address_in_use(format!(
1083                "address {}:{} is already bound",
1084                address.host(),
1085                address.port()
1086            )));
1087        }
1088        let cloned = {
1089            let record = table
1090                .sockets
1091                .get_mut(&socket_id)
1092                .ok_or_else(|| SocketTableError::not_found(socket_id))?;
1093
1094            match record.state {
1095                SocketState::Created => {}
1096                SocketState::Bound if record.local_address.as_ref() == Some(&address) => {
1097                    return Ok(record.clone());
1098                }
1099                SocketState::Bound | SocketState::Listening | SocketState::Connected => {
1100                    return Err(SocketTableError::invalid_argument(format!(
1101                        "socket {socket_id} cannot bind in state {:?}",
1102                        record.state
1103                    )));
1104                }
1105            }
1106
1107            record.local_address = Some(address.clone());
1108            record.peer_address = None;
1109            record.local_unix_path = None;
1110            record.peer_unix_path = None;
1111            record.listener_state = None;
1112            record.connection_state = None;
1113            record.state = SocketState::Bound;
1114            record.clone()
1115        };
1116        register_bound_inet_socket(&mut table, cloned.spec, address, socket_id);
1117        Ok(cloned)
1118    }
1119
1120    pub fn set_datagram_socket_option(
1121        &self,
1122        socket_id: SocketId,
1123        option: DatagramSocketOption,
1124        enabled: bool,
1125    ) -> SocketResult<SocketRecord> {
1126        let mut table = lock_or_recover(&self.inner.state);
1127        let record = table
1128            .sockets
1129            .get_mut(&socket_id)
1130            .ok_or_else(|| SocketTableError::not_found(socket_id))?;
1131        let datagram_state = datagram_state_mut(record)?;
1132
1133        match option {
1134            DatagramSocketOption::ReuseAddr => datagram_state.reuse_addr = enabled,
1135            DatagramSocketOption::ReusePort => datagram_state.reuse_port = enabled,
1136            DatagramSocketOption::Broadcast => datagram_state.broadcast = enabled,
1137        }
1138
1139        Ok(record.clone())
1140    }
1141
1142    pub fn add_multicast_membership(
1143        &self,
1144        socket_id: SocketId,
1145        membership: SocketMulticastMembership,
1146    ) -> SocketResult<SocketRecord> {
1147        let mut table = lock_or_recover(&self.inner.state);
1148        let normalized_membership = {
1149            let record = table
1150                .sockets
1151                .get(&socket_id)
1152                .ok_or_else(|| SocketTableError::not_found(socket_id))?;
1153            validate_multicast_socket(record)?;
1154            normalize_multicast_membership(record.spec, membership)?
1155        };
1156
1157        let cloned = {
1158            let record = table
1159                .sockets
1160                .get_mut(&socket_id)
1161                .ok_or_else(|| SocketTableError::not_found(socket_id))?;
1162            let datagram_state = datagram_state_mut(record)?;
1163            datagram_state
1164                .multicast_memberships
1165                .insert(normalized_membership.clone());
1166            record.clone()
1167        };
1168
1169        table
1170            .multicast_groups
1171            .entry(normalized_membership)
1172            .or_default()
1173            .insert(socket_id);
1174        Ok(cloned)
1175    }
1176
1177    pub fn drop_multicast_membership(
1178        &self,
1179        socket_id: SocketId,
1180        membership: SocketMulticastMembership,
1181    ) -> SocketResult<SocketRecord> {
1182        let mut table = lock_or_recover(&self.inner.state);
1183        let normalized_membership = {
1184            let record = table
1185                .sockets
1186                .get(&socket_id)
1187                .ok_or_else(|| SocketTableError::not_found(socket_id))?;
1188            validate_multicast_socket(record)?;
1189            normalize_multicast_membership(record.spec, membership)?
1190        };
1191
1192        let cloned = {
1193            let record = table
1194                .sockets
1195                .get_mut(&socket_id)
1196                .ok_or_else(|| SocketTableError::not_found(socket_id))?;
1197            let datagram_state = datagram_state_mut(record)?;
1198            if !datagram_state
1199                .multicast_memberships
1200                .remove(&normalized_membership)
1201            {
1202                return Err(SocketTableError::address_not_available(format!(
1203                    "socket {socket_id} has not joined multicast group {}",
1204                    normalized_membership.group_address()
1205                )));
1206            }
1207            record.clone()
1208        };
1209
1210        if let Some(members) = table.multicast_groups.get_mut(&normalized_membership) {
1211            members.remove(&socket_id);
1212            if members.is_empty() {
1213                table.multicast_groups.remove(&normalized_membership);
1214            }
1215        }
1216
1217        Ok(cloned)
1218    }
1219
1220    pub fn bind_unix(
1221        &self,
1222        socket_id: SocketId,
1223        path: impl Into<String>,
1224    ) -> SocketResult<SocketRecord> {
1225        let path = normalize_unix_socket_path(path.into())?;
1226        let mut table = lock_or_recover(&self.inner.state);
1227        let existing = table
1228            .sockets
1229            .get(&socket_id)
1230            .cloned()
1231            .ok_or_else(|| SocketTableError::not_found(socket_id))?;
1232        if !supports_unix_stream_lifecycle(existing.spec) {
1233            return Err(SocketTableError::invalid_argument(format!(
1234                "socket {socket_id} is not a Unix stream socket"
1235            )));
1236        }
1237        let existing_id = table.bound_unix_streams.get(&path).copied();
1238        let cloned = {
1239            let record = table
1240                .sockets
1241                .get_mut(&socket_id)
1242                .ok_or_else(|| SocketTableError::not_found(socket_id))?;
1243
1244            if let Some(bound_socket_id) = existing_id {
1245                if bound_socket_id != socket_id {
1246                    return Err(SocketTableError::address_in_use(format!(
1247                        "path {path} is already bound"
1248                    )));
1249                }
1250            }
1251
1252            match record.state {
1253                SocketState::Created => {}
1254                SocketState::Bound if record.local_unix_path.as_deref() == Some(path.as_str()) => {
1255                    return Ok(record.clone());
1256                }
1257                SocketState::Bound | SocketState::Listening | SocketState::Connected => {
1258                    return Err(SocketTableError::invalid_argument(format!(
1259                        "socket {socket_id} cannot bind in state {:?}",
1260                        record.state
1261                    )));
1262                }
1263            }
1264
1265            record.local_address = None;
1266            record.peer_address = None;
1267            record.local_unix_path = Some(path.clone());
1268            record.peer_unix_path = None;
1269            record.listener_state = None;
1270            record.connection_state = None;
1271            record.state = SocketState::Bound;
1272            record.clone()
1273        };
1274        table.bound_unix_streams.insert(path, socket_id);
1275        Ok(cloned)
1276    }
1277
1278    pub fn listen(&self, socket_id: SocketId, backlog: usize) -> SocketResult<SocketRecord> {
1279        if backlog == 0 {
1280            return Err(SocketTableError::invalid_argument(
1281                "listener backlog must be greater than zero",
1282            ));
1283        }
1284
1285        let mut table = lock_or_recover(&self.inner.state);
1286        let record = table
1287            .sockets
1288            .get_mut(&socket_id)
1289            .ok_or_else(|| SocketTableError::not_found(socket_id))?;
1290
1291        if !supports_listener_lifecycle(record.spec) {
1292            return Err(SocketTableError::invalid_argument(format!(
1293                "socket {socket_id} is not a stream socket"
1294            )));
1295        }
1296        if record.state != SocketState::Bound || !has_bound_endpoint(record) {
1297            return Err(SocketTableError::invalid_argument(format!(
1298                "socket {socket_id} must be bound before listen"
1299            )));
1300        }
1301
1302        record.state = SocketState::Listening;
1303        record.listener_state = Some(ListenerState {
1304            backlog,
1305            pending_accepts: VecDeque::new(),
1306        });
1307        Ok(record.clone())
1308    }
1309
1310    pub fn enqueue_incoming_tcp_connection(
1311        &self,
1312        listener_socket_id: SocketId,
1313        peer_address: InetSocketAddress,
1314    ) -> SocketResult<()> {
1315        let readiness = {
1316            let mut table = lock_or_recover(&self.inner.state);
1317            let record = table
1318                .sockets
1319                .get_mut(&listener_socket_id)
1320                .ok_or_else(|| SocketTableError::not_found(listener_socket_id))?;
1321
1322            if record.state != SocketState::Listening {
1323                return Err(SocketTableError::invalid_argument(format!(
1324                    "socket {listener_socket_id} is not listening"
1325                )));
1326            }
1327
1328            let listener_state = record.listener_state.as_mut().ok_or_else(|| {
1329                SocketTableError::invalid_argument(format!(
1330                    "socket {listener_socket_id} has no listener state"
1331                ))
1332            })?;
1333
1334            if listener_state.pending_accepts.len() >= listener_state.backlog {
1335                return Err(SocketTableError::would_block(format!(
1336                    "listener {listener_socket_id} backlog is full"
1337                )));
1338            }
1339
1340            let was_empty = listener_state.pending_accepts.is_empty();
1341            listener_state.pending_accepts.push_back(PendingConnection {
1342                peer_address: Some(peer_address),
1343                peer_unix_path: None,
1344                accepted_socket_id: None,
1345            });
1346            was_empty.then_some(SocketReadiness {
1347                socket_id: listener_socket_id,
1348                kind: SocketReadinessKind::Accept,
1349            })
1350        };
1351        self.emit_readiness(readiness);
1352        Ok(())
1353    }
1354
1355    pub fn accept(&self, listener_socket_id: SocketId) -> SocketResult<SocketRecord> {
1356        let mut table = lock_or_recover(&self.inner.state);
1357        let (owner_pid, spec, local_address, local_unix_path, needs_socket_id) = {
1358            let record = table
1359                .sockets
1360                .get(&listener_socket_id)
1361                .ok_or_else(|| SocketTableError::not_found(listener_socket_id))?;
1362
1363            if record.state != SocketState::Listening {
1364                return Err(SocketTableError::invalid_argument(format!(
1365                    "socket {listener_socket_id} is not listening"
1366                )));
1367            }
1368
1369            let listener_state = record.listener_state.as_ref().ok_or_else(|| {
1370                SocketTableError::invalid_argument(format!(
1371                    "socket {listener_socket_id} has no listener state"
1372                ))
1373            })?;
1374            let pending = listener_state.pending_accepts.front().ok_or_else(|| {
1375                SocketTableError::would_block(format!(
1376                    "listener {listener_socket_id} has no pending connections"
1377                ))
1378            })?;
1379
1380            (
1381                record.owner_pid,
1382                record.spec,
1383                record.local_address.clone(),
1384                record.local_unix_path.clone(),
1385                pending.accepted_socket_id.is_none(),
1386            )
1387        };
1388        // External pending connections need a new kernel identity. Reserve it
1389        // before removing the backlog entry so exhaustion is a retryable,
1390        // non-destructive accept failure.
1391        let new_socket_id = needs_socket_id
1392            .then(|| next_socket_id(&mut table))
1393            .transpose()?;
1394        let pending = table
1395            .sockets
1396            .get_mut(&listener_socket_id)
1397            .and_then(|record| record.listener_state.as_mut())
1398            .and_then(|listener| listener.pending_accepts.pop_front())
1399            .ok_or_else(|| {
1400                SocketTableError::accounting_invariant(format!(
1401                    "listener {listener_socket_id} lost a pending accept during allocation"
1402                ))
1403            })?;
1404
1405        if let Some(accepted_socket_id) = pending.accepted_socket_id {
1406            return table
1407                .sockets
1408                .get(&accepted_socket_id)
1409                .cloned()
1410                .ok_or_else(|| SocketTableError::not_found(accepted_socket_id));
1411        }
1412
1413        let socket_id = new_socket_id.ok_or_else(|| {
1414            SocketTableError::accounting_invariant(format!(
1415                "listener {listener_socket_id} accepted an external connection without an id"
1416            ))
1417        })?;
1418        let record = SocketRecord {
1419            id: socket_id,
1420            owner_pid,
1421            spec,
1422            state: SocketState::Connected,
1423            local_address,
1424            peer_address: pending.peer_address,
1425            local_unix_path,
1426            peer_unix_path: pending.peer_unix_path,
1427            listener_state: None,
1428            connection_state: default_connection_state(spec, SocketState::Connected),
1429            datagram_state: default_datagram_state(spec),
1430        };
1431        table.sockets.insert(socket_id, record.clone());
1432        table
1433            .by_owner
1434            .entry(owner_pid)
1435            .or_default()
1436            .insert(socket_id);
1437        Ok(record)
1438    }
1439
1440    pub fn connect_pair(
1441        &self,
1442        socket_id: SocketId,
1443        peer_socket_id: SocketId,
1444    ) -> SocketResult<(SocketRecord, SocketRecord)> {
1445        if socket_id == peer_socket_id {
1446            return Err(SocketTableError::invalid_argument(
1447                "socket cannot connect to itself",
1448            ));
1449        }
1450
1451        let mut table = lock_or_recover(&self.inner.state);
1452        let mut socket = table
1453            .sockets
1454            .remove(&socket_id)
1455            .ok_or_else(|| SocketTableError::not_found(socket_id))?;
1456        let Some(mut peer) = table.sockets.remove(&peer_socket_id) else {
1457            table.sockets.insert(socket_id, socket);
1458            return Err(SocketTableError::not_found(peer_socket_id));
1459        };
1460
1461        if let Err(error) = validate_connect_pair(&socket, &peer) {
1462            table.sockets.insert(socket_id, socket);
1463            table.sockets.insert(peer_socket_id, peer);
1464            return Err(error);
1465        }
1466
1467        socket.state = SocketState::Connected;
1468        socket.peer_address = peer.local_address.clone();
1469        socket.peer_unix_path = peer.local_unix_path.clone();
1470        socket.listener_state = None;
1471        socket.connection_state = Some(ConnectionState {
1472            peer_socket_id: Some(peer_socket_id),
1473            ..ConnectionState::default()
1474        });
1475
1476        peer.state = SocketState::Connected;
1477        peer.peer_address = socket.local_address.clone();
1478        peer.peer_unix_path = socket.local_unix_path.clone();
1479        peer.listener_state = None;
1480        peer.connection_state = Some(ConnectionState {
1481            peer_socket_id: Some(socket_id),
1482            ..ConnectionState::default()
1483        });
1484
1485        let socket_clone = socket.clone();
1486        let peer_clone = peer.clone();
1487        table.sockets.insert(socket_id, socket);
1488        table.sockets.insert(peer_socket_id, peer);
1489        Ok((socket_clone, peer_clone))
1490    }
1491
1492    pub fn find_bound_inet_socket(
1493        &self,
1494        spec: SocketSpec,
1495        address: &InetSocketAddress,
1496    ) -> Option<SocketRecord> {
1497        let address = normalize_inet_address(address.clone());
1498        let table = lock_or_recover(&self.inner.state);
1499        let socket_id = lookup_bound_inet_socket(&table, spec, &address)?;
1500        table.sockets.get(&socket_id).cloned()
1501    }
1502
1503    pub fn connect_to_bound_inet_stream(
1504        &self,
1505        socket_id: SocketId,
1506        target_address: InetSocketAddress,
1507    ) -> SocketResult<()> {
1508        let target_address = normalize_inet_address(target_address);
1509        let (result, readiness) = {
1510            let mut table = lock_or_recover(&self.inner.state);
1511            let listener_socket_id =
1512                lookup_bound_inet_socket_in_table(&table.bound_inet_streams, &target_address)
1513                    .ok_or_else(|| {
1514                        SocketTableError::not_found_address(format!(
1515                            "no listening socket bound at {}:{}",
1516                            target_address.host(),
1517                            target_address.port()
1518                        ))
1519                    })?;
1520
1521            if socket_id == listener_socket_id {
1522                return Err(SocketTableError::invalid_argument(
1523                    "socket cannot connect to its own listening endpoint",
1524                ));
1525            }
1526
1527            let mut client = table
1528                .sockets
1529                .remove(&socket_id)
1530                .ok_or_else(|| SocketTableError::not_found(socket_id))?;
1531            let mut accept_was_empty = false;
1532            let result = (|| {
1533                // Validate the listener and confirm backlog capacity BEFORE consuming a
1534                // socket id. The id counter is monotonic and never
1535                // reclaims, so allocating an id before this check leaks one on every
1536                // rejected connect (for example when the backlog is full).
1537                {
1538                    let listener = table
1539                        .sockets
1540                        .get(&listener_socket_id)
1541                        .ok_or_else(|| SocketTableError::not_found(listener_socket_id))?;
1542                    validate_connect_to_listener(&client, listener)?;
1543
1544                    let listener_state = listener.listener_state.as_ref().ok_or_else(|| {
1545                        SocketTableError::invalid_argument(format!(
1546                            "socket {listener_socket_id} has no listener state"
1547                        ))
1548                    })?;
1549                    if listener_state.pending_accepts.len() >= listener_state.backlog {
1550                        return Err(SocketTableError::would_block(format!(
1551                            "listener {listener_socket_id} backlog is full"
1552                        )));
1553                    }
1554                }
1555
1556                // Capacity confirmed: only now is it safe to consume a socket id.
1557                let accepted_socket_id = next_socket_id(&mut table)?;
1558                let listener = table
1559                    .sockets
1560                    .get_mut(&listener_socket_id)
1561                    .ok_or_else(|| SocketTableError::not_found(listener_socket_id))?;
1562                let listener_state = listener.listener_state.as_mut().ok_or_else(|| {
1563                    SocketTableError::invalid_argument(format!(
1564                        "socket {listener_socket_id} has no listener state"
1565                    ))
1566                })?;
1567
1568                let accepted = SocketRecord {
1569                    id: accepted_socket_id,
1570                    owner_pid: listener.owner_pid,
1571                    spec: listener.spec,
1572                    state: SocketState::Connected,
1573                    local_address: listener.local_address.clone(),
1574                    peer_address: client.local_address.clone(),
1575                    local_unix_path: None,
1576                    peer_unix_path: None,
1577                    listener_state: None,
1578                    connection_state: Some(ConnectionState {
1579                        peer_socket_id: Some(socket_id),
1580                        ..ConnectionState::default()
1581                    }),
1582                    datagram_state: default_datagram_state(listener.spec),
1583                };
1584
1585                accept_was_empty = listener_state.pending_accepts.is_empty();
1586                listener_state.pending_accepts.push_back(PendingConnection {
1587                    peer_address: client.local_address.clone(),
1588                    peer_unix_path: None,
1589                    accepted_socket_id: Some(accepted_socket_id),
1590                });
1591
1592                client.state = SocketState::Connected;
1593                client.peer_address = listener.local_address.clone();
1594                client.peer_unix_path = None;
1595                client.listener_state = None;
1596                client.connection_state = Some(ConnectionState {
1597                    peer_socket_id: Some(accepted_socket_id),
1598                    ..ConnectionState::default()
1599                });
1600
1601                Ok(accepted)
1602            })();
1603
1604            let result = match result {
1605                Ok(accepted) => {
1606                    let accepted_socket_id = accepted.id;
1607                    table.sockets.insert(socket_id, client);
1608                    table.sockets.insert(accepted_socket_id, accepted.clone());
1609                    table
1610                        .by_owner
1611                        .entry(accepted.owner_pid)
1612                        .or_default()
1613                        .insert(accepted_socket_id);
1614                    Ok(())
1615                }
1616                Err(error) => {
1617                    table.sockets.insert(socket_id, client);
1618                    Err(error)
1619                }
1620            };
1621            let readiness = result.is_ok().then_some(()).and_then(|()| {
1622                accept_was_empty.then_some(SocketReadiness {
1623                    socket_id: listener_socket_id,
1624                    kind: SocketReadinessKind::Accept,
1625                })
1626            });
1627            (result, readiness)
1628        };
1629        self.emit_readiness(readiness);
1630        result
1631    }
1632
1633    pub fn find_bound_unix_socket(&self, path: &str) -> Option<SocketRecord> {
1634        let path = normalize_unix_socket_path(path).ok()?;
1635        let table = lock_or_recover(&self.inner.state);
1636        let socket_id = table.bound_unix_streams.get(&path).copied()?;
1637        table.sockets.get(&socket_id).cloned()
1638    }
1639
1640    pub fn connect_to_bound_unix_stream(
1641        &self,
1642        socket_id: SocketId,
1643        target_path: impl Into<String>,
1644    ) -> SocketResult<()> {
1645        let target_path = normalize_unix_socket_path(target_path.into())?;
1646        let (result, readiness) = {
1647            let mut table = lock_or_recover(&self.inner.state);
1648            let listener_socket_id = table
1649                .bound_unix_streams
1650                .get(&target_path)
1651                .copied()
1652                .ok_or_else(|| {
1653                    SocketTableError::not_found_address(format!(
1654                        "no listening socket bound at path {target_path}"
1655                    ))
1656                })?;
1657
1658            if socket_id == listener_socket_id {
1659                return Err(SocketTableError::invalid_argument(
1660                    "socket cannot connect to its own listening endpoint",
1661                ));
1662            }
1663
1664            let mut client = table
1665                .sockets
1666                .remove(&socket_id)
1667                .ok_or_else(|| SocketTableError::not_found(socket_id))?;
1668            let mut accept_was_empty = false;
1669            let result = (|| {
1670                // Validate the listener and confirm backlog capacity BEFORE consuming a
1671                // socket id. The id counter is monotonic and never
1672                // reclaims, so allocating an id before this check leaks one on every
1673                // rejected connect (for example when the backlog is full).
1674                {
1675                    let listener = table
1676                        .sockets
1677                        .get(&listener_socket_id)
1678                        .ok_or_else(|| SocketTableError::not_found(listener_socket_id))?;
1679                    validate_connect_to_listener(&client, listener)?;
1680
1681                    let listener_state = listener.listener_state.as_ref().ok_or_else(|| {
1682                        SocketTableError::invalid_argument(format!(
1683                            "socket {listener_socket_id} has no listener state"
1684                        ))
1685                    })?;
1686                    if listener_state.pending_accepts.len() >= listener_state.backlog {
1687                        return Err(SocketTableError::would_block(format!(
1688                            "listener {listener_socket_id} backlog is full"
1689                        )));
1690                    }
1691                }
1692
1693                // Capacity confirmed: only now is it safe to consume a socket id.
1694                let accepted_socket_id = next_socket_id(&mut table)?;
1695                let listener = table
1696                    .sockets
1697                    .get_mut(&listener_socket_id)
1698                    .ok_or_else(|| SocketTableError::not_found(listener_socket_id))?;
1699                let listener_state = listener.listener_state.as_mut().ok_or_else(|| {
1700                    SocketTableError::invalid_argument(format!(
1701                        "socket {listener_socket_id} has no listener state"
1702                    ))
1703                })?;
1704
1705                let accepted = SocketRecord {
1706                    id: accepted_socket_id,
1707                    owner_pid: listener.owner_pid,
1708                    spec: listener.spec,
1709                    state: SocketState::Connected,
1710                    local_address: None,
1711                    peer_address: None,
1712                    local_unix_path: listener.local_unix_path.clone(),
1713                    peer_unix_path: client.local_unix_path.clone(),
1714                    listener_state: None,
1715                    connection_state: Some(ConnectionState {
1716                        peer_socket_id: Some(socket_id),
1717                        ..ConnectionState::default()
1718                    }),
1719                    datagram_state: default_datagram_state(listener.spec),
1720                };
1721
1722                accept_was_empty = listener_state.pending_accepts.is_empty();
1723                listener_state.pending_accepts.push_back(PendingConnection {
1724                    peer_address: None,
1725                    peer_unix_path: client.local_unix_path.clone(),
1726                    accepted_socket_id: Some(accepted_socket_id),
1727                });
1728
1729                client.state = SocketState::Connected;
1730                client.peer_address = None;
1731                client.peer_unix_path = listener.local_unix_path.clone();
1732                client.listener_state = None;
1733                client.connection_state = Some(ConnectionState {
1734                    peer_socket_id: Some(accepted_socket_id),
1735                    ..ConnectionState::default()
1736                });
1737
1738                Ok(accepted)
1739            })();
1740
1741            let result = match result {
1742                Ok(accepted) => {
1743                    let accepted_socket_id = accepted.id;
1744                    table.sockets.insert(socket_id, client);
1745                    table.sockets.insert(accepted_socket_id, accepted.clone());
1746                    table
1747                        .by_owner
1748                        .entry(accepted.owner_pid)
1749                        .or_default()
1750                        .insert(accepted_socket_id);
1751                    Ok(())
1752                }
1753                Err(error) => {
1754                    table.sockets.insert(socket_id, client);
1755                    Err(error)
1756                }
1757            };
1758            let readiness = result.is_ok().then_some(()).and_then(|()| {
1759                accept_was_empty.then_some(SocketReadiness {
1760                    socket_id: listener_socket_id,
1761                    kind: SocketReadinessKind::Accept,
1762                })
1763            });
1764            (result, readiness)
1765        };
1766        self.emit_readiness(readiness);
1767        result
1768    }
1769
1770    pub fn send_to_bound_udp_socket(
1771        &self,
1772        socket_id: SocketId,
1773        target_address: InetSocketAddress,
1774        data: &[u8],
1775    ) -> SocketResult<usize> {
1776        let target_address = normalize_inet_address(target_address);
1777        let readiness = {
1778            let mut table = lock_or_recover(&self.inner.state);
1779            let sender = table
1780                .sockets
1781                .get(&socket_id)
1782                .cloned()
1783                .ok_or_else(|| SocketTableError::not_found(socket_id))?;
1784            validate_bound_udp_sender(&sender)?;
1785            let source_address = sender.local_address.as_ref().map(|source| {
1786                if source.host() == "0.0.0.0" || source.host() == "::" {
1787                    InetSocketAddress::new(target_address.host(), source.port())
1788                } else {
1789                    source.clone()
1790                }
1791            });
1792
1793            let receiver_socket_id = lookup_bound_inet_datagram_socket_in_table(
1794                &table.bound_inet_datagrams,
1795                &target_address,
1796            )
1797            .ok_or_else(|| {
1798                SocketTableError::not_found_address(format!(
1799                    "no UDP socket bound at {}:{}",
1800                    target_address.host(),
1801                    target_address.port()
1802                ))
1803            })?;
1804            let receiver = table
1805                .sockets
1806                .get_mut(&receiver_socket_id)
1807                .ok_or_else(|| SocketTableError::not_found(receiver_socket_id))?;
1808            validate_bound_udp_receiver(receiver)?;
1809
1810            // A connected UDP socket only admits datagrams from its selected
1811            // peer. The sender still observes a successful datagram send, as
1812            // it would when the network drops a packet before delivery.
1813            if receiver.peer_address.is_some()
1814                && receiver.peer_address.as_ref() != source_address.as_ref()
1815            {
1816                return Ok(data.len());
1817            }
1818
1819            let datagram_state = receiver.datagram_state.as_mut().ok_or_else(|| {
1820                SocketTableError::invalid_argument(format!(
1821                    "socket {receiver_socket_id} does not support datagrams"
1822                ))
1823            })?;
1824            #[cfg(not(target_arch = "wasm32"))]
1825            let retained = self.reserve_datagram(data.len())?;
1826            let was_empty = datagram_state.recv_queue.is_empty();
1827            datagram_state.recv_queue.push_back(QueuedDatagram {
1828                source_address,
1829                payload: data.to_vec(),
1830            });
1831            #[cfg(not(target_arch = "wasm32"))]
1832            if let Some((bytes, datagram, udp_bytes, udp_datagram)) = retained {
1833                let resources = table
1834                    .retained_resources
1835                    .entry(receiver_socket_id)
1836                    .or_default();
1837                resources.buffered_bytes.push_back(bytes);
1838                resources.datagrams.push_back(datagram);
1839                resources.udp_bytes.push_back(udp_bytes);
1840                resources.udp_datagrams.push_back(udp_datagram);
1841            }
1842            was_empty.then_some(SocketReadiness {
1843                socket_id: receiver_socket_id,
1844                kind: SocketReadinessKind::Data,
1845            })
1846        };
1847        self.emit_readiness(readiness);
1848        Ok(data.len())
1849    }
1850
1851    pub fn connect_bound_udp_socket(
1852        &self,
1853        socket_id: SocketId,
1854        peer_address: InetSocketAddress,
1855    ) -> SocketResult<()> {
1856        let mut table = lock_or_recover(&self.inner.state);
1857        let socket = table
1858            .sockets
1859            .get_mut(&socket_id)
1860            .ok_or_else(|| SocketTableError::not_found(socket_id))?;
1861        validate_bound_udp_sender(socket)?;
1862        if socket.peer_address.is_some() {
1863            return Err(SocketTableError::invalid_argument(format!(
1864                "UDP socket {socket_id} is already connected"
1865            )));
1866        }
1867        socket.peer_address = Some(normalize_inet_address(peer_address));
1868        Ok(())
1869    }
1870
1871    pub fn disconnect_bound_udp_socket(&self, socket_id: SocketId) -> SocketResult<()> {
1872        let mut table = lock_or_recover(&self.inner.state);
1873        let socket = table
1874            .sockets
1875            .get_mut(&socket_id)
1876            .ok_or_else(|| SocketTableError::not_found(socket_id))?;
1877        validate_bound_udp_sender(socket)?;
1878        if socket.peer_address.take().is_none() {
1879            return Err(SocketTableError::not_connected(format!(
1880                "UDP socket {socket_id} is not connected"
1881            )));
1882        }
1883        Ok(())
1884    }
1885
1886    pub fn send_connected_udp_socket(
1887        &self,
1888        socket_id: SocketId,
1889        data: &[u8],
1890    ) -> SocketResult<usize> {
1891        let peer_address = {
1892            let table = lock_or_recover(&self.inner.state);
1893            let socket = table
1894                .sockets
1895                .get(&socket_id)
1896                .ok_or_else(|| SocketTableError::not_found(socket_id))?;
1897            validate_bound_udp_sender(socket)?;
1898            socket.peer_address.clone().ok_or_else(|| {
1899                SocketTableError::not_connected(format!("UDP socket {socket_id} is not connected"))
1900            })?
1901        };
1902        self.send_to_bound_udp_socket(socket_id, peer_address, data)
1903    }
1904
1905    pub fn check_send_to_bound_udp_socket(
1906        &self,
1907        socket_id: SocketId,
1908        target_address: InetSocketAddress,
1909    ) -> SocketResult<()> {
1910        let target_address = normalize_inet_address(target_address);
1911        let table = lock_or_recover(&self.inner.state);
1912        let sender = table
1913            .sockets
1914            .get(&socket_id)
1915            .ok_or_else(|| SocketTableError::not_found(socket_id))?;
1916        validate_bound_udp_sender(sender)?;
1917
1918        let receiver_socket_id = lookup_bound_inet_datagram_socket_in_table(
1919            &table.bound_inet_datagrams,
1920            &target_address,
1921        )
1922        .ok_or_else(|| {
1923            SocketTableError::not_found_address(format!(
1924                "no UDP socket bound at {}:{}",
1925                target_address.host(),
1926                target_address.port()
1927            ))
1928        })?;
1929        let receiver = table
1930            .sockets
1931            .get(&receiver_socket_id)
1932            .ok_or_else(|| SocketTableError::not_found(receiver_socket_id))?;
1933        validate_bound_udp_receiver(receiver)?;
1934        Ok(())
1935    }
1936
1937    pub fn recv_datagram(
1938        &self,
1939        socket_id: SocketId,
1940        max_bytes: usize,
1941    ) -> SocketResult<Option<ReceivedDatagram>> {
1942        let (datagram, reservations) = self.recv_datagram_inner(socket_id, max_bytes)?;
1943        drop(reservations);
1944        Ok(datagram)
1945    }
1946
1947    #[cfg(not(target_arch = "wasm32"))]
1948    pub fn recv_datagram_charged(
1949        &self,
1950        socket_id: SocketId,
1951        max_bytes: usize,
1952    ) -> SocketResult<Option<ChargedReceivedDatagram>> {
1953        let (datagram, reservations) = self.recv_datagram_inner(socket_id, max_bytes)?;
1954        Ok(datagram.map(|datagram| ChargedReceivedDatagram {
1955            datagram,
1956            reservations,
1957        }))
1958    }
1959
1960    #[cfg(not(target_arch = "wasm32"))]
1961    fn recv_datagram_inner(
1962        &self,
1963        socket_id: SocketId,
1964        max_bytes: usize,
1965    ) -> SocketResult<(Option<ReceivedDatagram>, Option<DatagramReservations>)> {
1966        let mut table = lock_or_recover(&self.inner.state);
1967        let record = table
1968            .sockets
1969            .get(&socket_id)
1970            .ok_or_else(|| SocketTableError::not_found(socket_id))?;
1971        validate_bound_udp_receiver(record)?;
1972        let datagram_state = record.datagram_state.as_ref().ok_or_else(|| {
1973            SocketTableError::invalid_argument(format!(
1974                "socket {socket_id} does not support datagrams"
1975            ))
1976        })?;
1977        if datagram_state.recv_queue.is_empty() {
1978            return Err(SocketTableError::would_block(format!(
1979                "socket {socket_id} has no queued datagrams"
1980            )));
1981        }
1982        // Validate and transfer accounting ownership before removing the
1983        // payload. An internal ownership mismatch must not silently discard a
1984        // guest datagram and leave the queue/accountant out of sync.
1985        let reservations = if self.has_resource_ledger() {
1986            Some(take_retained_datagram(&mut table, socket_id)?)
1987        } else {
1988            None
1989        };
1990        let datagram = table
1991            .sockets
1992            .get_mut(&socket_id)
1993            .and_then(|record| record.datagram_state.as_mut())
1994            .and_then(|state| state.recv_queue.pop_front())
1995            .ok_or_else(|| {
1996                SocketTableError::accounting_invariant(format!(
1997                    "socket {socket_id} lost a datagram during charged transfer"
1998                ))
1999            })?;
2000        let mut payload = datagram.payload;
2001        // Truncate in place so the original backing allocation and its full
2002        // byte reservation move together. Copying a prefix here would require
2003        // a second reservation until the source allocation was released.
2004        payload.truncate(max_bytes);
2005        Ok((
2006            Some(ReceivedDatagram {
2007                source_address: datagram.source_address,
2008                payload,
2009            }),
2010            reservations,
2011        ))
2012    }
2013
2014    #[cfg(target_arch = "wasm32")]
2015    fn recv_datagram_inner(
2016        &self,
2017        socket_id: SocketId,
2018        max_bytes: usize,
2019    ) -> SocketResult<(Option<ReceivedDatagram>, Option<()>)> {
2020        let mut table = lock_or_recover(&self.inner.state);
2021        let record = table
2022            .sockets
2023            .get_mut(&socket_id)
2024            .ok_or_else(|| SocketTableError::not_found(socket_id))?;
2025        validate_bound_udp_receiver(record)?;
2026        let datagram_state = record.datagram_state.as_mut().ok_or_else(|| {
2027            SocketTableError::invalid_argument(format!(
2028                "socket {socket_id} does not support datagrams"
2029            ))
2030        })?;
2031        let Some(datagram) = datagram_state.recv_queue.pop_front() else {
2032            return Err(SocketTableError::would_block(format!(
2033                "socket {socket_id} has no queued datagrams"
2034            )));
2035        };
2036        let mut payload = datagram.payload;
2037        payload.truncate(max_bytes);
2038        Ok((
2039            Some(ReceivedDatagram {
2040                source_address: datagram.source_address,
2041                payload,
2042            }),
2043            None,
2044        ))
2045    }
2046
2047    pub fn poll(&self, socket_id: SocketId, requested: PollEvents) -> SocketResult<PollEvents> {
2048        let table = lock_or_recover(&self.inner.state);
2049        let record = table
2050            .sockets
2051            .get(&socket_id)
2052            .ok_or_else(|| SocketTableError::not_found(socket_id))?;
2053
2054        let mut events = PollEvents::empty();
2055        match record.state {
2056            SocketState::Listening => {
2057                if requested.intersects(POLLIN) && record.pending_accept_count() > 0 {
2058                    events |= POLLIN;
2059                }
2060            }
2061            SocketState::Connected => {
2062                let connection = record.connection_state.as_ref().ok_or_else(|| {
2063                    SocketTableError::not_connected(format!("socket {socket_id} is not connected"))
2064                })?;
2065                let peer = connection
2066                    .peer_socket_id
2067                    .and_then(|peer_socket_id| table.sockets.get(&peer_socket_id));
2068
2069                if requested.intersects(POLLIN) && connection.has_buffered_data() {
2070                    events |= POLLIN;
2071                }
2072                if connection.peer_write_shutdown || peer.is_none() {
2073                    events |= POLLHUP;
2074                }
2075
2076                if requested.intersects(POLLOUT) && !connection.write_shutdown {
2077                    if peer
2078                        .and_then(|peer| peer.connection_state.as_ref())
2079                        .map(|peer_connection| peer_connection.read_shutdown)
2080                        .unwrap_or(true)
2081                    {
2082                        events |= POLLERR;
2083                    } else {
2084                        events |= POLLOUT;
2085                    }
2086                }
2087            }
2088            SocketState::Bound if supports_inet_datagram_lifecycle(record.spec) => {
2089                let datagram_state = record.datagram_state.as_ref().ok_or_else(|| {
2090                    SocketTableError::invalid_argument(format!(
2091                        "socket {socket_id} does not support datagrams"
2092                    ))
2093                })?;
2094                if requested.intersects(POLLIN) && !datagram_state.recv_queue.is_empty() {
2095                    events |= POLLIN;
2096                }
2097                if requested.intersects(POLLOUT) {
2098                    events |= POLLOUT;
2099                }
2100            }
2101            SocketState::Created | SocketState::Bound => {}
2102        }
2103
2104        Ok(events)
2105    }
2106
2107    pub fn write(&self, socket_id: SocketId, data: &[u8]) -> SocketResult<usize> {
2108        let readiness = {
2109            let mut table = lock_or_recover(&self.inner.state);
2110            let record = table
2111                .sockets
2112                .get(&socket_id)
2113                .cloned()
2114                .ok_or_else(|| SocketTableError::not_found(socket_id))?;
2115            let connection = record.connection_state.as_ref().ok_or_else(|| {
2116                SocketTableError::not_connected(format!("socket {socket_id} is not connected"))
2117            })?;
2118            if record.state != SocketState::Connected {
2119                return Err(SocketTableError::not_connected(format!(
2120                    "socket {socket_id} is not connected"
2121                )));
2122            }
2123            if connection.write_shutdown {
2124                return Err(SocketTableError::broken_pipe(format!(
2125                    "socket {socket_id} write side is shut down"
2126                )));
2127            }
2128
2129            let peer_socket_id = connection.peer_socket_id.ok_or_else(|| {
2130                SocketTableError::broken_pipe(format!("socket {socket_id} peer is closed"))
2131            })?;
2132            let peer = table.sockets.get_mut(&peer_socket_id).ok_or_else(|| {
2133                SocketTableError::broken_pipe(format!("socket {socket_id} peer is closed"))
2134            })?;
2135            let peer_connection = peer.connection_state.as_mut().ok_or_else(|| {
2136                SocketTableError::broken_pipe(format!("socket {socket_id} peer is closed"))
2137            })?;
2138            if peer_connection.read_shutdown {
2139                return Err(SocketTableError::broken_pipe(format!(
2140                    "socket {peer_socket_id} read side is shut down"
2141                )));
2142            }
2143
2144            #[cfg(not(target_arch = "wasm32"))]
2145            let retained = self.reserve_buffered_bytes(data.len())?;
2146            let was_empty = !peer_connection.has_buffered_data();
2147            peer_connection.push_recv(data, Vec::new());
2148            #[cfg(not(target_arch = "wasm32"))]
2149            if let Some(retained) = retained {
2150                table
2151                    .retained_resources
2152                    .entry(peer_socket_id)
2153                    .or_default()
2154                    .buffered_bytes
2155                    .push_back(retained);
2156            }
2157            (was_empty && !data.is_empty()).then_some(SocketReadiness {
2158                socket_id: peer_socket_id,
2159                kind: SocketReadinessKind::Data,
2160            })
2161        };
2162        self.emit_readiness(readiness);
2163        Ok(data.len())
2164    }
2165
2166    pub fn send_message(
2167        &self,
2168        socket_id: SocketId,
2169        data: &[u8],
2170        rights: Vec<TransferredSocketRight>,
2171    ) -> SocketResult<usize> {
2172        let readiness = {
2173            let mut table = lock_or_recover(&self.inner.state);
2174            let record = table
2175                .sockets
2176                .get(&socket_id)
2177                .cloned()
2178                .ok_or_else(|| SocketTableError::not_found(socket_id))?;
2179            let connection = record.connection_state.as_ref().ok_or_else(|| {
2180                SocketTableError::not_connected(format!("socket {socket_id} is not connected"))
2181            })?;
2182            if record.state != SocketState::Connected {
2183                return Err(SocketTableError::not_connected(format!(
2184                    "socket {socket_id} is not connected"
2185                )));
2186            }
2187            if connection.write_shutdown {
2188                return Err(SocketTableError::broken_pipe(format!(
2189                    "socket {socket_id} write side is shut down"
2190                )));
2191            }
2192            if data.is_empty() && record.spec.socket_type == SocketType::Stream {
2193                return Ok(0);
2194            }
2195
2196            let peer_socket_id = connection.peer_socket_id.ok_or_else(|| {
2197                SocketTableError::broken_pipe(format!("socket {socket_id} peer is closed"))
2198            })?;
2199            let peer = table.sockets.get_mut(&peer_socket_id).ok_or_else(|| {
2200                SocketTableError::broken_pipe(format!("socket {socket_id} peer is closed"))
2201            })?;
2202            let peer_connection = peer.connection_state.as_mut().ok_or_else(|| {
2203                SocketTableError::broken_pipe(format!("socket {socket_id} peer is closed"))
2204            })?;
2205            if peer_connection.read_shutdown {
2206                return Err(SocketTableError::broken_pipe(format!(
2207                    "socket {peer_socket_id} read side is shut down"
2208                )));
2209            }
2210            let was_empty = !peer_connection.has_buffered_data();
2211            peer_connection.push_recv(data, rights);
2212            was_empty.then_some(SocketReadiness {
2213                socket_id: peer_socket_id,
2214                kind: SocketReadinessKind::Data,
2215            })
2216        };
2217        self.emit_readiness(readiness);
2218        Ok(data.len())
2219    }
2220
2221    pub fn check_write(&self, socket_id: SocketId) -> SocketResult<()> {
2222        let table = lock_or_recover(&self.inner.state);
2223        let record = table
2224            .sockets
2225            .get(&socket_id)
2226            .ok_or_else(|| SocketTableError::not_found(socket_id))?;
2227        let connection = record.connection_state.as_ref().ok_or_else(|| {
2228            SocketTableError::not_connected(format!("socket {socket_id} is not connected"))
2229        })?;
2230        if record.state != SocketState::Connected {
2231            return Err(SocketTableError::not_connected(format!(
2232                "socket {socket_id} is not connected"
2233            )));
2234        }
2235        if connection.write_shutdown {
2236            return Err(SocketTableError::broken_pipe(format!(
2237                "socket {socket_id} write side is shut down"
2238            )));
2239        }
2240
2241        let peer_socket_id = connection.peer_socket_id.ok_or_else(|| {
2242            SocketTableError::broken_pipe(format!("socket {socket_id} peer is closed"))
2243        })?;
2244        let peer = table.sockets.get(&peer_socket_id).ok_or_else(|| {
2245            SocketTableError::broken_pipe(format!("socket {socket_id} peer is closed"))
2246        })?;
2247        let peer_connection = peer.connection_state.as_ref().ok_or_else(|| {
2248            SocketTableError::broken_pipe(format!("socket {socket_id} peer is closed"))
2249        })?;
2250        if peer_connection.read_shutdown {
2251            return Err(SocketTableError::broken_pipe(format!(
2252                "socket {peer_socket_id} read side is shut down"
2253            )));
2254        }
2255
2256        Ok(())
2257    }
2258
2259    pub fn read(&self, socket_id: SocketId, max_bytes: usize) -> SocketResult<Option<Vec<u8>>> {
2260        if max_bytes == 0 {
2261            return Ok(Some(Vec::new()));
2262        }
2263
2264        let mut table = lock_or_recover(&self.inner.state);
2265        let record_ref = table
2266            .sockets
2267            .get(&socket_id)
2268            .ok_or_else(|| SocketTableError::not_found(socket_id))?;
2269        let clone_started = SOCKET_READ_TRACE_ENABLED
2270            .load(Ordering::Relaxed)
2271            .then(Instant::now);
2272        let record = record_ref.clone();
2273        if let Some(started) = clone_started {
2274            SOCKET_READ_TRACE_COUNTERS
2275                .socket_record_clone_calls
2276                .fetch_add(1, Ordering::Relaxed);
2277            SOCKET_READ_TRACE_COUNTERS.socket_record_clone_us.fetch_add(
2278                u64::try_from(started.elapsed().as_micros()).unwrap_or(u64::MAX),
2279                Ordering::Relaxed,
2280            );
2281        }
2282        if record.state != SocketState::Connected {
2283            return Err(SocketTableError::not_connected(format!(
2284                "socket {socket_id} is not connected"
2285            )));
2286        }
2287
2288        let connection = record.connection_state.as_ref().ok_or_else(|| {
2289            SocketTableError::not_connected(format!("socket {socket_id} is not connected"))
2290        })?;
2291        if connection.read_shutdown {
2292            return Ok(None);
2293        }
2294        if connection.has_buffered_data() {
2295            let record = table
2296                .sockets
2297                .get_mut(&socket_id)
2298                .ok_or_else(|| SocketTableError::not_found(socket_id))?;
2299            let connection = record.connection_state.as_mut().ok_or_else(|| {
2300                SocketTableError::not_connected(format!("socket {socket_id} is not connected"))
2301            })?;
2302            let result =
2303                connection.read_recv(max_bytes, record.spec.socket_type != SocketType::Stream);
2304            #[cfg(not(target_arch = "wasm32"))]
2305            if self.has_resource_ledger() {
2306                if let Some(read) = result.as_ref() {
2307                    release_retained_bytes(&mut table, socket_id, read.len())?;
2308                }
2309            }
2310            return Ok(result);
2311        }
2312
2313        let peer_open = connection
2314            .peer_socket_id
2315            .map(|peer_socket_id| table.sockets.contains_key(&peer_socket_id))
2316            .unwrap_or(false);
2317        if connection.peer_write_shutdown || !peer_open {
2318            return Ok(None);
2319        }
2320
2321        Err(SocketTableError::would_block(format!(
2322            "socket {socket_id} has no readable data"
2323        )))
2324    }
2325
2326    pub fn recv_message(
2327        &self,
2328        socket_id: SocketId,
2329        max_bytes: usize,
2330        peek: bool,
2331    ) -> SocketResult<Option<ReceivedSocketMessage>> {
2332        let mut table = lock_or_recover(&self.inner.state);
2333        let record = table
2334            .sockets
2335            .get_mut(&socket_id)
2336            .ok_or_else(|| SocketTableError::not_found(socket_id))?;
2337        if record.state != SocketState::Connected {
2338            return Err(SocketTableError::not_connected(format!(
2339                "socket {socket_id} is not connected"
2340            )));
2341        }
2342        let message_oriented = record.spec.socket_type != SocketType::Stream;
2343        let connection = record.connection_state.as_mut().ok_or_else(|| {
2344            SocketTableError::not_connected(format!("socket {socket_id} is not connected"))
2345        })?;
2346        if connection.read_shutdown {
2347            return Ok(None);
2348        }
2349        if connection.has_buffered_data() {
2350            return Ok(if peek {
2351                connection.peek_recv_message(max_bytes, message_oriented)
2352            } else {
2353                connection.read_recv_message(max_bytes, message_oriented)
2354            });
2355        }
2356        if connection.peer_write_shutdown {
2357            return Ok(None);
2358        }
2359        Err(SocketTableError::would_block(format!(
2360            "socket {socket_id} has no readable data"
2361        )))
2362    }
2363
2364    pub fn next_message_rights_count(&self, socket_id: SocketId) -> SocketResult<usize> {
2365        let table = lock_or_recover(&self.inner.state);
2366        let record = table
2367            .sockets
2368            .get(&socket_id)
2369            .ok_or_else(|| SocketTableError::not_found(socket_id))?;
2370        let connection = record.connection_state.as_ref().ok_or_else(|| {
2371            SocketTableError::not_connected(format!("socket {socket_id} is not connected"))
2372        })?;
2373        Ok(connection
2374            .recv_buffer
2375            .iter()
2376            .take(1)
2377            .map(|chunk| chunk.rights.len())
2378            .sum())
2379    }
2380
2381    pub fn shutdown(&self, socket_id: SocketId, how: SocketShutdown) -> SocketResult<SocketRecord> {
2382        let mut table = lock_or_recover(&self.inner.state);
2383        let record = table
2384            .sockets
2385            .remove(&socket_id)
2386            .ok_or_else(|| SocketTableError::not_found(socket_id))?;
2387
2388        if record.state != SocketState::Connected {
2389            table.sockets.insert(socket_id, record);
2390            return Err(SocketTableError::not_connected(format!(
2391                "socket {socket_id} is not connected"
2392            )));
2393        }
2394
2395        let Some(mut connection) = record.connection_state.clone() else {
2396            table.sockets.insert(socket_id, record);
2397            return Err(SocketTableError::not_connected(format!(
2398                "socket {socket_id} is not connected"
2399            )));
2400        };
2401
2402        if matches!(how, SocketShutdown::Read | SocketShutdown::Both) {
2403            connection.clear_recv();
2404            #[cfg(not(target_arch = "wasm32"))]
2405            release_all_retained_bytes(&mut table, socket_id);
2406            connection.read_shutdown = true;
2407        }
2408        if matches!(how, SocketShutdown::Write | SocketShutdown::Both) {
2409            connection.write_shutdown = true;
2410            if let Some(peer_socket_id) = connection.peer_socket_id {
2411                if let Some(peer) = table.sockets.get_mut(&peer_socket_id) {
2412                    if let Some(peer_connection) = peer.connection_state.as_mut() {
2413                        peer_connection.peer_write_shutdown = true;
2414                    }
2415                }
2416            }
2417        }
2418
2419        let mut record = record;
2420        record.connection_state = Some(connection);
2421        let cloned = record.clone();
2422        table.sockets.insert(socket_id, record);
2423        Ok(cloned)
2424    }
2425
2426    pub fn remove(&self, socket_id: SocketId) -> SocketResult<SocketRecord> {
2427        let (record, readiness) = {
2428            let mut table = lock_or_recover(&self.inner.state);
2429            let record = remove_socket(&mut table, socket_id)
2430                .ok_or_else(|| SocketTableError::not_found(socket_id))?;
2431            let readiness = record
2432                .connection_state
2433                .as_ref()
2434                .and_then(|connection| connection.peer_socket_id)
2435                .filter(|peer_socket_id| table.sockets.contains_key(peer_socket_id))
2436                .map(|peer_socket_id| SocketReadiness {
2437                    socket_id: peer_socket_id,
2438                    kind: SocketReadinessKind::Hangup,
2439                });
2440            (record, readiness)
2441        };
2442        self.emit_readiness(readiness);
2443        Ok(record)
2444    }
2445
2446    pub fn remove_all_for_pid(&self, owner_pid: u32) -> Vec<SocketRecord> {
2447        let mut table = lock_or_recover(&self.inner.state);
2448        let Some(socket_ids) = table.by_owner.remove(&owner_pid) else {
2449            return Vec::new();
2450        };
2451
2452        socket_ids
2453            .into_iter()
2454            .filter_map(|socket_id| remove_socket(&mut table, socket_id))
2455            .collect()
2456    }
2457
2458    pub fn snapshot(&self) -> SocketTableSnapshot {
2459        let table = lock_or_recover(&self.inner.state);
2460        let mut snapshot = SocketTableSnapshot {
2461            sockets: table.sockets.len(),
2462            ..SocketTableSnapshot::default()
2463        };
2464        for record in table.sockets.values() {
2465            if record.state.counts_as_listener() {
2466                snapshot.listeners += 1;
2467            }
2468            if record.state.counts_as_connection() {
2469                snapshot.connections += 1;
2470            }
2471            if let Some(connection) = &record.connection_state {
2472                snapshot.buffered_bytes = snapshot
2473                    .buffered_bytes
2474                    .saturating_add(connection.buffered_len());
2475                if record.spec.socket_type != SocketType::Stream {
2476                    snapshot.datagram_queue_len = snapshot
2477                        .datagram_queue_len
2478                        .saturating_add(connection.recv_buffer.len());
2479                }
2480            }
2481            if let Some(datagram_state) = &record.datagram_state {
2482                snapshot.datagram_queue_len = snapshot
2483                    .datagram_queue_len
2484                    .saturating_add(datagram_state.recv_queue.len());
2485                snapshot.buffered_bytes = snapshot
2486                    .buffered_bytes
2487                    .saturating_add(datagram_queue_bytes(&datagram_state.recv_queue));
2488            }
2489        }
2490        snapshot
2491    }
2492}
2493
2494#[cfg(not(target_arch = "wasm32"))]
2495fn release_retained_bytes(
2496    table: &mut SocketTableState,
2497    socket_id: SocketId,
2498    mut amount: usize,
2499) -> SocketResult<()> {
2500    if amount == 0 {
2501        return Ok(());
2502    }
2503    let resources = table
2504        .retained_resources
2505        .get_mut(&socket_id)
2506        .ok_or_else(|| {
2507            SocketTableError::accounting_invariant(format!(
2508                "socket {socket_id} released {amount} unowned buffered bytes"
2509            ))
2510        })?;
2511    let owned = resources
2512        .buffered_bytes
2513        .iter()
2514        .try_fold(0usize, |total, reservation| {
2515            total.checked_add(reservation.amount())
2516        })
2517        .ok_or_else(|| {
2518            SocketTableError::accounting_invariant(format!(
2519                "socket {socket_id} buffered-byte ownership overflowed"
2520            ))
2521        })?;
2522    if owned < amount {
2523        return Err(SocketTableError::accounting_invariant(format!(
2524            "socket {socket_id} released {amount} buffered bytes but owns only {owned}"
2525        )));
2526    }
2527    while amount > 0 {
2528        let reservation = resources.buffered_bytes.front_mut().ok_or_else(|| {
2529            SocketTableError::accounting_invariant(format!(
2530                "socket {socket_id} released more buffered bytes than it owns"
2531            ))
2532        })?;
2533        if reservation.amount() <= amount {
2534            amount -= reservation.amount();
2535            resources.buffered_bytes.pop_front();
2536        } else {
2537            let released = reservation.split(amount).ok_or_else(|| {
2538                SocketTableError::accounting_invariant(format!(
2539                    "socket {socket_id} could not split its buffered-byte reservation"
2540                ))
2541            })?;
2542            drop(released);
2543            amount = 0;
2544        }
2545    }
2546    if resources.buffered_bytes.is_empty()
2547        && resources.datagrams.is_empty()
2548        && resources.udp_bytes.is_empty()
2549        && resources.udp_datagrams.is_empty()
2550    {
2551        table.retained_resources.remove(&socket_id);
2552    }
2553    Ok(())
2554}
2555
2556#[cfg(not(target_arch = "wasm32"))]
2557fn take_retained_datagram(
2558    table: &mut SocketTableState,
2559    socket_id: SocketId,
2560) -> SocketResult<(Reservation, Reservation, Reservation, Reservation)> {
2561    let resources = table
2562        .retained_resources
2563        .get_mut(&socket_id)
2564        .ok_or_else(|| {
2565            SocketTableError::accounting_invariant(format!(
2566                "socket {socket_id} transferred an unowned datagram"
2567            ))
2568        })?;
2569    if resources.buffered_bytes.is_empty()
2570        || resources.datagrams.is_empty()
2571        || resources.udp_bytes.is_empty()
2572        || resources.udp_datagrams.is_empty()
2573    {
2574        return Err(SocketTableError::accounting_invariant(format!(
2575            "socket {socket_id} has incomplete datagram ownership"
2576        )));
2577    }
2578    let bytes = resources.buffered_bytes.pop_front().ok_or_else(|| {
2579        SocketTableError::accounting_invariant(format!(
2580            "socket {socket_id} transferred a datagram without buffered-byte ownership"
2581        ))
2582    })?;
2583    let datagram = resources.datagrams.pop_front().ok_or_else(|| {
2584        SocketTableError::accounting_invariant(format!(
2585            "socket {socket_id} transferred more datagrams than it owns"
2586        ))
2587    })?;
2588    let udp_bytes = resources.udp_bytes.pop_front().ok_or_else(|| {
2589        SocketTableError::accounting_invariant(format!(
2590            "socket {socket_id} transferred a datagram without UDP-byte ownership"
2591        ))
2592    })?;
2593    let udp_datagram = resources.udp_datagrams.pop_front().ok_or_else(|| {
2594        SocketTableError::accounting_invariant(format!(
2595            "socket {socket_id} transferred more UDP datagrams than it owns"
2596        ))
2597    })?;
2598    if resources.buffered_bytes.is_empty()
2599        && resources.datagrams.is_empty()
2600        && resources.udp_bytes.is_empty()
2601        && resources.udp_datagrams.is_empty()
2602    {
2603        table.retained_resources.remove(&socket_id);
2604    }
2605    Ok((bytes, datagram, udp_bytes, udp_datagram))
2606}
2607
2608#[cfg(not(target_arch = "wasm32"))]
2609fn release_all_retained_bytes(table: &mut SocketTableState, socket_id: SocketId) {
2610    let remove_entry = if let Some(resources) = table.retained_resources.get_mut(&socket_id) {
2611        resources.buffered_bytes.clear();
2612        resources.datagrams.is_empty()
2613            && resources.udp_bytes.is_empty()
2614            && resources.udp_datagrams.is_empty()
2615    } else {
2616        false
2617    };
2618    if remove_entry {
2619        table.retained_resources.remove(&socket_id);
2620    }
2621}
2622
2623fn datagram_queue_bytes(queue: &VecDeque<QueuedDatagram>) -> usize {
2624    queue
2625        .iter()
2626        .map(|datagram| datagram.payload.len())
2627        .sum::<usize>()
2628}
2629
2630fn next_socket_id(table: &mut SocketTableState) -> SocketResult<SocketId> {
2631    if table.next_socket_id == 0 {
2632        table.next_socket_id = 1;
2633    }
2634    let socket_id = table.next_socket_id;
2635    table.next_socket_id = table
2636        .next_socket_id
2637        .checked_add(1)
2638        .ok_or_else(SocketTableError::id_exhausted)?;
2639    Ok(socket_id)
2640}
2641
2642fn validate_state_transition(current: SocketState, next: SocketState) -> SocketResult<()> {
2643    if current == SocketState::Connected && next != SocketState::Connected {
2644        return Err(SocketTableError::invalid_argument(format!(
2645            "invalid socket state transition from {current:?} to {next:?}"
2646        )));
2647    }
2648    Ok(())
2649}
2650
2651fn validate_connect_pair(socket: &SocketRecord, peer: &SocketRecord) -> SocketResult<()> {
2652    if socket.spec != peer.spec {
2653        return Err(SocketTableError::invalid_argument(format!(
2654            "socket {} and peer {} have incompatible types",
2655            socket.id, peer.id
2656        )));
2657    }
2658    if !supports_connection_lifecycle(socket.spec) {
2659        return Err(SocketTableError::invalid_argument(format!(
2660            "socket {} does not support stream connections",
2661            socket.id
2662        )));
2663    }
2664    if !supports_connection_lifecycle(peer.spec) {
2665        return Err(SocketTableError::invalid_argument(format!(
2666            "socket {} does not support stream connections",
2667            peer.id
2668        )));
2669    }
2670    if !matches!(socket.state, SocketState::Created | SocketState::Bound) {
2671        return Err(SocketTableError::invalid_argument(format!(
2672            "socket {} cannot connect in state {:?}",
2673            socket.id, socket.state
2674        )));
2675    }
2676    if !matches!(peer.state, SocketState::Created | SocketState::Bound) {
2677        return Err(SocketTableError::invalid_argument(format!(
2678            "socket {} cannot connect in state {:?}",
2679            peer.id, peer.state
2680        )));
2681    }
2682    Ok(())
2683}
2684
2685fn default_connection_state(spec: SocketSpec, state: SocketState) -> Option<ConnectionState> {
2686    if state == SocketState::Connected && supports_connection_lifecycle(spec) {
2687        Some(ConnectionState::default())
2688    } else {
2689        None
2690    }
2691}
2692
2693fn default_datagram_state(spec: SocketSpec) -> Option<DatagramState> {
2694    if supports_inet_datagram_lifecycle(spec) {
2695        Some(DatagramState::default())
2696    } else {
2697        None
2698    }
2699}
2700
2701fn supports_connection_lifecycle(spec: SocketSpec) -> bool {
2702    matches!(spec.socket_type, SocketType::Stream)
2703        || (spec.domain == SocketDomain::Unix
2704            && matches!(
2705                spec.socket_type,
2706                SocketType::Datagram | SocketType::SeqPacket
2707            ))
2708}
2709
2710fn supports_listener_lifecycle(spec: SocketSpec) -> bool {
2711    matches!(spec.socket_type, SocketType::Stream)
2712        && matches!(
2713            spec.domain,
2714            SocketDomain::Inet | SocketDomain::Inet6 | SocketDomain::Unix
2715        )
2716}
2717
2718fn supports_inet_bind(spec: SocketSpec) -> bool {
2719    matches!(spec.domain, SocketDomain::Inet | SocketDomain::Inet6)
2720        && matches!(spec.socket_type, SocketType::Stream | SocketType::Datagram)
2721}
2722
2723fn supports_unix_stream_lifecycle(spec: SocketSpec) -> bool {
2724    matches!(spec.socket_type, SocketType::Stream) && matches!(spec.domain, SocketDomain::Unix)
2725}
2726
2727fn supports_inet_stream_lifecycle(spec: SocketSpec) -> bool {
2728    matches!(spec.socket_type, SocketType::Stream)
2729        && matches!(spec.domain, SocketDomain::Inet | SocketDomain::Inet6)
2730}
2731
2732fn supports_inet_datagram_lifecycle(spec: SocketSpec) -> bool {
2733    matches!(spec.socket_type, SocketType::Datagram)
2734        && matches!(spec.domain, SocketDomain::Inet | SocketDomain::Inet6)
2735}
2736
2737/// Pick a free ephemeral port for `bind(port=0)`, scanning the IANA dynamic
2738/// range and skipping any already in conflict for `spec`/`host`.
2739fn assign_ephemeral_inet_port(
2740    table: &SocketTableState,
2741    spec: SocketSpec,
2742    host: &str,
2743) -> SocketResult<u16> {
2744    const EPHEMERAL_START: u16 = 49152;
2745    const EPHEMERAL_END: u16 = 65535;
2746    for port in EPHEMERAL_START..=EPHEMERAL_END {
2747        let candidate = normalize_inet_address(InetSocketAddress::new(host, port));
2748        if lookup_conflicting_bound_inet_socket_ids(table, spec, &candidate).is_empty() {
2749            return Ok(port);
2750        }
2751    }
2752    Err(SocketTableError::address_in_use(
2753        "no free ephemeral port available",
2754    ))
2755}
2756
2757fn lookup_conflicting_bound_inet_socket_ids(
2758    table: &SocketTableState,
2759    spec: SocketSpec,
2760    address: &InetSocketAddress,
2761) -> Vec<SocketId> {
2762    if supports_inet_stream_lifecycle(spec) {
2763        table
2764            .bound_inet_streams
2765            .iter()
2766            .find_map(|(bound_address, socket_id)| {
2767                inet_stream_bind_addresses_overlap(bound_address, address).then_some(*socket_id)
2768            })
2769            .into_iter()
2770            .collect()
2771    } else if supports_inet_datagram_lifecycle(spec) {
2772        table
2773            .bound_inet_datagrams
2774            .iter()
2775            .filter(|(bound_address, _)| inet_stream_bind_addresses_overlap(bound_address, address))
2776            .flat_map(|(_, socket_ids)| socket_ids.iter().copied())
2777            .collect()
2778    } else {
2779        Vec::new()
2780    }
2781}
2782
2783fn lookup_bound_inet_socket(
2784    table: &SocketTableState,
2785    spec: SocketSpec,
2786    address: &InetSocketAddress,
2787) -> Option<SocketId> {
2788    if supports_inet_stream_lifecycle(spec) {
2789        lookup_bound_inet_socket_in_table(&table.bound_inet_streams, address)
2790    } else if supports_inet_datagram_lifecycle(spec) {
2791        lookup_bound_inet_datagram_socket_in_table(&table.bound_inet_datagrams, address)
2792    } else {
2793        None
2794    }
2795}
2796
2797fn inet_stream_bind_addresses_overlap(
2798    existing: &InetSocketAddress,
2799    requested: &InetSocketAddress,
2800) -> bool {
2801    if existing == requested {
2802        return true;
2803    }
2804
2805    wildcard_inet_address(existing).as_ref() == Some(requested)
2806        || wildcard_inet_address(requested).as_ref() == Some(existing)
2807}
2808
2809fn lookup_bound_inet_socket_in_table(
2810    sockets: &BTreeMap<InetSocketAddress, SocketId>,
2811    address: &InetSocketAddress,
2812) -> Option<SocketId> {
2813    sockets.get(address).copied().or_else(|| {
2814        wildcard_inet_address(address).and_then(|wildcard| sockets.get(&wildcard).copied())
2815    })
2816}
2817
2818fn lookup_bound_inet_datagram_socket_in_table(
2819    sockets: &BTreeMap<InetSocketAddress, BTreeSet<SocketId>>,
2820    address: &InetSocketAddress,
2821) -> Option<SocketId> {
2822    sockets
2823        .get(address)
2824        .and_then(|socket_ids| socket_ids.first().copied())
2825        .or_else(|| {
2826            wildcard_inet_address(address).and_then(|wildcard| {
2827                sockets
2828                    .get(&wildcard)
2829                    .and_then(|socket_ids| socket_ids.first().copied())
2830            })
2831        })
2832}
2833
2834fn register_bound_inet_socket(
2835    table: &mut SocketTableState,
2836    spec: SocketSpec,
2837    address: InetSocketAddress,
2838    socket_id: SocketId,
2839) {
2840    if supports_inet_stream_lifecycle(spec) {
2841        table.bound_inet_streams.insert(address, socket_id);
2842    } else if supports_inet_datagram_lifecycle(spec) {
2843        table
2844            .bound_inet_datagrams
2845            .entry(address)
2846            .or_default()
2847            .insert(socket_id);
2848    }
2849}
2850
2851fn validate_connect_to_listener(
2852    client: &SocketRecord,
2853    listener: &SocketRecord,
2854) -> SocketResult<()> {
2855    if !supports_connection_lifecycle(client.spec) {
2856        return Err(SocketTableError::invalid_argument(format!(
2857            "socket {} does not support stream connections",
2858            client.id
2859        )));
2860    }
2861    if !supports_listener_lifecycle(listener.spec) {
2862        return Err(SocketTableError::invalid_argument(format!(
2863            "socket {} is not a stream listener",
2864            listener.id
2865        )));
2866    }
2867    if !matches!(client.state, SocketState::Created | SocketState::Bound) {
2868        return Err(SocketTableError::invalid_argument(format!(
2869            "socket {} cannot connect in state {:?}",
2870            client.id, client.state
2871        )));
2872    }
2873    if listener.state != SocketState::Listening {
2874        return Err(SocketTableError::invalid_argument(format!(
2875            "socket {} is not listening",
2876            listener.id
2877        )));
2878    }
2879    Ok(())
2880}
2881
2882fn has_bound_endpoint(record: &SocketRecord) -> bool {
2883    record.local_address.is_some() || record.local_unix_path.is_some()
2884}
2885
2886fn validate_bound_udp_sender(sender: &SocketRecord) -> SocketResult<()> {
2887    if !supports_inet_datagram_lifecycle(sender.spec) {
2888        return Err(SocketTableError::invalid_argument(format!(
2889            "socket {} is not an INET datagram socket",
2890            sender.id
2891        )));
2892    }
2893    if sender.state != SocketState::Bound || sender.local_address.is_none() {
2894        return Err(SocketTableError::invalid_argument(format!(
2895            "socket {} must be bound before sending datagrams",
2896            sender.id
2897        )));
2898    }
2899    Ok(())
2900}
2901
2902fn validate_bound_udp_receiver(receiver: &SocketRecord) -> SocketResult<()> {
2903    if !supports_inet_datagram_lifecycle(receiver.spec) {
2904        return Err(SocketTableError::invalid_argument(format!(
2905            "socket {} is not an INET datagram socket",
2906            receiver.id
2907        )));
2908    }
2909    if receiver.state != SocketState::Bound || receiver.local_address.is_none() {
2910        return Err(SocketTableError::invalid_argument(format!(
2911            "socket {} must be bound to receive datagrams",
2912            receiver.id
2913        )));
2914    }
2915    Ok(())
2916}
2917
2918fn datagram_state_mut(record: &mut SocketRecord) -> SocketResult<&mut DatagramState> {
2919    if !supports_inet_datagram_lifecycle(record.spec) {
2920        return Err(SocketTableError::invalid_argument(format!(
2921            "socket {} is not an INET datagram socket",
2922            record.id
2923        )));
2924    }
2925    record.datagram_state.as_mut().ok_or_else(|| {
2926        SocketTableError::invalid_argument(format!(
2927            "socket {} does not support datagrams",
2928            record.id
2929        ))
2930    })
2931}
2932
2933fn validate_multicast_socket(record: &SocketRecord) -> SocketResult<()> {
2934    validate_bound_udp_receiver(record)?;
2935    if record.spec.domain != SocketDomain::Inet {
2936        return Err(SocketTableError::invalid_argument(format!(
2937            "socket {} multicast membership is only implemented for IPv4 datagrams",
2938            record.id
2939        )));
2940    }
2941    Ok(())
2942}
2943
2944fn normalize_multicast_membership(
2945    spec: SocketSpec,
2946    membership: SocketMulticastMembership,
2947) -> SocketResult<SocketMulticastMembership> {
2948    let group_address = membership.group_address.trim().to_ascii_lowercase();
2949    let interface_address = membership
2950        .interface_address
2951        .map(|value| value.trim().to_ascii_lowercase())
2952        .filter(|value| !value.is_empty());
2953
2954    match spec.domain {
2955        SocketDomain::Inet => {
2956            let parsed = group_address.parse::<Ipv4Addr>().map_err(|_| {
2957                SocketTableError::invalid_argument(format!(
2958                    "invalid IPv4 multicast address {group_address}"
2959                ))
2960            })?;
2961            if !parsed.is_multicast() {
2962                return Err(SocketTableError::invalid_argument(format!(
2963                    "address {group_address} is not an IPv4 multicast group"
2964                )));
2965            }
2966        }
2967        SocketDomain::Inet6 => {
2968            let parsed = group_address.parse::<Ipv6Addr>().map_err(|_| {
2969                SocketTableError::invalid_argument(format!(
2970                    "invalid IPv6 multicast address {group_address}"
2971                ))
2972            })?;
2973            if !parsed.is_multicast() {
2974                return Err(SocketTableError::invalid_argument(format!(
2975                    "address {group_address} is not an IPv6 multicast group"
2976                )));
2977            }
2978        }
2979        SocketDomain::Unix => {
2980            return Err(SocketTableError::invalid_argument(
2981                "unix sockets do not support multicast membership",
2982            ));
2983        }
2984    }
2985
2986    Ok(SocketMulticastMembership::new(
2987        group_address,
2988        interface_address,
2989    ))
2990}
2991
2992fn has_incompatible_inet_bind_conflict(
2993    table: &SocketTableState,
2994    record: &SocketRecord,
2995    conflicting_ids: &[SocketId],
2996) -> bool {
2997    conflicting_ids.iter().any(|conflicting_id| {
2998        if *conflicting_id == record.id {
2999            return false;
3000        }
3001
3002        let Some(existing) = table.sockets.get(conflicting_id) else {
3003            return false;
3004        };
3005
3006        if supports_inet_datagram_lifecycle(record.spec) {
3007            !inet_datagram_bind_shares_port(record, existing)
3008        } else {
3009            true
3010        }
3011    })
3012}
3013
3014fn inet_datagram_bind_shares_port(requested: &SocketRecord, existing: &SocketRecord) -> bool {
3015    (requested.reuse_port() && existing.reuse_port())
3016        || (requested.reuse_address() && existing.reuse_address())
3017}
3018
3019fn remove_socket(table: &mut SocketTableState, socket_id: SocketId) -> Option<SocketRecord> {
3020    let record = table.sockets.remove(&socket_id)?;
3021    #[cfg(not(target_arch = "wasm32"))]
3022    table.retained_resources.remove(&socket_id);
3023    unregister_bound_socket(table, &record);
3024    unregister_multicast_memberships(table, &record);
3025    if let Some(listener_state) = record.listener_state.as_ref() {
3026        let pending_socket_ids = listener_state
3027            .pending_accepts
3028            .iter()
3029            .filter_map(|pending| pending.accepted_socket_id)
3030            .collect::<Vec<_>>();
3031        for pending_socket_id in pending_socket_ids {
3032            let _ = remove_socket(table, pending_socket_id);
3033        }
3034    }
3035    if let Some(connection) = record.connection_state.as_ref() {
3036        if let Some(peer_socket_id) = connection.peer_socket_id {
3037            if let Some(peer) = table.sockets.get_mut(&peer_socket_id) {
3038                if let Some(peer_connection) = peer.connection_state.as_mut() {
3039                    if peer_connection.peer_socket_id == Some(socket_id) {
3040                        peer_connection.peer_socket_id = None;
3041                    }
3042                    peer_connection.peer_write_shutdown = true;
3043                }
3044            }
3045        }
3046    }
3047    if let Some(owner_sockets) = table.by_owner.get_mut(&record.owner_pid) {
3048        owner_sockets.remove(&socket_id);
3049        if owner_sockets.is_empty() {
3050            table.by_owner.remove(&record.owner_pid);
3051        }
3052    }
3053    Some(record)
3054}
3055
3056fn unregister_bound_socket(table: &mut SocketTableState, record: &SocketRecord) {
3057    let Some(address) = record.local_address.as_ref() else {
3058        if supports_unix_stream_lifecycle(record.spec) {
3059            if let Some(path) = record.local_unix_path.as_ref() {
3060                if table.bound_unix_streams.get(path).copied() == Some(record.id) {
3061                    table.bound_unix_streams.remove(path);
3062                }
3063            }
3064        }
3065        return;
3066    };
3067    if supports_inet_stream_lifecycle(record.spec)
3068        && table.bound_inet_streams.get(address).copied() == Some(record.id)
3069    {
3070        table.bound_inet_streams.remove(address);
3071    }
3072    if supports_inet_datagram_lifecycle(record.spec) {
3073        if let Some(socket_ids) = table.bound_inet_datagrams.get_mut(address) {
3074            socket_ids.remove(&record.id);
3075            if socket_ids.is_empty() {
3076                table.bound_inet_datagrams.remove(address);
3077            }
3078        }
3079    }
3080}
3081
3082fn unregister_multicast_memberships(table: &mut SocketTableState, record: &SocketRecord) {
3083    let Some(datagram_state) = record.datagram_state.as_ref() else {
3084        return;
3085    };
3086
3087    for membership in &datagram_state.multicast_memberships {
3088        if let Some(socket_ids) = table.multicast_groups.get_mut(membership) {
3089            socket_ids.remove(&record.id);
3090            if socket_ids.is_empty() {
3091                table.multicast_groups.remove(membership);
3092            }
3093        }
3094    }
3095}
3096
3097fn normalize_inet_address(address: InetSocketAddress) -> InetSocketAddress {
3098    match address.host().to_ascii_lowercase().as_str() {
3099        "localhost" => InetSocketAddress::new("127.0.0.1", address.port()),
3100        _ => address,
3101    }
3102}
3103
3104fn wildcard_inet_address(address: &InetSocketAddress) -> Option<InetSocketAddress> {
3105    match address.host() {
3106        "127.0.0.1" => Some(InetSocketAddress::new("0.0.0.0", address.port())),
3107        "::1" => Some(InetSocketAddress::new("::", address.port())),
3108        _ => None,
3109    }
3110}
3111
3112fn normalize_unix_socket_path(path: impl AsRef<str>) -> SocketResult<String> {
3113    let normalized = normalize_path(path.as_ref());
3114    if normalized == "/" {
3115        return Err(SocketTableError::invalid_argument(
3116            "unix socket path must not be empty or root",
3117        ));
3118    }
3119    Ok(normalized)
3120}
3121
3122fn lock_or_recover<'a, T>(mutex: &'a Mutex<T>) -> MutexGuard<'a, T> {
3123    match mutex.lock() {
3124        Ok(guard) => guard,
3125        Err(poisoned) => poisoned.into_inner(),
3126    }
3127}
3128
3129#[cfg(test)]
3130mod tests {
3131    use super::*;
3132    #[cfg(not(target_arch = "wasm32"))]
3133    use agentos_runtime::accounting::{ResourceLimit, ResourceUsage};
3134
3135    /// Reads the monotonic socket-id counter without advancing it, so a test can
3136    /// observe whether a code path consumed an id.
3137    fn peek_next_socket_id(table: &SocketTable) -> SocketId {
3138        lock_or_recover(&table.inner.state).next_socket_id
3139    }
3140
3141    #[test]
3142    fn exhausted_socket_ids_fail_without_reusing_a_live_identity() {
3143        let table = SocketTable::new();
3144        lock_or_recover(&table.inner.state).next_socket_id = u64::MAX;
3145
3146        let error = table
3147            .allocate(1, SocketSpec::tcp())
3148            .expect_err("exhausted id space must reject allocation");
3149
3150        assert_eq!(error.code(), "EMFILE");
3151        assert!(error
3152            .to_string()
3153            .contains("ERR_AGENTOS_SOCKET_ID_EXHAUSTED"));
3154        assert_eq!(table.snapshot().sockets, 0);
3155    }
3156
3157    #[test]
3158    fn exhausted_accept_preserves_the_pending_connection() {
3159        let table = SocketTable::new();
3160        let listener = table
3161            .allocate(1, SocketSpec::tcp())
3162            .expect("allocate listener");
3163        let address = InetSocketAddress::new("127.0.0.1", 43001);
3164        table
3165            .bind_inet(listener.id(), address)
3166            .expect("bind listener");
3167        table.listen(listener.id(), 1).expect("listen");
3168        table
3169            .enqueue_incoming_tcp_connection(
3170                listener.id(),
3171                InetSocketAddress::new("127.0.0.1", 43002),
3172            )
3173            .expect("queue incoming connection");
3174        lock_or_recover(&table.inner.state).next_socket_id = u64::MAX;
3175
3176        let error = table
3177            .accept(listener.id())
3178            .expect_err("exhausted id space must reject accept");
3179
3180        assert_eq!(error.code(), "EMFILE");
3181        assert_eq!(
3182            table
3183                .get(listener.id())
3184                .expect("listener remains live")
3185                .pending_accept_count(),
3186            1
3187        );
3188    }
3189
3190    #[test]
3191    fn full_backlog_unix_connect_does_not_consume_socket_id() {
3192        let table = SocketTable::new();
3193        let path = "/tmp/leak-test/server.sock";
3194
3195        let listener = table
3196            .allocate(1, SocketSpec::unix_stream())
3197            .expect("allocate listener");
3198        table
3199            .bind_unix(listener.id, path)
3200            .expect("bind unix listener");
3201        table.listen(listener.id, 1).expect("listen with backlog 1");
3202
3203        // Fill the only backlog slot with one pending connection.
3204        let first = table
3205            .allocate(2, SocketSpec::unix_stream())
3206            .expect("allocate first client");
3207        table
3208            .connect_to_bound_unix_stream(first.id, path)
3209            .expect("first connect fills the backlog");
3210
3211        // A second connect must be rejected because the backlog is full, and it
3212        // must NOT consume a socket id (the counter is monotonic and never reclaims).
3213        let second = table
3214            .allocate(2, SocketSpec::unix_stream())
3215            .expect("allocate second client");
3216        let before = peek_next_socket_id(&table);
3217        let error = table
3218            .connect_to_bound_unix_stream(second.id, path)
3219            .expect_err("full-backlog connect must fail");
3220        assert_eq!(error.code(), "EAGAIN");
3221        let after = peek_next_socket_id(&table);
3222
3223        assert_eq!(
3224            before, after,
3225            "full-backlog unix connect leaked a socket id (counter advanced from {before} to {after})"
3226        );
3227    }
3228
3229    #[test]
3230    fn full_backlog_inet_connect_does_not_consume_socket_id() {
3231        let table = SocketTable::new();
3232        let target = InetSocketAddress::new("127.0.0.1", 49222);
3233
3234        let listener = table
3235            .allocate(1, SocketSpec::tcp())
3236            .expect("allocate listener");
3237        table
3238            .bind_inet(listener.id, target.clone())
3239            .expect("bind inet listener");
3240        table.listen(listener.id, 1).expect("listen with backlog 1");
3241
3242        // Fill the only backlog slot with one pending connection.
3243        let first = table
3244            .allocate(2, SocketSpec::tcp())
3245            .expect("allocate first client");
3246        table
3247            .connect_to_bound_inet_stream(first.id, target.clone())
3248            .expect("first connect fills the backlog");
3249
3250        // A second connect must be rejected because the backlog is full, and it
3251        // must NOT consume a socket id (the counter is monotonic and never reclaims).
3252        let second = table
3253            .allocate(2, SocketSpec::tcp())
3254            .expect("allocate second client");
3255        let before = peek_next_socket_id(&table);
3256        let error = table
3257            .connect_to_bound_inet_stream(second.id, target)
3258            .expect_err("full-backlog connect must fail");
3259        assert_eq!(error.code(), "EAGAIN");
3260        let after = peek_next_socket_id(&table);
3261
3262        assert_eq!(
3263            before, after,
3264            "full-backlog inet connect leaked a socket id (counter advanced from {before} to {after})"
3265        );
3266    }
3267
3268    #[cfg(not(target_arch = "wasm32"))]
3269    fn test_resource_ledger(buffered_bytes: usize, datagrams: usize) -> Arc<ResourceLedger> {
3270        Arc::new(ResourceLedger::root(
3271            "test-vm",
3272            [
3273                (
3274                    ResourceClass::BufferedBytes,
3275                    ResourceLimit::new(buffered_bytes, "test.maxBufferedBytes"),
3276                ),
3277                (
3278                    ResourceClass::Datagrams,
3279                    ResourceLimit::new(datagrams, "test.maxDatagrams"),
3280                ),
3281                (
3282                    ResourceClass::UdpBytes,
3283                    ResourceLimit::new(buffered_bytes, "limits.udp.maxBufferedBytes"),
3284                ),
3285                (
3286                    ResourceClass::UdpDatagrams,
3287                    ResourceLimit::new(datagrams, "limits.udp.maxBufferedDatagrams"),
3288                ),
3289            ],
3290        ))
3291    }
3292
3293    #[cfg(not(target_arch = "wasm32"))]
3294    fn usage(ledger: &ResourceLedger, class: ResourceClass) -> ResourceUsage {
3295        ledger.usage(class)
3296    }
3297
3298    #[cfg(not(target_arch = "wasm32"))]
3299    #[test]
3300    fn stream_queue_reservations_follow_write_read_shutdown_and_close() {
3301        let table = SocketTable::new();
3302        let ledger = test_resource_ledger(5, 1);
3303        table
3304            .set_resource_ledger(Arc::clone(&ledger))
3305            .expect("install resource ledger");
3306
3307        let writer = table
3308            .allocate(1, SocketSpec::tcp())
3309            .expect("allocate writer");
3310        let reader = table
3311            .allocate(2, SocketSpec::tcp())
3312            .expect("allocate reader");
3313        table
3314            .connect_pair(writer.id(), reader.id())
3315            .expect("connect stream pair");
3316
3317        // Socket and connection counts belong to CapabilityRegistry, not the
3318        // kernel queue owner.
3319        assert_eq!(usage(&ledger, ResourceClass::Sockets).used, 0);
3320        assert_eq!(usage(&ledger, ResourceClass::Connections).used, 0);
3321
3322        table.write(writer.id(), b"12345").expect("fill queue");
3323        assert_eq!(usage(&ledger, ResourceClass::BufferedBytes).used, 5);
3324        assert!(!table.buffered_byte_capacity_available());
3325        let error = table
3326            .write(writer.id(), b"6")
3327            .expect_err("write beyond retained-byte limit must fail");
3328        assert_eq!(error.code(), "EAGAIN");
3329        assert_eq!(table.snapshot().buffered_bytes, 5);
3330        assert_eq!(usage(&ledger, ResourceClass::BufferedBytes).used, 5);
3331
3332        assert_eq!(
3333            table.read(reader.id(), 2).expect("partial read"),
3334            Some(b"12".to_vec())
3335        );
3336        assert_eq!(usage(&ledger, ResourceClass::BufferedBytes).used, 3);
3337        assert!(table.buffered_byte_capacity_available());
3338
3339        table
3340            .shutdown(reader.id(), SocketShutdown::Read)
3341            .expect("discard unread queue");
3342        assert_eq!(usage(&ledger, ResourceClass::BufferedBytes).used, 0);
3343
3344        table.remove(writer.id()).expect("close writer");
3345        table.remove(reader.id()).expect("close reader");
3346        assert!(ledger.is_zero());
3347    }
3348
3349    #[cfg(not(target_arch = "wasm32"))]
3350    #[test]
3351    fn retained_byte_mismatch_fails_before_releasing_owned_capacity() {
3352        let table = SocketTable::new();
3353        let ledger = test_resource_ledger(8, 1);
3354        table
3355            .set_resource_ledger(Arc::clone(&ledger))
3356            .expect("install resource ledger");
3357        let writer = table
3358            .allocate(1, SocketSpec::tcp())
3359            .expect("allocate writer");
3360        let reader = table
3361            .allocate(2, SocketSpec::tcp())
3362            .expect("allocate reader");
3363        table
3364            .connect_pair(writer.id(), reader.id())
3365            .expect("connect stream pair");
3366        table.write(writer.id(), b"abc").expect("queue bytes");
3367
3368        let mut state = lock_or_recover(&table.inner.state);
3369        let error = release_retained_bytes(&mut state, reader.id(), 4)
3370            .expect_err("over-release must fail atomically");
3371        assert_eq!(error.code(), "EIO");
3372        assert_eq!(
3373            state
3374                .retained_resources
3375                .get(&reader.id())
3376                .expect("retained ownership")
3377                .buffered_bytes
3378                .front()
3379                .expect("byte reservation")
3380                .amount(),
3381            3
3382        );
3383        drop(state);
3384        assert_eq!(usage(&ledger, ResourceClass::BufferedBytes).used, 3);
3385        assert_eq!(table.snapshot().buffered_bytes, 3);
3386    }
3387
3388    #[cfg(not(target_arch = "wasm32"))]
3389    #[test]
3390    fn datagram_reservations_are_atomic_and_release_full_truncated_payload() {
3391        let table = SocketTable::new();
3392        let ledger = test_resource_ledger(8, 1);
3393        table
3394            .set_resource_ledger(Arc::clone(&ledger))
3395            .expect("install resource ledger");
3396
3397        let sender = table
3398            .allocate(1, SocketSpec::udp())
3399            .expect("allocate sender");
3400        let receiver = table
3401            .allocate(2, SocketSpec::udp())
3402            .expect("allocate receiver");
3403        let sender_address = InetSocketAddress::new("127.0.0.1", 41001);
3404        let receiver_address = InetSocketAddress::new("127.0.0.1", 41002);
3405        table
3406            .bind_inet(sender.id(), sender_address)
3407            .expect("bind sender");
3408        table
3409            .bind_inet(receiver.id(), receiver_address.clone())
3410            .expect("bind receiver");
3411
3412        table
3413            .send_to_bound_udp_socket(sender.id(), receiver_address.clone(), b"abc")
3414            .expect("enqueue datagram");
3415        assert_eq!(usage(&ledger, ResourceClass::BufferedBytes).used, 3);
3416        assert_eq!(usage(&ledger, ResourceClass::Datagrams).used, 1);
3417        assert_eq!(usage(&ledger, ResourceClass::UdpBytes).used, 3);
3418        assert_eq!(usage(&ledger, ResourceClass::UdpDatagrams).used, 1);
3419        assert!(!table.datagram_capacity_available());
3420
3421        let error = table
3422            .send_to_bound_udp_socket(sender.id(), receiver_address.clone(), b"def")
3423            .expect_err("second datagram must fail atomically");
3424        assert_eq!(error.code(), "EAGAIN");
3425        assert_eq!(table.snapshot().datagram_queue_len, 1);
3426        assert_eq!(usage(&ledger, ResourceClass::BufferedBytes).used, 3);
3427        assert_eq!(usage(&ledger, ResourceClass::Datagrams).used, 1);
3428        assert_eq!(usage(&ledger, ResourceClass::UdpBytes).used, 3);
3429        assert_eq!(usage(&ledger, ResourceClass::UdpDatagrams).used, 1);
3430
3431        let received = table
3432            .recv_datagram(receiver.id(), 1)
3433            .expect("receive datagram")
3434            .expect("queued datagram");
3435        assert_eq!(received.payload(), b"a");
3436        assert_eq!(usage(&ledger, ResourceClass::BufferedBytes).used, 0);
3437        assert_eq!(usage(&ledger, ResourceClass::Datagrams).used, 0);
3438        assert_eq!(usage(&ledger, ResourceClass::UdpBytes).used, 0);
3439        assert_eq!(usage(&ledger, ResourceClass::UdpDatagrams).used, 0);
3440
3441        table
3442            .send_to_bound_udp_socket(sender.id(), receiver_address.clone(), b"charged")
3443            .expect("enqueue charged handoff");
3444        let charged = table
3445            .recv_datagram_charged(receiver.id(), 1)
3446            .expect("charged receive")
3447            .expect("charged datagram");
3448        assert_eq!(usage(&ledger, ResourceClass::BufferedBytes).used, 7);
3449        assert_eq!(usage(&ledger, ResourceClass::Datagrams).used, 1);
3450        assert_eq!(usage(&ledger, ResourceClass::UdpBytes).used, 7);
3451        assert_eq!(usage(&ledger, ResourceClass::UdpDatagrams).used, 1);
3452        let error = table
3453            .send_to_bound_udp_socket(sender.id(), receiver_address.clone(), b"blocked")
3454            .expect_err("guest-bound datagram ownership must retain the count permit");
3455        assert_eq!(error.code(), "EAGAIN");
3456        let (_, payload, reservations) = charged.into_parts();
3457        assert_eq!(payload, b"c");
3458        let (bytes, datagram, udp_bytes, udp_datagram) = reservations.expect("charged ownership");
3459        assert_eq!(bytes.amount(), 7);
3460        assert_eq!(datagram.amount(), 1);
3461        assert_eq!(udp_bytes.amount(), 7);
3462        assert_eq!(udp_datagram.amount(), 1);
3463        drop((bytes, datagram, udp_bytes, udp_datagram));
3464        assert_eq!(usage(&ledger, ResourceClass::BufferedBytes).used, 0);
3465        assert_eq!(usage(&ledger, ResourceClass::Datagrams).used, 0);
3466        assert_eq!(usage(&ledger, ResourceClass::UdpBytes).used, 0);
3467        assert_eq!(usage(&ledger, ResourceClass::UdpDatagrams).used, 0);
3468
3469        table
3470            .send_to_bound_udp_socket(sender.id(), receiver_address, b"queued")
3471            .expect("enqueue replacement datagram");
3472        table.remove(receiver.id()).expect("close queued receiver");
3473        assert_eq!(usage(&ledger, ResourceClass::BufferedBytes).used, 0);
3474        assert_eq!(usage(&ledger, ResourceClass::Datagrams).used, 0);
3475        assert_eq!(usage(&ledger, ResourceClass::UdpBytes).used, 0);
3476        assert_eq!(usage(&ledger, ResourceClass::UdpDatagrams).used, 0);
3477        table.remove(sender.id()).expect("close sender");
3478        assert!(ledger.is_zero());
3479    }
3480
3481    #[test]
3482    fn connected_udp_filters_non_peer_datagrams_and_disconnect_restores_delivery() {
3483        let table = SocketTable::new();
3484        let allowed_sender = table
3485            .allocate(1, SocketSpec::udp())
3486            .expect("allocate allowed sender");
3487        let other_sender = table
3488            .allocate(2, SocketSpec::udp())
3489            .expect("allocate other sender");
3490        let receiver = table
3491            .allocate(3, SocketSpec::udp())
3492            .expect("allocate receiver");
3493        let allowed_address = InetSocketAddress::new("127.0.0.1", 41101);
3494        let other_address = InetSocketAddress::new("127.0.0.1", 41102);
3495        let receiver_address = InetSocketAddress::new("127.0.0.1", 41103);
3496        table
3497            .bind_inet(allowed_sender.id(), allowed_address.clone())
3498            .expect("bind allowed sender");
3499        table
3500            .bind_inet(other_sender.id(), other_address)
3501            .expect("bind other sender");
3502        table
3503            .bind_inet(receiver.id(), receiver_address.clone())
3504            .expect("bind receiver");
3505
3506        table
3507            .connect_bound_udp_socket(receiver.id(), allowed_address.clone())
3508            .expect("connect receiver to allowed peer");
3509        assert_eq!(
3510            table
3511                .get(receiver.id())
3512                .expect("connected receiver")
3513                .peer_address(),
3514            Some(&allowed_address)
3515        );
3516
3517        assert_eq!(
3518            table
3519                .send_to_bound_udp_socket(other_sender.id(), receiver_address.clone(), b"drop")
3520                .expect("non-peer send still succeeds"),
3521            4
3522        );
3523        assert_eq!(
3524            table
3525                .recv_datagram(receiver.id(), usize::MAX)
3526                .expect_err("connected receiver must filter another peer")
3527                .code(),
3528            "EAGAIN"
3529        );
3530
3531        table
3532            .send_to_bound_udp_socket(allowed_sender.id(), receiver_address.clone(), b"allowed")
3533            .expect("send from connected peer");
3534        let received = table
3535            .recv_datagram(receiver.id(), usize::MAX)
3536            .expect("receive from connected peer")
3537            .expect("connected datagram");
3538        assert_eq!(received.payload(), b"allowed");
3539        assert_eq!(received.source_address(), Some(&allowed_address));
3540
3541        table
3542            .send_connected_udp_socket(receiver.id(), b"reply")
3543            .expect("send through connected peer state");
3544        let reply = table
3545            .recv_datagram(allowed_sender.id(), usize::MAX)
3546            .expect("receive connected reply")
3547            .expect("connected reply datagram");
3548        assert_eq!(reply.payload(), b"reply");
3549        assert_eq!(reply.source_address(), Some(&receiver_address));
3550
3551        table
3552            .disconnect_bound_udp_socket(receiver.id())
3553            .expect("disconnect receiver");
3554        assert!(table
3555            .get(receiver.id())
3556            .expect("disconnected receiver")
3557            .peer_address()
3558            .is_none());
3559        table
3560            .send_to_bound_udp_socket(other_sender.id(), receiver_address, b"accepted")
3561            .expect("send after disconnect");
3562        assert_eq!(
3563            table
3564                .recv_datagram(receiver.id(), usize::MAX)
3565                .expect("receive after disconnect")
3566                .expect("disconnected datagram")
3567                .payload(),
3568            b"accepted"
3569        );
3570    }
3571
3572    #[cfg(not(target_arch = "wasm32"))]
3573    #[test]
3574    fn incomplete_datagram_ownership_does_not_discard_payload() {
3575        let table = SocketTable::new();
3576        let ledger = test_resource_ledger(8, 1);
3577        table
3578            .set_resource_ledger(Arc::clone(&ledger))
3579            .expect("install resource ledger");
3580        let sender = table
3581            .allocate(1, SocketSpec::udp())
3582            .expect("allocate sender");
3583        let receiver = table
3584            .allocate(2, SocketSpec::udp())
3585            .expect("allocate receiver");
3586        let sender_address = InetSocketAddress::new("127.0.0.1", 42001);
3587        let receiver_address = InetSocketAddress::new("127.0.0.1", 42002);
3588        table
3589            .bind_inet(sender.id(), sender_address)
3590            .expect("bind sender");
3591        table
3592            .bind_inet(receiver.id(), receiver_address.clone())
3593            .expect("bind receiver");
3594        table
3595            .send_to_bound_udp_socket(sender.id(), receiver_address, b"abc")
3596            .expect("queue datagram");
3597
3598        // Simulate an internal ownership defect. Receiving must report it
3599        // without losing the guest-visible payload.
3600        let missing = lock_or_recover(&table.inner.state)
3601            .retained_resources
3602            .get_mut(&receiver.id())
3603            .expect("retained ownership")
3604            .udp_datagrams
3605            .pop_front()
3606            .expect("UDP datagram reservation");
3607        drop(missing);
3608        let error = table
3609            .recv_datagram(receiver.id(), usize::MAX)
3610            .expect_err("incomplete ownership must fail");
3611        assert_eq!(error.code(), "EIO");
3612        assert_eq!(table.snapshot().datagram_queue_len, 1);
3613        assert_eq!(table.snapshot().buffered_bytes, 3);
3614    }
3615
3616    #[cfg(not(target_arch = "wasm32"))]
3617    #[test]
3618    fn retained_queue_admission_charges_the_process_parent_atomically() {
3619        let process = Arc::new(ResourceLedger::root(
3620            "process",
3621            [(
3622                ResourceClass::BufferedBytes,
3623                ResourceLimit::new(3, "runtime.resources.maxBufferedBytes"),
3624            )],
3625        ));
3626        let vm = Arc::new(ResourceLedger::child(
3627            "vm",
3628            [(
3629                ResourceClass::BufferedBytes,
3630                ResourceLimit::new(5, "limits.resources.maxSocketBufferedBytes"),
3631            )],
3632            Arc::clone(&process),
3633        ));
3634        let table = SocketTable::new();
3635        table
3636            .set_resource_ledger(Arc::clone(&vm))
3637            .expect("install child ledger");
3638        let writer = table
3639            .allocate(1, SocketSpec::tcp())
3640            .expect("allocate writer");
3641        let reader = table
3642            .allocate(2, SocketSpec::tcp())
3643            .expect("allocate reader");
3644        table
3645            .connect_pair(writer.id(), reader.id())
3646            .expect("connect stream pair");
3647
3648        let error = table
3649            .write(writer.id(), b"1234")
3650            .expect_err("process aggregate must reject before queue mutation");
3651        assert_eq!(error.code(), "EAGAIN");
3652        assert_eq!(table.snapshot().buffered_bytes, 0);
3653        assert_eq!(usage(&process, ResourceClass::BufferedBytes).used, 0);
3654        assert_eq!(usage(&vm, ResourceClass::BufferedBytes).used, 0);
3655
3656        table
3657            .write(writer.id(), b"123")
3658            .expect("write within aggregate limit");
3659        assert_eq!(usage(&process, ResourceClass::BufferedBytes).used, 3);
3660        assert_eq!(usage(&vm, ResourceClass::BufferedBytes).used, 3);
3661        table.read(reader.id(), 3).expect("drain retained bytes");
3662        assert_eq!(usage(&process, ResourceClass::BufferedBytes).used, 0);
3663        assert_eq!(usage(&vm, ResourceClass::BufferedBytes).used, 0);
3664    }
3665}