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