Skip to main content

spin_factor_wasi/
sockets.rs

1//! Socket quota tracking and WASI socket host implementations.
2//!
3//! This module provides [`SocketPermitState`], [`SpinSocketsView`], and
4//! [`SpinSockets`] — the types needed to intercept WASI TCP/UDP socket
5//! creation and enforce a per-app cap on the number of concurrently open
6//! sockets.
7
8use std::{
9    collections::HashMap,
10    marker::PhantomData,
11    sync::{Arc, Mutex},
12};
13
14use spin_connection_semaphore::{ConnectionPermit, ConnectionSemaphore};
15use wasmtime::component::{HasData, Resource};
16use wasmtime_wasi::p2::bindings::sockets::tcp::{self as p2_tcp, IpSocketAddress, ShutdownType};
17use wasmtime_wasi::p2::bindings::sockets::tcp_create_socket as p2_tcp_create;
18use wasmtime_wasi::p2::bindings::sockets::udp as p2_udp;
19use wasmtime_wasi::p2::bindings::sockets::udp_create_socket as p2_udp_create;
20use wasmtime_wasi::p2::{DynInputStream, DynOutputStream, DynPollable};
21use wasmtime_wasi::p2::{TcpSocket, UdpSocket};
22use wasmtime_wasi::{
23    p2::bindings::sockets::network::{ErrorCode as SocketErrorCode, Host as NetworkHost, Network},
24    sockets::{WasiSockets, WasiSocketsCtxView},
25};
26
27/// Shared state for tracking per-socket semaphore permits. Permits are
28/// acquired when a socket is allocated and released when the socket resource
29/// is dropped.
30pub struct SocketPermitState {
31    semaphore: ConnectionSemaphore,
32    /// Active permits keyed by socket resource rep, released when the resource is dropped.
33    active: Mutex<HashMap<u32, ConnectionPermit>>,
34}
35
36impl SocketPermitState {
37    pub fn new(semaphore: ConnectionSemaphore) -> Arc<Self> {
38        Arc::new(Self {
39            semaphore,
40            active: Mutex::new(HashMap::new()),
41        })
42    }
43}
44
45/// A view over WASI socket state that carries an optional per-instance socket
46/// permit store, enabling per-connection quota tracking.
47pub struct SpinSocketsView<'a, T> {
48    pub(crate) inner: WasiSocketsCtxView<'a>,
49    pub(crate) permit_state: Option<Arc<SocketPermitState>>,
50    pub(crate) getter: fn(&mut T) -> WasiSocketsCtxView<'_>,
51}
52
53impl<'a, T> std::ops::Deref for SpinSocketsView<'a, T> {
54    type Target = WasiSocketsCtxView<'a>;
55    fn deref(&self) -> &Self::Target {
56        &self.inner
57    }
58}
59
60impl<T> std::ops::DerefMut for SpinSocketsView<'_, T> {
61    fn deref_mut(&mut self) -> &mut Self::Target {
62        &mut self.inner
63    }
64}
65
66/// [`HasData`] accessor for [`SpinSocketsView`], used in place of [`WasiSockets`]
67/// when registering TCP socket bindings so that socket creation and `drop` can
68/// participate in socket quota tracking.
69pub struct SpinSockets<T>(PhantomData<fn() -> T>);
70
71impl<T: 'static> HasData for SpinSockets<T> {
72    type Data<'a> = SpinSocketsView<'a, T>;
73}
74
75impl<'a, T> SpinSocketsView<'a, T> {
76    /// Consumes this view and returns the inner [`WasiSocketsCtxView`].
77    pub fn into_wasi(self) -> WasiSocketsCtxView<'a> {
78        self.inner
79    }
80}
81
82impl<T> SpinSocketsView<'_, T> {
83    /// Attempts to acquire a connection permit from the semaphore.
84    ///
85    /// Returns `Ok(None)` when no quota is configured, `Ok(Some(permit))` on
86    /// success, or `Err(())` when the quota is exhausted.
87    ///
88    /// The returned permit is unregistered — call [`Self::register_permit`] once
89    /// the socket resource rep is known to tie its lifetime to the socket.
90    pub(crate) fn try_acquire(&self) -> Result<Option<ConnectionPermit>, ()> {
91        let Some(state) = &self.permit_state else {
92            return Ok(None);
93        };
94        state.semaphore.try_acquire().map(Some).ok_or(())
95    }
96
97    /// Registers `permit` under `socket_rep` so it is held until the socket is
98    /// dropped. No-op when `permit` is `None` (no quota configured).
99    pub(crate) fn register_permit(&self, socket_rep: u32, permit: Option<ConnectionPermit>) {
100        let (Some(state), Some(permit)) = (&self.permit_state, permit) else {
101            return;
102        };
103        state
104            .active
105            .lock()
106            .unwrap_or_else(|e| e.into_inner())
107            .insert(socket_rep, permit);
108    }
109
110    /// Releases the connection permit for `socket_rep`, if any.
111    pub(crate) fn release_permit(&self, socket_rep: u32) {
112        if let Some(state) = &self.permit_state {
113            state
114                .active
115                .lock()
116                .unwrap_or_else(|e| e.into_inner())
117                .remove(&socket_rep);
118        }
119    }
120}
121
122impl<T> p2_tcp::Host for SpinSocketsView<'_, T> {}
123
124impl<T> p2_tcp::HostTcpSocket for SpinSocketsView<'_, T> {
125    async fn start_bind(
126        &mut self,
127        this: Resource<TcpSocket>,
128        network: Resource<Network>,
129        local_address: IpSocketAddress,
130    ) -> wasmtime_wasi::p2::SocketResult<()> {
131        p2_tcp::HostTcpSocket::start_bind(&mut self.inner, this, network, local_address).await
132    }
133
134    fn finish_bind(&mut self, this: Resource<TcpSocket>) -> wasmtime_wasi::p2::SocketResult<()> {
135        p2_tcp::HostTcpSocket::finish_bind(&mut self.inner, this)
136    }
137
138    fn start_connect(
139        &mut self,
140        this: Resource<TcpSocket>,
141        network: Resource<Network>,
142        remote_address: IpSocketAddress,
143    ) -> wasmtime_wasi::p2::SocketResult<()> {
144        p2_tcp::HostTcpSocket::start_connect(&mut self.inner, this, network, remote_address)
145    }
146
147    fn finish_connect(
148        &mut self,
149        this: Resource<TcpSocket>,
150    ) -> wasmtime_wasi::p2::SocketResult<(Resource<DynInputStream>, Resource<DynOutputStream>)>
151    {
152        p2_tcp::HostTcpSocket::finish_connect(&mut self.inner, this)
153    }
154
155    async fn start_listen(
156        &mut self,
157        this: Resource<TcpSocket>,
158    ) -> wasmtime_wasi::p2::SocketResult<()> {
159        p2_tcp::HostTcpSocket::start_listen(&mut self.inner, this).await
160    }
161
162    fn finish_listen(&mut self, this: Resource<TcpSocket>) -> wasmtime_wasi::p2::SocketResult<()> {
163        p2_tcp::HostTcpSocket::finish_listen(&mut self.inner, this)
164    }
165
166    fn accept(
167        &mut self,
168        this: Resource<TcpSocket>,
169    ) -> wasmtime_wasi::p2::SocketResult<(
170        Resource<TcpSocket>,
171        Resource<DynInputStream>,
172        Resource<DynOutputStream>,
173    )> {
174        p2_tcp::HostTcpSocket::accept(&mut self.inner, this)
175    }
176
177    fn local_address(
178        &mut self,
179        this: Resource<TcpSocket>,
180    ) -> wasmtime_wasi::p2::SocketResult<IpSocketAddress> {
181        p2_tcp::HostTcpSocket::local_address(&mut self.inner, this)
182    }
183
184    fn remote_address(
185        &mut self,
186        this: Resource<TcpSocket>,
187    ) -> wasmtime_wasi::p2::SocketResult<IpSocketAddress> {
188        p2_tcp::HostTcpSocket::remote_address(&mut self.inner, this)
189    }
190
191    fn is_listening(&mut self, this: Resource<TcpSocket>) -> wasmtime::Result<bool> {
192        p2_tcp::HostTcpSocket::is_listening(&mut self.inner, this)
193    }
194
195    fn address_family(
196        &mut self,
197        this: Resource<TcpSocket>,
198    ) -> wasmtime::Result<wasmtime_wasi::p2::bindings::sockets::network::IpAddressFamily> {
199        p2_tcp::HostTcpSocket::address_family(&mut self.inner, this)
200    }
201
202    fn set_listen_backlog_size(
203        &mut self,
204        this: Resource<TcpSocket>,
205        value: u64,
206    ) -> wasmtime_wasi::p2::SocketResult<()> {
207        p2_tcp::HostTcpSocket::set_listen_backlog_size(&mut self.inner, this, value)
208    }
209
210    fn keep_alive_enabled(
211        &mut self,
212        this: Resource<TcpSocket>,
213    ) -> wasmtime_wasi::p2::SocketResult<bool> {
214        p2_tcp::HostTcpSocket::keep_alive_enabled(&mut self.inner, this)
215    }
216
217    fn set_keep_alive_enabled(
218        &mut self,
219        this: Resource<TcpSocket>,
220        value: bool,
221    ) -> wasmtime_wasi::p2::SocketResult<()> {
222        p2_tcp::HostTcpSocket::set_keep_alive_enabled(&mut self.inner, this, value)
223    }
224
225    fn keep_alive_idle_time(
226        &mut self,
227        this: Resource<TcpSocket>,
228    ) -> wasmtime_wasi::p2::SocketResult<u64> {
229        p2_tcp::HostTcpSocket::keep_alive_idle_time(&mut self.inner, this)
230    }
231
232    fn set_keep_alive_idle_time(
233        &mut self,
234        this: Resource<TcpSocket>,
235        value: u64,
236    ) -> wasmtime_wasi::p2::SocketResult<()> {
237        p2_tcp::HostTcpSocket::set_keep_alive_idle_time(&mut self.inner, this, value)
238    }
239
240    fn keep_alive_interval(
241        &mut self,
242        this: Resource<TcpSocket>,
243    ) -> wasmtime_wasi::p2::SocketResult<u64> {
244        p2_tcp::HostTcpSocket::keep_alive_interval(&mut self.inner, this)
245    }
246
247    fn set_keep_alive_interval(
248        &mut self,
249        this: Resource<TcpSocket>,
250        value: u64,
251    ) -> wasmtime_wasi::p2::SocketResult<()> {
252        p2_tcp::HostTcpSocket::set_keep_alive_interval(&mut self.inner, this, value)
253    }
254
255    fn keep_alive_count(
256        &mut self,
257        this: Resource<TcpSocket>,
258    ) -> wasmtime_wasi::p2::SocketResult<u32> {
259        p2_tcp::HostTcpSocket::keep_alive_count(&mut self.inner, this)
260    }
261
262    fn set_keep_alive_count(
263        &mut self,
264        this: Resource<TcpSocket>,
265        value: u32,
266    ) -> wasmtime_wasi::p2::SocketResult<()> {
267        p2_tcp::HostTcpSocket::set_keep_alive_count(&mut self.inner, this, value)
268    }
269
270    fn hop_limit(&mut self, this: Resource<TcpSocket>) -> wasmtime_wasi::p2::SocketResult<u8> {
271        p2_tcp::HostTcpSocket::hop_limit(&mut self.inner, this)
272    }
273
274    fn set_hop_limit(
275        &mut self,
276        this: Resource<TcpSocket>,
277        value: u8,
278    ) -> wasmtime_wasi::p2::SocketResult<()> {
279        p2_tcp::HostTcpSocket::set_hop_limit(&mut self.inner, this, value)
280    }
281
282    fn receive_buffer_size(
283        &mut self,
284        this: Resource<TcpSocket>,
285    ) -> wasmtime_wasi::p2::SocketResult<u64> {
286        p2_tcp::HostTcpSocket::receive_buffer_size(&mut self.inner, this)
287    }
288
289    fn set_receive_buffer_size(
290        &mut self,
291        this: Resource<TcpSocket>,
292        value: u64,
293    ) -> wasmtime_wasi::p2::SocketResult<()> {
294        p2_tcp::HostTcpSocket::set_receive_buffer_size(&mut self.inner, this, value)
295    }
296
297    fn send_buffer_size(
298        &mut self,
299        this: Resource<TcpSocket>,
300    ) -> wasmtime_wasi::p2::SocketResult<u64> {
301        p2_tcp::HostTcpSocket::send_buffer_size(&mut self.inner, this)
302    }
303
304    fn set_send_buffer_size(
305        &mut self,
306        this: Resource<TcpSocket>,
307        value: u64,
308    ) -> wasmtime_wasi::p2::SocketResult<()> {
309        p2_tcp::HostTcpSocket::set_send_buffer_size(&mut self.inner, this, value)
310    }
311
312    fn subscribe(&mut self, this: Resource<TcpSocket>) -> wasmtime::Result<Resource<DynPollable>> {
313        p2_tcp::HostTcpSocket::subscribe(&mut self.inner, this)
314    }
315
316    fn shutdown(
317        &mut self,
318        this: Resource<TcpSocket>,
319        shutdown_type: ShutdownType,
320    ) -> wasmtime_wasi::p2::SocketResult<()> {
321        p2_tcp::HostTcpSocket::shutdown(&mut self.inner, this, shutdown_type)
322    }
323
324    fn drop(&mut self, this: Resource<TcpSocket>) -> wasmtime::Result<()> {
325        self.release_permit(this.rep());
326        p2_tcp::HostTcpSocket::drop(&mut self.inner, this)
327    }
328}
329
330impl<T> NetworkHost for SpinSocketsView<'_, T> {
331    fn convert_error_code(
332        &mut self,
333        error: wasmtime_wasi::p2::SocketError,
334    ) -> wasmtime::Result<wasmtime_wasi::p2::bindings::sockets::network::ErrorCode> {
335        NetworkHost::convert_error_code(&mut self.inner, error)
336    }
337
338    fn network_error_code(
339        &mut self,
340        err: Resource<wasmtime::Error>,
341    ) -> wasmtime::Result<Option<wasmtime_wasi::p2::bindings::sockets::network::ErrorCode>> {
342        NetworkHost::network_error_code(&mut self.inner, err)
343    }
344}
345
346impl<T> wasmtime_wasi::p2::bindings::sockets::network::HostNetwork for SpinSocketsView<'_, T> {
347    fn drop(&mut self, this: Resource<Network>) -> wasmtime::Result<()> {
348        wasmtime_wasi::p2::bindings::sockets::network::HostNetwork::drop(&mut self.inner, this)
349    }
350}
351
352impl<T> p2_tcp_create::Host for SpinSocketsView<'_, T> {
353    fn create_tcp_socket(
354        &mut self,
355        address_family: wasmtime_wasi::p2::bindings::sockets::network::IpAddressFamily,
356    ) -> wasmtime_wasi::p2::SocketResult<Resource<TcpSocket>> {
357        // Unlike outbound HTTP, socket creation fails immediately when the
358        // quota is full. Waiting could deadlock a guest that keeps sockets open.
359        let Ok(permit) = self.try_acquire() else {
360            tracing::warn!("TCP socket creation refused: connection quota exhausted");
361            return Err(SocketErrorCode::NewSocketLimit.into());
362        };
363        let socket = p2_tcp_create::Host::create_tcp_socket(&mut self.inner, address_family)?;
364        self.register_permit(socket.rep(), permit);
365        Ok(socket)
366    }
367}
368
369impl<T> p2_udp::Host for SpinSocketsView<'_, T> {}
370
371impl<T> p2_udp::HostUdpSocket for SpinSocketsView<'_, T> {
372    async fn start_bind(
373        &mut self,
374        this: Resource<p2_udp::UdpSocket>,
375        network: Resource<p2_udp::Network>,
376        local_address: p2_udp::IpSocketAddress,
377    ) -> wasmtime_wasi::p2::SocketResult<()> {
378        p2_udp::HostUdpSocket::start_bind(&mut self.inner, this, network, local_address).await
379    }
380
381    fn finish_bind(
382        &mut self,
383        this: Resource<p2_udp::UdpSocket>,
384    ) -> wasmtime_wasi::p2::SocketResult<()> {
385        p2_udp::HostUdpSocket::finish_bind(&mut self.inner, this)
386    }
387
388    async fn stream(
389        &mut self,
390        this: Resource<p2_udp::UdpSocket>,
391        remote_address: Option<p2_udp::IpSocketAddress>,
392    ) -> wasmtime_wasi::p2::SocketResult<(
393        Resource<p2_udp::IncomingDatagramStream>,
394        Resource<p2_udp::OutgoingDatagramStream>,
395    )> {
396        p2_udp::HostUdpSocket::stream(&mut self.inner, this, remote_address).await
397    }
398
399    fn local_address(
400        &mut self,
401        this: Resource<p2_udp::UdpSocket>,
402    ) -> wasmtime_wasi::p2::SocketResult<p2_udp::IpSocketAddress> {
403        p2_udp::HostUdpSocket::local_address(&mut self.inner, this)
404    }
405
406    fn remote_address(
407        &mut self,
408        this: Resource<p2_udp::UdpSocket>,
409    ) -> wasmtime_wasi::p2::SocketResult<p2_udp::IpSocketAddress> {
410        p2_udp::HostUdpSocket::remote_address(&mut self.inner, this)
411    }
412
413    fn address_family(
414        &mut self,
415        this: Resource<p2_udp::UdpSocket>,
416    ) -> wasmtime::Result<p2_udp::IpAddressFamily> {
417        p2_udp::HostUdpSocket::address_family(&mut self.inner, this)
418    }
419
420    fn unicast_hop_limit(
421        &mut self,
422        this: Resource<p2_udp::UdpSocket>,
423    ) -> wasmtime_wasi::p2::SocketResult<u8> {
424        p2_udp::HostUdpSocket::unicast_hop_limit(&mut self.inner, this)
425    }
426
427    fn set_unicast_hop_limit(
428        &mut self,
429        this: Resource<p2_udp::UdpSocket>,
430        value: u8,
431    ) -> wasmtime_wasi::p2::SocketResult<()> {
432        p2_udp::HostUdpSocket::set_unicast_hop_limit(&mut self.inner, this, value)
433    }
434
435    fn receive_buffer_size(
436        &mut self,
437        this: Resource<p2_udp::UdpSocket>,
438    ) -> wasmtime_wasi::p2::SocketResult<u64> {
439        p2_udp::HostUdpSocket::receive_buffer_size(&mut self.inner, this)
440    }
441
442    fn set_receive_buffer_size(
443        &mut self,
444        this: Resource<p2_udp::UdpSocket>,
445        value: u64,
446    ) -> wasmtime_wasi::p2::SocketResult<()> {
447        p2_udp::HostUdpSocket::set_receive_buffer_size(&mut self.inner, this, value)
448    }
449
450    fn send_buffer_size(
451        &mut self,
452        this: Resource<p2_udp::UdpSocket>,
453    ) -> wasmtime_wasi::p2::SocketResult<u64> {
454        p2_udp::HostUdpSocket::send_buffer_size(&mut self.inner, this)
455    }
456
457    fn set_send_buffer_size(
458        &mut self,
459        this: Resource<p2_udp::UdpSocket>,
460        value: u64,
461    ) -> wasmtime_wasi::p2::SocketResult<()> {
462        p2_udp::HostUdpSocket::set_send_buffer_size(&mut self.inner, this, value)
463    }
464
465    fn subscribe(
466        &mut self,
467        this: Resource<p2_udp::UdpSocket>,
468    ) -> wasmtime::Result<Resource<DynPollable>> {
469        p2_udp::HostUdpSocket::subscribe(&mut self.inner, this)
470    }
471
472    fn drop(&mut self, this: Resource<p2_udp::UdpSocket>) -> wasmtime::Result<()> {
473        self.release_permit(this.rep());
474        p2_udp::HostUdpSocket::drop(&mut self.inner, this)
475    }
476}
477
478impl<T> p2_udp::HostIncomingDatagramStream for SpinSocketsView<'_, T> {
479    fn receive(
480        &mut self,
481        this: Resource<p2_udp::IncomingDatagramStream>,
482        max_results: u64,
483    ) -> wasmtime_wasi::p2::SocketResult<Vec<p2_udp::IncomingDatagram>> {
484        p2_udp::HostIncomingDatagramStream::receive(&mut self.inner, this, max_results)
485    }
486
487    fn subscribe(
488        &mut self,
489        this: Resource<p2_udp::IncomingDatagramStream>,
490    ) -> wasmtime::Result<Resource<DynPollable>> {
491        p2_udp::HostIncomingDatagramStream::subscribe(&mut self.inner, this)
492    }
493
494    async fn drop(
495        &mut self,
496        this: Resource<p2_udp::IncomingDatagramStream>,
497    ) -> wasmtime::Result<()> {
498        p2_udp::HostIncomingDatagramStream::drop(&mut self.inner, this).await
499    }
500}
501
502impl<T> p2_udp::HostOutgoingDatagramStream for SpinSocketsView<'_, T> {
503    fn check_send(
504        &mut self,
505        this: Resource<p2_udp::OutgoingDatagramStream>,
506    ) -> wasmtime_wasi::p2::SocketResult<u64> {
507        p2_udp::HostOutgoingDatagramStream::check_send(&mut self.inner, this)
508    }
509
510    fn send(
511        &mut self,
512        this: Resource<p2_udp::OutgoingDatagramStream>,
513        datagrams: Vec<p2_udp::OutgoingDatagram>,
514    ) -> wasmtime_wasi::p2::SocketResult<u64> {
515        p2_udp::HostOutgoingDatagramStream::send(&mut self.inner, this, datagrams)
516    }
517
518    fn subscribe(
519        &mut self,
520        this: Resource<p2_udp::OutgoingDatagramStream>,
521    ) -> wasmtime::Result<Resource<DynPollable>> {
522        p2_udp::HostOutgoingDatagramStream::subscribe(&mut self.inner, this)
523    }
524
525    async fn drop(
526        &mut self,
527        this: Resource<p2_udp::OutgoingDatagramStream>,
528    ) -> wasmtime::Result<()> {
529        p2_udp::HostOutgoingDatagramStream::drop(&mut self.inner, this).await
530    }
531}
532
533impl<T> p2_udp_create::Host for SpinSocketsView<'_, T> {
534    async fn create_udp_socket(
535        &mut self,
536        address_family: wasmtime_wasi::p2::bindings::sockets::network::IpAddressFamily,
537    ) -> wasmtime_wasi::p2::SocketResult<Resource<UdpSocket>> {
538        // Fail immediately rather than wait. Waiting could deadlock a guest
539        // that keeps sockets open.
540        let Ok(permit) = self.try_acquire() else {
541            tracing::warn!("UDP socket creation refused: connection quota exhausted");
542            return Err(SocketErrorCode::NewSocketLimit.into());
543        };
544        let sock = p2_udp_create::Host::create_udp_socket(&mut self.inner, address_family).await?;
545        self.register_permit(sock.rep(), permit);
546        Ok(sock)
547    }
548}
549
550// ===== p3 impls =====
551
552use wasmtime::AsContextMut as _;
553use wasmtime::component::{Access, Accessor};
554use wasmtime_wasi::p3::bindings::sockets::types::{
555    self as p3_types, Duration as p3_Duration, ErrorCode as p3_ErrorCode, Host as p3_Host,
556    HostTcpSocket as p3_HostTcpSocket, HostTcpSocketWithStore, HostUdpSocket as p3_HostUdpSocket,
557    HostUdpSocketWithStore, IpAddressFamily as p3_IpAddressFamily,
558    IpSocketAddress as p3_IpSocketAddress,
559};
560use wasmtime_wasi::p3::sockets::SocketResult as P3SocketResult;
561
562impl<T> p3_Host for SpinSocketsView<'_, T> {
563    fn convert_error_code(
564        &mut self,
565        error: wasmtime_wasi::p3::sockets::SocketError,
566    ) -> wasmtime::Result<p3_ErrorCode> {
567        p3_Host::convert_error_code(&mut self.inner, error)
568    }
569}
570
571impl<T> p3_HostTcpSocket for SpinSocketsView<'_, T> {
572    async fn bind(
573        &mut self,
574        socket: Resource<p3_types::TcpSocket>,
575        local_address: p3_IpSocketAddress,
576    ) -> P3SocketResult<()> {
577        p3_HostTcpSocket::bind(&mut self.inner, socket, local_address).await
578    }
579
580    fn create(
581        &mut self,
582        address_family: p3_IpAddressFamily,
583    ) -> P3SocketResult<Resource<p3_types::TcpSocket>> {
584        let Ok(permit) = self.try_acquire() else {
585            tracing::warn!("TCP socket creation refused: connection quota exhausted");
586            return Err(p3_ErrorCode::Other(Some("connection quota exhausted".into())).into());
587        };
588        let socket = p3_HostTcpSocket::create(&mut self.inner, address_family)?;
589        self.register_permit(socket.rep(), permit);
590        Ok(socket)
591    }
592
593    fn get_local_address(
594        &mut self,
595        socket: Resource<p3_types::TcpSocket>,
596    ) -> P3SocketResult<p3_IpSocketAddress> {
597        p3_HostTcpSocket::get_local_address(&mut self.inner, socket)
598    }
599
600    fn get_remote_address(
601        &mut self,
602        socket: Resource<p3_types::TcpSocket>,
603    ) -> P3SocketResult<p3_IpSocketAddress> {
604        p3_HostTcpSocket::get_remote_address(&mut self.inner, socket)
605    }
606
607    fn get_is_listening(
608        &mut self,
609        socket: Resource<p3_types::TcpSocket>,
610    ) -> wasmtime::Result<bool> {
611        p3_HostTcpSocket::get_is_listening(&mut self.inner, socket)
612    }
613
614    fn get_address_family(
615        &mut self,
616        socket: Resource<p3_types::TcpSocket>,
617    ) -> wasmtime::Result<p3_IpAddressFamily> {
618        p3_HostTcpSocket::get_address_family(&mut self.inner, socket)
619    }
620
621    fn set_listen_backlog_size(
622        &mut self,
623        socket: Resource<p3_types::TcpSocket>,
624        value: u64,
625    ) -> P3SocketResult<()> {
626        p3_HostTcpSocket::set_listen_backlog_size(&mut self.inner, socket, value)
627    }
628
629    fn get_keep_alive_enabled(
630        &mut self,
631        socket: Resource<p3_types::TcpSocket>,
632    ) -> P3SocketResult<bool> {
633        p3_HostTcpSocket::get_keep_alive_enabled(&mut self.inner, socket)
634    }
635
636    fn set_keep_alive_enabled(
637        &mut self,
638        socket: Resource<p3_types::TcpSocket>,
639        value: bool,
640    ) -> P3SocketResult<()> {
641        p3_HostTcpSocket::set_keep_alive_enabled(&mut self.inner, socket, value)
642    }
643
644    fn get_keep_alive_idle_time(
645        &mut self,
646        socket: Resource<p3_types::TcpSocket>,
647    ) -> P3SocketResult<p3_Duration> {
648        p3_HostTcpSocket::get_keep_alive_idle_time(&mut self.inner, socket)
649    }
650
651    fn set_keep_alive_idle_time(
652        &mut self,
653        socket: Resource<p3_types::TcpSocket>,
654        value: p3_Duration,
655    ) -> P3SocketResult<()> {
656        p3_HostTcpSocket::set_keep_alive_idle_time(&mut self.inner, socket, value)
657    }
658
659    fn get_keep_alive_interval(
660        &mut self,
661        socket: Resource<p3_types::TcpSocket>,
662    ) -> P3SocketResult<p3_Duration> {
663        p3_HostTcpSocket::get_keep_alive_interval(&mut self.inner, socket)
664    }
665
666    fn set_keep_alive_interval(
667        &mut self,
668        socket: Resource<p3_types::TcpSocket>,
669        value: p3_Duration,
670    ) -> P3SocketResult<()> {
671        p3_HostTcpSocket::set_keep_alive_interval(&mut self.inner, socket, value)
672    }
673
674    fn get_keep_alive_count(
675        &mut self,
676        socket: Resource<p3_types::TcpSocket>,
677    ) -> P3SocketResult<u32> {
678        p3_HostTcpSocket::get_keep_alive_count(&mut self.inner, socket)
679    }
680
681    fn set_keep_alive_count(
682        &mut self,
683        socket: Resource<p3_types::TcpSocket>,
684        value: u32,
685    ) -> P3SocketResult<()> {
686        p3_HostTcpSocket::set_keep_alive_count(&mut self.inner, socket, value)
687    }
688
689    fn get_hop_limit(&mut self, socket: Resource<p3_types::TcpSocket>) -> P3SocketResult<u8> {
690        p3_HostTcpSocket::get_hop_limit(&mut self.inner, socket)
691    }
692
693    fn set_hop_limit(
694        &mut self,
695        socket: Resource<p3_types::TcpSocket>,
696        value: u8,
697    ) -> P3SocketResult<()> {
698        p3_HostTcpSocket::set_hop_limit(&mut self.inner, socket, value)
699    }
700
701    fn get_receive_buffer_size(
702        &mut self,
703        socket: Resource<p3_types::TcpSocket>,
704    ) -> P3SocketResult<u64> {
705        p3_HostTcpSocket::get_receive_buffer_size(&mut self.inner, socket)
706    }
707
708    fn set_receive_buffer_size(
709        &mut self,
710        socket: Resource<p3_types::TcpSocket>,
711        value: u64,
712    ) -> P3SocketResult<()> {
713        p3_HostTcpSocket::set_receive_buffer_size(&mut self.inner, socket, value)
714    }
715
716    fn get_send_buffer_size(
717        &mut self,
718        socket: Resource<p3_types::TcpSocket>,
719    ) -> P3SocketResult<u64> {
720        p3_HostTcpSocket::get_send_buffer_size(&mut self.inner, socket)
721    }
722
723    fn set_send_buffer_size(
724        &mut self,
725        socket: Resource<p3_types::TcpSocket>,
726        value: u64,
727    ) -> P3SocketResult<()> {
728        p3_HostTcpSocket::set_send_buffer_size(&mut self.inner, socket, value)
729    }
730
731    fn drop(&mut self, sock: Resource<p3_types::TcpSocket>) -> wasmtime::Result<()> {
732        self.release_permit(sock.rep());
733        p3_HostTcpSocket::drop(&mut self.inner, sock)
734    }
735}
736
737impl<T> p3_HostUdpSocket for SpinSocketsView<'_, T> {
738    async fn bind(
739        &mut self,
740        socket: Resource<p3_types::UdpSocket>,
741        local_address: p3_IpSocketAddress,
742    ) -> P3SocketResult<()> {
743        p3_HostUdpSocket::bind(&mut self.inner, socket, local_address).await
744    }
745
746    async fn connect(
747        &mut self,
748        socket: Resource<p3_types::UdpSocket>,
749        remote_address: p3_IpSocketAddress,
750    ) -> P3SocketResult<()> {
751        p3_HostUdpSocket::connect(&mut self.inner, socket, remote_address).await
752    }
753
754    async fn create(
755        &mut self,
756        address_family: p3_IpAddressFamily,
757    ) -> P3SocketResult<Resource<p3_types::UdpSocket>> {
758        // Fail immediately rather than wait. Waiting could deadlock a guest
759        // that keeps sockets open.
760        let Ok(permit) = self.try_acquire() else {
761            tracing::warn!("UDP socket creation refused: connection quota exhausted");
762            return Err(p3_ErrorCode::Other(Some("connection quota exhausted".into())).into());
763        };
764        let sock = p3_HostUdpSocket::create(&mut self.inner, address_family).await?;
765        self.register_permit(sock.rep(), permit);
766        Ok(sock)
767    }
768
769    fn disconnect(&mut self, socket: Resource<p3_types::UdpSocket>) -> P3SocketResult<()> {
770        p3_HostUdpSocket::disconnect(&mut self.inner, socket)
771    }
772
773    fn get_local_address(
774        &mut self,
775        socket: Resource<p3_types::UdpSocket>,
776    ) -> P3SocketResult<p3_IpSocketAddress> {
777        p3_HostUdpSocket::get_local_address(&mut self.inner, socket)
778    }
779
780    fn get_remote_address(
781        &mut self,
782        socket: Resource<p3_types::UdpSocket>,
783    ) -> P3SocketResult<p3_IpSocketAddress> {
784        p3_HostUdpSocket::get_remote_address(&mut self.inner, socket)
785    }
786
787    fn get_address_family(
788        &mut self,
789        socket: Resource<p3_types::UdpSocket>,
790    ) -> wasmtime::Result<p3_IpAddressFamily> {
791        p3_HostUdpSocket::get_address_family(&mut self.inner, socket)
792    }
793
794    fn get_unicast_hop_limit(
795        &mut self,
796        socket: Resource<p3_types::UdpSocket>,
797    ) -> P3SocketResult<u8> {
798        p3_HostUdpSocket::get_unicast_hop_limit(&mut self.inner, socket)
799    }
800
801    fn set_unicast_hop_limit(
802        &mut self,
803        socket: Resource<p3_types::UdpSocket>,
804        value: u8,
805    ) -> P3SocketResult<()> {
806        p3_HostUdpSocket::set_unicast_hop_limit(&mut self.inner, socket, value)
807    }
808
809    fn get_receive_buffer_size(
810        &mut self,
811        socket: Resource<p3_types::UdpSocket>,
812    ) -> P3SocketResult<u64> {
813        p3_HostUdpSocket::get_receive_buffer_size(&mut self.inner, socket)
814    }
815
816    fn set_receive_buffer_size(
817        &mut self,
818        socket: Resource<p3_types::UdpSocket>,
819        value: u64,
820    ) -> P3SocketResult<()> {
821        p3_HostUdpSocket::set_receive_buffer_size(&mut self.inner, socket, value)
822    }
823
824    fn get_send_buffer_size(
825        &mut self,
826        socket: Resource<p3_types::UdpSocket>,
827    ) -> P3SocketResult<u64> {
828        p3_HostUdpSocket::get_send_buffer_size(&mut self.inner, socket)
829    }
830
831    fn set_send_buffer_size(
832        &mut self,
833        socket: Resource<p3_types::UdpSocket>,
834        value: u64,
835    ) -> P3SocketResult<()> {
836        p3_HostUdpSocket::set_send_buffer_size(&mut self.inner, socket, value)
837    }
838
839    fn drop(&mut self, sock: Resource<p3_types::UdpSocket>) -> wasmtime::Result<()> {
840        self.release_permit(sock.rep());
841        p3_HostUdpSocket::drop(&mut self.inner, sock)
842    }
843}
844
845impl<T: Send + 'static> HostTcpSocketWithStore<T> for SpinSockets<T> {
846    async fn connect(
847        store: &Accessor<T, Self>,
848        socket: Resource<p3_types::TcpSocket>,
849        remote_address: p3_IpSocketAddress,
850    ) -> P3SocketResult<()> {
851        let getter = store.with(|mut store| store.get().getter);
852        let wasi_accessor = store.with_getter::<WasiSockets>(getter);
853        <WasiSockets as HostTcpSocketWithStore<T>>::connect(&wasi_accessor, socket, remote_address)
854            .await
855    }
856
857    async fn listen(
858        mut store: Access<'_, T, Self>,
859        socket: Resource<p3_types::TcpSocket>,
860    ) -> P3SocketResult<wasmtime::component::StreamReader<Resource<p3_types::TcpSocket>>> {
861        let getter = store.get().getter;
862        let wasi_store = Access::<T, WasiSockets>::new(store.as_context_mut(), getter);
863        <WasiSockets as HostTcpSocketWithStore<T>>::listen(wasi_store, socket).await
864    }
865
866    fn send(
867        mut store: Access<'_, T, Self>,
868        socket: Resource<p3_types::TcpSocket>,
869        data: wasmtime::component::StreamReader<u8>,
870    ) -> wasmtime::Result<wasmtime::component::FutureReader<Result<(), p3_ErrorCode>>> {
871        let getter = store.get().getter;
872        let wasi_store = Access::<T, WasiSockets>::new(store.as_context_mut(), getter);
873        <WasiSockets as HostTcpSocketWithStore<T>>::send(wasi_store, socket, data)
874    }
875
876    fn receive(
877        mut store: Access<'_, T, Self>,
878        socket: Resource<p3_types::TcpSocket>,
879    ) -> wasmtime::Result<(
880        wasmtime::component::StreamReader<u8>,
881        wasmtime::component::FutureReader<Result<(), p3_ErrorCode>>,
882    )> {
883        let getter = store.get().getter;
884        let wasi_store = Access::<T, WasiSockets>::new(store.as_context_mut(), getter);
885        <WasiSockets as HostTcpSocketWithStore<T>>::receive(wasi_store, socket)
886    }
887}
888
889impl<T: 'static> HostUdpSocketWithStore<T> for SpinSockets<T> {
890    async fn send(
891        store: &Accessor<T, Self>,
892        socket: Resource<p3_types::UdpSocket>,
893        data: Vec<u8>,
894        remote_address: Option<p3_IpSocketAddress>,
895    ) -> P3SocketResult<()> {
896        let getter = store.with(|mut store| store.get().getter);
897        let wasi_accessor = store.with_getter::<WasiSockets>(getter);
898        <WasiSockets as HostUdpSocketWithStore<T>>::send(
899            &wasi_accessor,
900            socket,
901            data,
902            remote_address,
903        )
904        .await
905    }
906
907    async fn receive(
908        store: &Accessor<T, Self>,
909        socket: Resource<p3_types::UdpSocket>,
910    ) -> P3SocketResult<(Vec<u8>, p3_IpSocketAddress)> {
911        let getter = store.with(|mut store| store.get().getter);
912        let wasi_accessor = store.with_getter::<WasiSockets>(getter);
913        <WasiSockets as HostUdpSocketWithStore<T>>::receive(&wasi_accessor, socket).await
914    }
915}