1use core::fmt;
2use core::future::Future;
3use core::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
4use core::ops::Deref;
5use rustix::fd::AsFd;
6use rustix::io::Errno;
7use rustix::net::sockopt;
8use std::pin::Pin;
9use std::sync::Arc;
10use std::task::Poll;
11use tracing::debug;
12use wasmtime::component::{HasData, ResourceTable};
13
14pub(crate) mod ip_name_lookup;
15mod tcp;
16mod udp;
17pub use tcp::TcpSocket;
18pub(crate) use tcp::{TcpListenStream, TcpReceiveStream, TcpSendStream};
19pub use udp::UdpSocket;
20
21pub struct WasiSockets;
60
61impl HasData for WasiSockets {
62 type Data<'a> = WasiSocketsCtxView<'a>;
63}
64
65#[derive(Clone, Default)]
66pub struct WasiSocketsCtx {
67 pub(crate) socket_addr_check: SocketAddrCheck,
68 pub(crate) allowed_network_uses: AllowedNetworkUses,
69}
70
71pub struct WasiSocketsCtxView<'a> {
72 pub ctx: &'a mut WasiSocketsCtx,
73 pub table: &'a mut ResourceTable,
74}
75
76pub trait WasiSocketsView: Send {
77 fn sockets(&mut self) -> WasiSocketsCtxView<'_>;
78}
79
80#[derive(Copy, Clone, Default)]
81pub(crate) struct AllowedNetworkUses {
82 pub(crate) ip_name_lookup: bool,
83 pub(crate) udp: bool,
84 pub(crate) tcp: bool,
85}
86
87impl AllowedNetworkUses {
88 pub(crate) fn check_allowed_udp(&self) -> std::io::Result<()> {
89 if !self.udp {
90 return Err(std::io::Error::new(
91 std::io::ErrorKind::PermissionDenied,
92 "UDP is not allowed",
93 ));
94 }
95
96 Ok(())
97 }
98
99 pub(crate) fn check_allowed_tcp(&self) -> std::io::Result<()> {
100 if !self.tcp {
101 return Err(std::io::Error::new(
102 std::io::ErrorKind::PermissionDenied,
103 "TCP is not allowed",
104 ));
105 }
106
107 Ok(())
108 }
109}
110
111#[derive(Clone)]
113pub(crate) struct SocketAddrCheck(
114 Arc<
115 dyn Fn(SocketAddr, SocketAddrUse) -> Pin<Box<dyn Future<Output = bool> + Send + Sync>>
116 + Send
117 + Sync,
118 >,
119);
120
121impl SocketAddrCheck {
122 pub(crate) fn new(
127 f: impl Fn(SocketAddr, SocketAddrUse) -> Pin<Box<dyn Future<Output = bool> + Send + Sync>>
128 + Send
129 + Sync
130 + 'static,
131 ) -> Self {
132 Self(Arc::new(f))
133 }
134
135 pub(crate) async fn check(
136 &self,
137 addr: SocketAddr,
138 reason: SocketAddrUse,
139 ) -> std::io::Result<()> {
140 if (self.0)(addr, reason).await {
141 Ok(())
142 } else {
143 Err(std::io::Error::new(
144 std::io::ErrorKind::PermissionDenied,
145 "An address was not permitted by the socket address check.",
146 ))
147 }
148 }
149}
150
151impl Deref for SocketAddrCheck {
152 type Target = dyn Fn(SocketAddr, SocketAddrUse) -> Pin<Box<dyn Future<Output = bool> + Send + Sync>>
153 + Send
154 + Sync;
155
156 fn deref(&self) -> &Self::Target {
157 self.0.as_ref()
158 }
159}
160
161impl Default for SocketAddrCheck {
162 fn default() -> Self {
163 Self(Arc::new(|_, _| Box::pin(async { false })))
164 }
165}
166
167#[derive(Clone, Copy, Debug)]
169pub enum SocketAddrUse {
170 TcpBind,
179
180 TcpListen,
186
187 TcpAccept,
193
194 TcpConnect,
199
200 UdpBind,
209
210 UdpSend,
215
216 UdpReceive,
222}
223
224#[derive(Copy, Clone, Eq, PartialEq)]
225pub(crate) enum SocketAddressFamily {
226 Ipv4,
227 Ipv6,
228}
229
230pub(crate) enum MaybeReady<T> {
235 Pending(Pin<Box<dyn Future<Output = T> + Send>>),
236 Ready(T),
237}
238impl<T> MaybeReady<T> {
239 pub(crate) fn new(fut: impl Future<Output = T> + Send + 'static) -> Self {
240 Self::Pending(Box::pin(fut))
241 }
242
243 pub(crate) fn poll_or_spawn(fut: impl Future<Output = T> + Send + 'static) -> Self
246 where
247 T: Send + 'static,
248 {
249 let mut fut = Box::pin(fut);
250 match crate::runtime::with_ambient_tokio_runtime(|| fut.as_mut().poll(&mut noop_cx())) {
251 Poll::Ready(val) => Self::Ready(val),
252 Poll::Pending => Self::new(crate::runtime::spawn(fut)),
253 }
254 }
255 pub(crate) fn unwrap_ready(self) -> T {
256 match self {
257 Self::Ready(val) => val,
258 Self::Pending(_) => panic!("future not ready"),
259 }
260 }
261 pub(crate) fn poll_ready(&mut self, cx: &mut std::task::Context<'_>) -> Poll<&mut T> {
262 match self {
263 Self::Pending(fut) => match fut.as_mut().poll(cx) {
264 Poll::Ready(val) => {
265 *self = Self::Ready(val);
266 Poll::Ready(match self {
267 Self::Ready(val) => val,
268 _ => unreachable!(),
269 })
270 }
271 Poll::Pending => Poll::Pending,
272 },
273 Self::Ready(val) => Poll::Ready(val),
274 }
275 }
276 pub(crate) async fn into_future(self) -> T {
277 match self {
278 Self::Ready(val) => val,
279 Self::Pending(fut) => fut.await,
280 }
281 }
282}
283
284pub(crate) fn noop_cx() -> std::task::Context<'static> {
285 std::task::Context::from_waker(futures::task::noop_waker_ref())
286}
287
288#[derive(Clone, Copy, Debug)]
289pub enum ErrorCode {
290 AccessDenied,
291 NotSupported,
292 InvalidArgument,
293 OutOfMemory,
294 Timeout,
295 InvalidState,
296 AddressNotBindable,
297 AddressInUse,
298 RemoteUnreachable,
299 ConnectionRefused,
300 ConnectionBroken,
301 ConnectionReset,
302 ConnectionAborted,
303 DatagramTooLarge,
304 Other,
305}
306
307impl fmt::Display for ErrorCode {
308 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
309 fmt::Debug::fmt(self, f)
310 }
311}
312
313impl std::error::Error for ErrorCode {}
314
315impl From<std::io::Error> for ErrorCode {
316 fn from(value: std::io::Error) -> Self {
317 (&value).into()
318 }
319}
320
321impl From<&std::io::Error> for ErrorCode {
322 fn from(value: &std::io::Error) -> Self {
323 if let Some(errno) = Errno::from_io_error(value) {
325 return errno.into();
326 }
327
328 match value.kind() {
329 std::io::ErrorKind::AddrInUse => Self::AddressInUse,
330 std::io::ErrorKind::AddrNotAvailable => Self::AddressNotBindable,
331 std::io::ErrorKind::ConnectionAborted => Self::ConnectionAborted,
332 std::io::ErrorKind::ConnectionRefused => Self::ConnectionRefused,
333 std::io::ErrorKind::ConnectionReset => Self::ConnectionReset,
334 std::io::ErrorKind::InvalidInput => Self::InvalidArgument,
335 std::io::ErrorKind::NotConnected => Self::InvalidState,
336 std::io::ErrorKind::OutOfMemory => Self::OutOfMemory,
337 std::io::ErrorKind::PermissionDenied => Self::AccessDenied,
338 std::io::ErrorKind::TimedOut => Self::Timeout,
339 std::io::ErrorKind::Unsupported => Self::NotSupported,
340 std::io::ErrorKind::HostUnreachable => Self::RemoteUnreachable,
341 std::io::ErrorKind::NetworkUnreachable => Self::RemoteUnreachable,
342 std::io::ErrorKind::NetworkDown => Self::RemoteUnreachable,
343 std::io::ErrorKind::BrokenPipe => Self::ConnectionBroken,
344 _ => {
345 debug!("unknown I/O error: {value}");
346 Self::Other
347 }
348 }
349 }
350}
351
352impl From<Errno> for ErrorCode {
353 fn from(value: Errno) -> Self {
354 (&value).into()
355 }
356}
357
358impl From<&Errno> for ErrorCode {
359 fn from(value: &Errno) -> Self {
360 match *value {
361 #[cfg(not(windows))]
362 Errno::PERM => Self::AccessDenied,
363 Errno::ACCESS => Self::AccessDenied,
364 Errno::ADDRINUSE => Self::AddressInUse,
365 Errno::ADDRNOTAVAIL => Self::AddressNotBindable,
366 Errno::TIMEDOUT => Self::Timeout,
367 #[cfg(not(windows))]
368 Errno::PIPE => Self::ConnectionBroken,
369 Errno::CONNREFUSED => Self::ConnectionRefused,
370 Errno::CONNRESET => Self::ConnectionReset,
371 Errno::CONNABORTED => Self::ConnectionAborted,
372 Errno::INVAL => Self::InvalidArgument,
373 Errno::HOSTUNREACH => Self::RemoteUnreachable,
374 Errno::HOSTDOWN => Self::RemoteUnreachable,
375 Errno::NETDOWN => Self::RemoteUnreachable,
376 Errno::NETUNREACH => Self::RemoteUnreachable,
377 #[cfg(target_os = "linux")]
378 Errno::NONET => Self::RemoteUnreachable,
379 Errno::ISCONN => Self::InvalidState,
380 Errno::NOTCONN => Self::InvalidState,
381 Errno::DESTADDRREQ => Self::InvalidState,
382 Errno::MSGSIZE => Self::DatagramTooLarge,
383 #[cfg(not(windows))]
384 Errno::NOMEM => Self::OutOfMemory,
385 Errno::NOBUFS => Self::OutOfMemory,
386 Errno::OPNOTSUPP => Self::NotSupported,
387 Errno::NOPROTOOPT => Self::NotSupported,
388 Errno::PFNOSUPPORT => Self::NotSupported,
389 Errno::PROTONOSUPPORT => Self::NotSupported,
390 Errno::PROTOTYPE => Self::NotSupported,
391 Errno::SOCKTNOSUPPORT => Self::NotSupported,
392 Errno::AFNOSUPPORT => Self::NotSupported,
393
394 _ => {
396 debug!("unknown I/O error: {value}");
397 Self::Other
398 }
399 }
400 }
401}
402
403fn is_deprecated_ipv4_compatible(addr: Ipv6Addr) -> bool {
404 matches!(addr.segments(), [0, 0, 0, 0, 0, 0, _, _])
405 && addr != Ipv6Addr::UNSPECIFIED
406 && addr != Ipv6Addr::LOCALHOST
407}
408
409pub(crate) fn is_valid_address_family(addr: IpAddr, socket_family: SocketAddressFamily) -> bool {
410 match (socket_family, addr) {
411 (SocketAddressFamily::Ipv4, IpAddr::V4(..)) => true,
412 (SocketAddressFamily::Ipv6, IpAddr::V6(ipv6)) => {
413 !is_deprecated_ipv4_compatible(ipv6) && ipv6.to_ipv4_mapped().is_none()
418 }
419 _ => false,
420 }
421}
422
423pub(crate) fn is_valid_remote_address(addr: SocketAddr) -> bool {
424 !addr.ip().to_canonical().is_unspecified() && addr.port() != 0
425}
426
427pub(crate) fn is_valid_unicast_address(addr: IpAddr) -> bool {
428 match addr.to_canonical() {
429 IpAddr::V4(ipv4) => !ipv4.is_multicast() && !ipv4.is_broadcast(),
430 IpAddr::V6(ipv6) => !ipv6.is_multicast(),
431 }
432}
433
434pub(crate) fn to_ipv4_addr(addr: (u8, u8, u8, u8)) -> Ipv4Addr {
435 let (x0, x1, x2, x3) = addr;
436 Ipv4Addr::new(x0, x1, x2, x3)
437}
438
439pub(crate) fn from_ipv4_addr(addr: Ipv4Addr) -> (u8, u8, u8, u8) {
440 let [x0, x1, x2, x3] = addr.octets();
441 (x0, x1, x2, x3)
442}
443
444pub(crate) fn to_ipv6_addr(addr: (u16, u16, u16, u16, u16, u16, u16, u16)) -> Ipv6Addr {
445 let (x0, x1, x2, x3, x4, x5, x6, x7) = addr;
446 Ipv6Addr::new(x0, x1, x2, x3, x4, x5, x6, x7)
447}
448
449pub(crate) fn from_ipv6_addr(addr: Ipv6Addr) -> (u16, u16, u16, u16, u16, u16, u16, u16) {
450 let [x0, x1, x2, x3, x4, x5, x6, x7] = addr.segments();
451 (x0, x1, x2, x3, x4, x5, x6, x7)
452}
453
454fn normalize_get_buffer_size(value: usize) -> usize {
459 if cfg!(target_os = "linux") {
460 value / 2
466 } else {
467 value
468 }
469}
470
471fn normalize_set_buffer_size(value: usize) -> usize {
472 value.clamp(1, i32::MAX as usize)
473}
474
475fn get_ip_ttl(fd: impl AsFd) -> Result<u8, ErrorCode> {
476 let v = sockopt::ip_ttl(fd)?;
477 let Ok(v) = v.try_into() else {
478 return Err(ErrorCode::NotSupported);
479 };
480 Ok(v)
481}
482
483fn get_ipv6_unicast_hops(fd: impl AsFd) -> Result<u8, ErrorCode> {
484 let v = sockopt::ipv6_unicast_hops(fd)?;
485 Ok(v)
486}
487
488pub(crate) fn get_unicast_hop_limit(
489 fd: impl AsFd,
490 family: SocketAddressFamily,
491) -> Result<u8, ErrorCode> {
492 match family {
493 SocketAddressFamily::Ipv4 => get_ip_ttl(fd),
494 SocketAddressFamily::Ipv6 => get_ipv6_unicast_hops(fd),
495 }
496}
497
498pub(crate) fn set_unicast_hop_limit(
499 fd: impl AsFd,
500 family: SocketAddressFamily,
501 value: u8,
502) -> Result<(), ErrorCode> {
503 if value == 0 {
504 return Err(ErrorCode::InvalidArgument);
510 }
511 match family {
512 SocketAddressFamily::Ipv4 => {
513 sockopt::set_ip_ttl(fd, value.into())?;
514 }
515 SocketAddressFamily::Ipv6 => {
516 sockopt::set_ipv6_unicast_hops(fd, Some(value))?;
517 }
518 }
519 Ok(())
520}
521
522pub(crate) fn get_receive_buffer_size(fd: impl AsFd) -> Result<u64, ErrorCode> {
523 let v = sockopt::socket_recv_buffer_size(fd)?;
524 Ok(normalize_get_buffer_size(v).try_into().unwrap_or(u64::MAX))
525}
526
527pub(crate) fn set_receive_buffer_size(fd: impl AsFd, value: u64) -> Result<usize, ErrorCode> {
528 if value == 0 {
529 return Err(ErrorCode::InvalidArgument);
531 }
532 let value = value.try_into().unwrap_or(usize::MAX);
533 let value = normalize_set_buffer_size(value);
534 match sockopt::set_socket_recv_buffer_size(fd, value) {
535 Err(Errno::NOBUFS) => {}
546 Err(err) => return Err(err.into()),
547 _ => {}
548 };
549 Ok(value)
550}
551
552pub(crate) fn get_send_buffer_size(fd: impl AsFd) -> Result<u64, ErrorCode> {
553 let v = sockopt::socket_send_buffer_size(fd)?;
554 Ok(normalize_get_buffer_size(v).try_into().unwrap_or(u64::MAX))
555}
556
557pub(crate) fn set_send_buffer_size(fd: impl AsFd, value: u64) -> Result<usize, ErrorCode> {
558 if value == 0 {
559 return Err(ErrorCode::InvalidArgument);
561 }
562 let value = value.try_into().unwrap_or(usize::MAX);
563 let value = normalize_set_buffer_size(value);
564 match sockopt::set_socket_send_buffer_size(fd, value) {
565 Err(Errno::NOBUFS) => {}
567 Err(err) => return Err(err.into()),
568 _ => {}
569 };
570 Ok(value)
571}
572
573pub(crate) fn unspecified_addr(family: SocketAddressFamily) -> SocketAddr {
574 let ip = match family {
575 SocketAddressFamily::Ipv4 => IpAddr::V4(Ipv4Addr::UNSPECIFIED),
576 SocketAddressFamily::Ipv6 => IpAddr::V6(Ipv6Addr::UNSPECIFIED),
577 };
578 SocketAddr::new(ip, 0)
579}