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