xref: /wasmtime-44.0.1/crates/wasi/src/sockets/mod.rs (revision bc4582c3)
1 use core::future::Future;
2 use core::ops::Deref;
3 use std::net::SocketAddr;
4 use std::pin::Pin;
5 use std::sync::Arc;
6 use wasmtime::component::{HasData, ResourceTable};
7 
8 mod tcp;
9 mod udp;
10 pub(crate) mod util;
11 
12 #[cfg(feature = "p3")]
13 pub(crate) use tcp::NonInheritedOptions;
14 pub use tcp::TcpSocket;
15 pub use udp::UdpSocket;
16 
17 /// A helper struct which implements [`HasData`] for the `wasi:sockets` APIs.
18 ///
19 /// This can be useful when directly calling `add_to_linker` functions directly,
20 /// such as [`wasmtime_wasi::p2::bindings::sockets::tcp::add_to_linker`] as the
21 /// `D` type parameter. See [`HasData`] for more information about the type
22 /// parameter's purpose.
23 ///
24 /// When using this type you can skip the [`WasiSocketsView`] trait, for
25 /// example.
26 ///
27 /// [`wasmtime_wasi::p2::bindings::sockets::tcp::add_to_linker`]: crate::p2::bindings::sockets::tcp::add_to_linker
28 ///
29 /// # Examples
30 ///
31 /// ```
32 /// use wasmtime::component::{Linker, ResourceTable};
33 /// use wasmtime::{Engine, Result};
34 /// use wasmtime_wasi::sockets::*;
35 ///
36 /// struct MyStoreState {
37 ///     table: ResourceTable,
38 ///     sockets: WasiSocketsCtx,
39 /// }
40 ///
41 /// fn main() -> Result<()> {
42 ///     let engine = Engine::default();
43 ///     let mut linker = Linker::new(&engine);
44 ///
45 ///     wasmtime_wasi::p2::bindings::sockets::tcp::add_to_linker::<MyStoreState, WasiSockets>(
46 ///         &mut linker,
47 ///         |state| WasiSocketsCtxView {
48 ///             ctx: &mut state.sockets,
49 ///             table: &mut state.table,
50 ///         },
51 ///     )?;
52 ///     Ok(())
53 /// }
54 /// ```
55 pub struct WasiSockets;
56 
57 impl HasData for WasiSockets {
58     type Data<'a> = WasiSocketsCtxView<'a>;
59 }
60 
61 /// Value taken from rust std library.
62 pub(crate) const DEFAULT_TCP_BACKLOG: u32 = 128;
63 
64 /// Theoretical maximum byte size of a UDP datagram, the real limit is lower,
65 /// but we do not account for e.g. the transport layer here for simplicity.
66 /// In practice, datagrams are typically less than 1500 bytes.
67 pub(crate) const MAX_UDP_DATAGRAM_SIZE: usize = u16::MAX as usize;
68 
69 #[derive(Clone, Default)]
70 pub struct WasiSocketsCtx {
71     pub(crate) socket_addr_check: SocketAddrCheck,
72     pub(crate) allowed_network_uses: AllowedNetworkUses,
73 }
74 
75 pub struct WasiSocketsCtxView<'a> {
76     pub ctx: &'a mut WasiSocketsCtx,
77     pub table: &'a mut ResourceTable,
78 }
79 
80 pub trait WasiSocketsView: Send {
sockets(&mut self) -> WasiSocketsCtxView<'_>81     fn sockets(&mut self) -> WasiSocketsCtxView<'_>;
82 }
83 
84 #[derive(Copy, Clone)]
85 pub(crate) struct AllowedNetworkUses {
86     pub(crate) ip_name_lookup: bool,
87     pub(crate) udp: bool,
88     pub(crate) tcp: bool,
89 }
90 
91 impl Default for AllowedNetworkUses {
default() -> Self92     fn default() -> Self {
93         Self {
94             ip_name_lookup: false,
95             udp: true,
96             tcp: true,
97         }
98     }
99 }
100 
101 impl AllowedNetworkUses {
check_allowed_udp(&self) -> std::io::Result<()>102     pub(crate) fn check_allowed_udp(&self) -> std::io::Result<()> {
103         if !self.udp {
104             return Err(std::io::Error::new(
105                 std::io::ErrorKind::PermissionDenied,
106                 "UDP is not allowed",
107             ));
108         }
109 
110         Ok(())
111     }
112 
check_allowed_tcp(&self) -> std::io::Result<()>113     pub(crate) fn check_allowed_tcp(&self) -> std::io::Result<()> {
114         if !self.tcp {
115             return Err(std::io::Error::new(
116                 std::io::ErrorKind::PermissionDenied,
117                 "TCP is not allowed",
118             ));
119         }
120 
121         Ok(())
122     }
123 }
124 
125 /// A check that will be called for each socket address that is used of whether the address is permitted.
126 #[derive(Clone)]
127 pub(crate) struct SocketAddrCheck(
128     Arc<
129         dyn Fn(SocketAddr, SocketAddrUse) -> Pin<Box<dyn Future<Output = bool> + Send + Sync>>
130             + Send
131             + Sync,
132     >,
133 );
134 
135 impl SocketAddrCheck {
136     /// A check that will be called for each socket address that is used.
137     ///
138     /// Returning `true` will permit socket connections to the `SocketAddr`,
139     /// while returning `false` will reject the connection.
new( f: impl Fn(SocketAddr, SocketAddrUse) -> Pin<Box<dyn Future<Output = bool> + Send + Sync>> + Send + Sync + 'static, ) -> Self140     pub(crate) fn new(
141         f: impl Fn(SocketAddr, SocketAddrUse) -> Pin<Box<dyn Future<Output = bool> + Send + Sync>>
142         + Send
143         + Sync
144         + 'static,
145     ) -> Self {
146         Self(Arc::new(f))
147     }
148 
check( &self, addr: SocketAddr, reason: SocketAddrUse, ) -> std::io::Result<()>149     pub(crate) async fn check(
150         &self,
151         addr: SocketAddr,
152         reason: SocketAddrUse,
153     ) -> std::io::Result<()> {
154         if (self.0)(addr, reason).await {
155             Ok(())
156         } else {
157             Err(std::io::Error::new(
158                 std::io::ErrorKind::PermissionDenied,
159                 "An address was not permitted by the socket address check.",
160             ))
161         }
162     }
163 }
164 
165 impl Deref for SocketAddrCheck {
166     type Target = dyn Fn(SocketAddr, SocketAddrUse) -> Pin<Box<dyn Future<Output = bool> + Send + Sync>>
167         + Send
168         + Sync;
169 
deref(&self) -> &Self::Target170     fn deref(&self) -> &Self::Target {
171         self.0.as_ref()
172     }
173 }
174 
175 impl Default for SocketAddrCheck {
default() -> Self176     fn default() -> Self {
177         Self(Arc::new(|_, _| Box::pin(async { false })))
178     }
179 }
180 
181 /// The reason what a socket address is being used for.
182 #[derive(Clone, Copy, Debug)]
183 pub enum SocketAddrUse {
184     /// Binding TCP socket
185     TcpBind,
186     /// Connecting TCP socket
187     TcpConnect,
188     /// Binding UDP socket
189     UdpBind,
190     /// Connecting UDP socket
191     UdpConnect,
192     /// Sending datagram on non-connected UDP socket
193     UdpOutgoingDatagram,
194 }
195 
196 #[derive(Copy, Clone, Eq, PartialEq)]
197 pub(crate) enum SocketAddressFamily {
198     Ipv4,
199     Ipv6,
200 }
201