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