1use crate::bindings::sockets::network::{
2 self, ErrorCode, IpAddress, IpAddressFamily, IpSocketAddress, Ipv4SocketAddress,
3 Ipv6SocketAddress,
4};
5use crate::network::{from_ipv4_addr, from_ipv6_addr, to_ipv4_addr, to_ipv6_addr};
6use crate::{IoView, SocketError, WasiImpl, WasiView};
7use anyhow::Error;
8use rustix::io::Errno;
9use std::io;
10use wasmtime::component::Resource;
11
12impl<T> network::Host for WasiImpl<T>
13where
14 T: WasiView,
15{
16 fn convert_error_code(&mut self, error: SocketError) -> anyhow::Result<ErrorCode> {
17 error.downcast()
18 }
19
20 fn network_error_code(&mut self, err: Resource<Error>) -> anyhow::Result<Option<ErrorCode>> {
21 let err = self.table().get(&err)?;
22
23 if let Some(err) = err.downcast_ref::<std::io::Error>() {
24 return Ok(Some(ErrorCode::from(err)));
25 }
26
27 Ok(None)
28 }
29}
30
31impl<T> crate::bindings::sockets::network::HostNetwork for WasiImpl<T>
32where
33 T: WasiView,
34{
35 fn drop(&mut self, this: Resource<network::Network>) -> Result<(), anyhow::Error> {
36 let table = self.table();
37
38 table.delete(this)?;
39
40 Ok(())
41 }
42}
43
44impl From<io::Error> for ErrorCode {
45 fn from(value: io::Error) -> Self {
46 (&value).into()
47 }
48}
49
50impl From<&io::Error> for ErrorCode {
51 fn from(value: &io::Error) -> Self {
52 if let Some(errno) = Errno::from_io_error(value) {
54 return errno.into();
55 }
56
57 match value.kind() {
58 std::io::ErrorKind::AddrInUse => ErrorCode::AddressInUse,
59 std::io::ErrorKind::AddrNotAvailable => ErrorCode::AddressNotBindable,
60 std::io::ErrorKind::ConnectionAborted => ErrorCode::ConnectionAborted,
61 std::io::ErrorKind::ConnectionRefused => ErrorCode::ConnectionRefused,
62 std::io::ErrorKind::ConnectionReset => ErrorCode::ConnectionReset,
63 std::io::ErrorKind::Interrupted => ErrorCode::WouldBlock,
64 std::io::ErrorKind::InvalidInput => ErrorCode::InvalidArgument,
65 std::io::ErrorKind::NotConnected => ErrorCode::InvalidState,
66 std::io::ErrorKind::OutOfMemory => ErrorCode::OutOfMemory,
67 std::io::ErrorKind::PermissionDenied => ErrorCode::AccessDenied,
68 std::io::ErrorKind::TimedOut => ErrorCode::Timeout,
69 std::io::ErrorKind::Unsupported => ErrorCode::NotSupported,
70 std::io::ErrorKind::WouldBlock => ErrorCode::WouldBlock,
71
72 _ => {
73 tracing::debug!("unknown I/O error: {value}");
74 ErrorCode::Unknown
75 }
76 }
77 }
78}
79
80impl From<Errno> for ErrorCode {
81 fn from(value: Errno) -> Self {
82 (&value).into()
83 }
84}
85
86impl From<&Errno> for ErrorCode {
87 fn from(value: &Errno) -> Self {
88 match *value {
89 Errno::WOULDBLOCK => ErrorCode::WouldBlock,
90 #[allow(unreachable_patterns)] Errno::AGAIN => ErrorCode::WouldBlock,
92 Errno::INTR => ErrorCode::WouldBlock,
93 #[cfg(not(windows))]
94 Errno::PERM => ErrorCode::AccessDenied,
95 Errno::ACCESS => ErrorCode::AccessDenied,
96 Errno::ADDRINUSE => ErrorCode::AddressInUse,
97 Errno::ADDRNOTAVAIL => ErrorCode::AddressNotBindable,
98 Errno::ALREADY => ErrorCode::ConcurrencyConflict,
99 Errno::TIMEDOUT => ErrorCode::Timeout,
100 Errno::CONNREFUSED => ErrorCode::ConnectionRefused,
101 Errno::CONNRESET => ErrorCode::ConnectionReset,
102 Errno::CONNABORTED => ErrorCode::ConnectionAborted,
103 Errno::INVAL => ErrorCode::InvalidArgument,
104 Errno::HOSTUNREACH => ErrorCode::RemoteUnreachable,
105 Errno::HOSTDOWN => ErrorCode::RemoteUnreachable,
106 Errno::NETDOWN => ErrorCode::RemoteUnreachable,
107 Errno::NETUNREACH => ErrorCode::RemoteUnreachable,
108 #[cfg(target_os = "linux")]
109 Errno::NONET => ErrorCode::RemoteUnreachable,
110 Errno::ISCONN => ErrorCode::InvalidState,
111 Errno::NOTCONN => ErrorCode::InvalidState,
112 Errno::DESTADDRREQ => ErrorCode::InvalidState,
113 #[cfg(not(windows))]
114 Errno::NFILE => ErrorCode::NewSocketLimit,
115 Errno::MFILE => ErrorCode::NewSocketLimit,
116 Errno::MSGSIZE => ErrorCode::DatagramTooLarge,
117 #[cfg(not(windows))]
118 Errno::NOMEM => ErrorCode::OutOfMemory,
119 Errno::NOBUFS => ErrorCode::OutOfMemory,
120 Errno::OPNOTSUPP => ErrorCode::NotSupported,
121 Errno::NOPROTOOPT => ErrorCode::NotSupported,
122 Errno::PFNOSUPPORT => ErrorCode::NotSupported,
123 Errno::PROTONOSUPPORT => ErrorCode::NotSupported,
124 Errno::PROTOTYPE => ErrorCode::NotSupported,
125 Errno::SOCKTNOSUPPORT => ErrorCode::NotSupported,
126 Errno::AFNOSUPPORT => ErrorCode::NotSupported,
127
128 _ => {
130 tracing::debug!("unknown I/O error: {value}");
131 ErrorCode::Unknown
132 }
133 }
134 }
135}
136
137impl From<std::net::IpAddr> for IpAddress {
138 fn from(addr: std::net::IpAddr) -> Self {
139 match addr {
140 std::net::IpAddr::V4(v4) => Self::Ipv4(from_ipv4_addr(v4)),
141 std::net::IpAddr::V6(v6) => Self::Ipv6(from_ipv6_addr(v6)),
142 }
143 }
144}
145
146impl From<IpSocketAddress> for std::net::SocketAddr {
147 fn from(addr: IpSocketAddress) -> Self {
148 match addr {
149 IpSocketAddress::Ipv4(ipv4) => Self::V4(ipv4.into()),
150 IpSocketAddress::Ipv6(ipv6) => Self::V6(ipv6.into()),
151 }
152 }
153}
154
155impl From<std::net::SocketAddr> for IpSocketAddress {
156 fn from(addr: std::net::SocketAddr) -> Self {
157 match addr {
158 std::net::SocketAddr::V4(v4) => Self::Ipv4(v4.into()),
159 std::net::SocketAddr::V6(v6) => Self::Ipv6(v6.into()),
160 }
161 }
162}
163
164impl From<Ipv4SocketAddress> for std::net::SocketAddrV4 {
165 fn from(addr: Ipv4SocketAddress) -> Self {
166 Self::new(to_ipv4_addr(addr.address), addr.port)
167 }
168}
169
170impl From<std::net::SocketAddrV4> for Ipv4SocketAddress {
171 fn from(addr: std::net::SocketAddrV4) -> Self {
172 Self {
173 address: from_ipv4_addr(*addr.ip()),
174 port: addr.port(),
175 }
176 }
177}
178
179impl From<Ipv6SocketAddress> for std::net::SocketAddrV6 {
180 fn from(addr: Ipv6SocketAddress) -> Self {
181 Self::new(
182 to_ipv6_addr(addr.address),
183 addr.port,
184 addr.flow_info,
185 addr.scope_id,
186 )
187 }
188}
189
190impl From<std::net::SocketAddrV6> for Ipv6SocketAddress {
191 fn from(addr: std::net::SocketAddrV6) -> Self {
192 Self {
193 address: from_ipv6_addr(*addr.ip()),
194 port: addr.port(),
195 flow_info: addr.flowinfo(),
196 scope_id: addr.scope_id(),
197 }
198 }
199}
200
201impl std::net::ToSocketAddrs for IpSocketAddress {
202 type Iter = <std::net::SocketAddr as std::net::ToSocketAddrs>::Iter;
203
204 fn to_socket_addrs(&self) -> io::Result<Self::Iter> {
205 std::net::SocketAddr::from(*self).to_socket_addrs()
206 }
207}
208
209impl std::net::ToSocketAddrs for Ipv4SocketAddress {
210 type Iter = <std::net::SocketAddrV4 as std::net::ToSocketAddrs>::Iter;
211
212 fn to_socket_addrs(&self) -> io::Result<Self::Iter> {
213 std::net::SocketAddrV4::from(*self).to_socket_addrs()
214 }
215}
216
217impl std::net::ToSocketAddrs for Ipv6SocketAddress {
218 type Iter = <std::net::SocketAddrV6 as std::net::ToSocketAddrs>::Iter;
219
220 fn to_socket_addrs(&self) -> io::Result<Self::Iter> {
221 std::net::SocketAddrV6::from(*self).to_socket_addrs()
222 }
223}
224
225impl From<IpAddressFamily> for cap_net_ext::AddressFamily {
226 fn from(family: IpAddressFamily) -> Self {
227 match family {
228 IpAddressFamily::Ipv4 => cap_net_ext::AddressFamily::Ipv4,
229 IpAddressFamily::Ipv6 => cap_net_ext::AddressFamily::Ipv6,
230 }
231 }
232}
233
234impl From<cap_net_ext::AddressFamily> for IpAddressFamily {
235 fn from(family: cap_net_ext::AddressFamily) -> Self {
236 match family {
237 cap_net_ext::AddressFamily::Ipv4 => IpAddressFamily::Ipv4,
238 cap_net_ext::AddressFamily::Ipv6 => IpAddressFamily::Ipv6,
239 }
240 }
241}
242
243pub(crate) mod util {
244 use std::io;
245 use std::net::{IpAddr, Ipv6Addr, SocketAddr};
246 use std::time::Duration;
247
248 use crate::network::SocketAddressFamily;
249 use cap_net_ext::{AddressFamily, Blocking, UdpSocketExt};
250 use rustix::fd::{AsFd, OwnedFd};
251 use rustix::io::Errno;
252 use rustix::net::sockopt;
253
254 pub fn validate_unicast(addr: &SocketAddr) -> io::Result<()> {
255 match to_canonical(&addr.ip()) {
256 IpAddr::V4(ipv4) => {
257 if ipv4.is_multicast() || ipv4.is_broadcast() {
258 Err(io::Error::new(
259 io::ErrorKind::InvalidInput,
260 "Both IPv4 broadcast and multicast addresses are not supported",
261 ))
262 } else {
263 Ok(())
264 }
265 }
266 IpAddr::V6(ipv6) => {
267 if ipv6.is_multicast() {
268 Err(io::Error::new(
269 io::ErrorKind::InvalidInput,
270 "IPv6 multicast addresses are not supported",
271 ))
272 } else {
273 Ok(())
274 }
275 }
276 }
277 }
278
279 pub fn validate_remote_address(addr: &SocketAddr) -> io::Result<()> {
280 if to_canonical(&addr.ip()).is_unspecified() {
281 return Err(io::Error::new(
282 io::ErrorKind::InvalidInput,
283 "Remote address may not be `0.0.0.0` or `::`",
284 ));
285 }
286
287 if addr.port() == 0 {
288 return Err(io::Error::new(
289 io::ErrorKind::InvalidInput,
290 "Remote port may not be 0",
291 ));
292 }
293
294 Ok(())
295 }
296
297 pub fn validate_address_family(
298 addr: &SocketAddr,
299 socket_family: &SocketAddressFamily,
300 ) -> io::Result<()> {
301 match (socket_family, addr.ip()) {
302 (SocketAddressFamily::Ipv4, IpAddr::V4(_)) => Ok(()),
303 (SocketAddressFamily::Ipv6, IpAddr::V6(ipv6)) => {
304 if is_deprecated_ipv4_compatible(&ipv6) {
305 Err(io::Error::new(
310 io::ErrorKind::InvalidInput,
311 "IPv4-compatible IPv6 addresses are not supported",
312 ))
313 } else if ipv6.to_ipv4_mapped().is_some() {
314 Err(io::Error::new(
315 io::ErrorKind::InvalidInput,
316 "IPv4-mapped IPv6 address passed to an IPv6-only socket",
317 ))
318 } else {
319 Ok(())
320 }
321 }
322 _ => Err(io::Error::new(
323 io::ErrorKind::InvalidInput,
324 "Address family mismatch",
325 )),
326 }
327 }
328
329 pub fn to_canonical(addr: &IpAddr) -> IpAddr {
331 match addr {
332 IpAddr::V4(ipv4) => IpAddr::V4(*ipv4),
333 IpAddr::V6(ipv6) => {
334 if let Some(ipv4) = ipv6.to_ipv4_mapped() {
335 IpAddr::V4(ipv4)
336 } else {
337 IpAddr::V6(*ipv6)
338 }
339 }
340 }
341 }
342
343 fn is_deprecated_ipv4_compatible(addr: &Ipv6Addr) -> bool {
344 matches!(addr.segments(), [0, 0, 0, 0, 0, 0, _, _])
345 && *addr != Ipv6Addr::UNSPECIFIED
346 && *addr != Ipv6Addr::LOCALHOST
347 }
348
349 pub fn udp_socket(family: AddressFamily, blocking: Blocking) -> io::Result<OwnedFd> {
354 let socket = cap_std::net::UdpSocket::new(family, blocking)?;
360 Ok(OwnedFd::from(socket))
361 }
362
363 pub fn udp_bind<Fd: AsFd>(sockfd: Fd, addr: &SocketAddr) -> rustix::io::Result<()> {
364 rustix::net::bind(sockfd, addr).map_err(|error| match error {
365 #[cfg(windows)]
368 Errno::NOBUFS => Errno::ADDRINUSE,
369 _ => error,
370 })
371 }
372
373 pub fn udp_disconnect<Fd: AsFd>(sockfd: Fd) -> rustix::io::Result<()> {
374 match rustix::net::connect_unspec(sockfd) {
375 #[cfg(target_os = "macos")]
387 Err(Errno::INVAL | Errno::AFNOSUPPORT) => Ok(()),
388 r => r,
389 }
390 }
391
392 #[allow(unused_variables)] pub fn set_tcp_reuseaddr<Fd: AsFd>(sockfd: Fd, value: bool) -> rustix::io::Result<()> {
396 #[cfg(not(windows))]
415 sockopt::set_socket_reuseaddr(sockfd, value)?;
416
417 Ok(())
418 }
419
420 pub fn set_tcp_keepidle<Fd: AsFd>(sockfd: Fd, value: Duration) -> rustix::io::Result<()> {
421 if value <= Duration::ZERO {
422 return Err(Errno::INVAL);
424 }
425
426 const MIN_SECS: u64 = 1;
428
429 const MAX_SECS: u64 = i16::MAX as u64;
431
432 sockopt::set_tcp_keepidle(
433 sockfd,
434 value.clamp(Duration::from_secs(MIN_SECS), Duration::from_secs(MAX_SECS)),
435 )
436 }
437
438 pub fn set_tcp_keepintvl<Fd: AsFd>(sockfd: Fd, value: Duration) -> rustix::io::Result<()> {
439 if value <= Duration::ZERO {
440 return Err(Errno::INVAL);
442 }
443
444 const MIN_SECS: u64 = 1;
446
447 const MAX_SECS: u64 = i16::MAX as u64;
449
450 sockopt::set_tcp_keepintvl(
451 sockfd,
452 value.clamp(Duration::from_secs(MIN_SECS), Duration::from_secs(MAX_SECS)),
453 )
454 }
455
456 pub fn set_tcp_keepcnt<Fd: AsFd>(sockfd: Fd, value: u32) -> rustix::io::Result<()> {
457 if value == 0 {
458 return Err(Errno::INVAL);
460 }
461
462 const MIN_CNT: u32 = 1;
463 const MAX_CNT: u32 = i8::MAX as u32;
465
466 sockopt::set_tcp_keepcnt(sockfd, value.clamp(MIN_CNT, MAX_CNT))
467 }
468
469 pub fn get_ip_ttl<Fd: AsFd>(sockfd: Fd) -> rustix::io::Result<u8> {
470 sockopt::get_ip_ttl(sockfd)?
471 .try_into()
472 .map_err(|_| Errno::OPNOTSUPP)
473 }
474
475 pub fn get_ipv6_unicast_hops<Fd: AsFd>(sockfd: Fd) -> rustix::io::Result<u8> {
476 sockopt::get_ipv6_unicast_hops(sockfd)
477 }
478
479 pub fn set_ip_ttl<Fd: AsFd>(sockfd: Fd, value: u8) -> rustix::io::Result<()> {
480 match value {
481 0 => Err(Errno::INVAL),
487 _ => sockopt::set_ip_ttl(sockfd, value.into()),
488 }
489 }
490
491 pub fn set_ipv6_unicast_hops<Fd: AsFd>(sockfd: Fd, value: u8) -> rustix::io::Result<()> {
492 match value {
493 0 => Err(Errno::INVAL), _ => sockopt::set_ipv6_unicast_hops(sockfd, Some(value)),
495 }
496 }
497
498 fn normalize_get_buffer_size(value: usize) -> usize {
499 if cfg!(target_os = "linux") {
500 value / 2
506 } else {
507 value
508 }
509 }
510
511 fn normalize_set_buffer_size(value: usize) -> usize {
512 value.clamp(1, i32::MAX as usize)
513 }
514
515 pub fn get_socket_recv_buffer_size<Fd: AsFd>(sockfd: Fd) -> rustix::io::Result<usize> {
516 let value = sockopt::get_socket_recv_buffer_size(sockfd)?;
517 Ok(normalize_get_buffer_size(value))
518 }
519
520 pub fn get_socket_send_buffer_size<Fd: AsFd>(sockfd: Fd) -> rustix::io::Result<usize> {
521 let value = sockopt::get_socket_send_buffer_size(sockfd)?;
522 Ok(normalize_get_buffer_size(value))
523 }
524
525 pub fn set_socket_recv_buffer_size<Fd: AsFd>(
526 sockfd: Fd,
527 value: usize,
528 ) -> rustix::io::Result<()> {
529 if value == 0 {
530 return Err(Errno::INVAL);
532 }
533
534 let value = normalize_set_buffer_size(value);
535
536 match sockopt::set_socket_recv_buffer_size(sockfd, value) {
537 Err(Errno::NOBUFS) => Ok(()),
548 r => r,
549 }
550 }
551
552 pub fn set_socket_send_buffer_size<Fd: AsFd>(
553 sockfd: Fd,
554 value: usize,
555 ) -> rustix::io::Result<()> {
556 if value == 0 {
557 return Err(Errno::INVAL);
559 }
560
561 let value = normalize_set_buffer_size(value);
562
563 match sockopt::set_socket_send_buffer_size(sockfd, value) {
564 Err(Errno::NOBUFS) => Ok(()), r => r,
566 }
567 }
568}