1use crate::p2::bindings::sockets::network::{
2 self, ErrorCode, IpAddress, IpAddressFamily, IpSocketAddress, Ipv4SocketAddress,
3 Ipv6SocketAddress,
4};
5use crate::p2::network::{from_ipv4_addr, from_ipv6_addr, to_ipv4_addr, to_ipv6_addr};
6use crate::p2::{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::p2::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(
91 unreachable_patterns,
92 reason = "EWOULDBLOCK and EAGAIN can have the same value"
93 )]
94 Errno::AGAIN => ErrorCode::WouldBlock,
95 Errno::INTR => ErrorCode::WouldBlock,
96 #[cfg(not(windows))]
97 Errno::PERM => ErrorCode::AccessDenied,
98 Errno::ACCESS => ErrorCode::AccessDenied,
99 Errno::ADDRINUSE => ErrorCode::AddressInUse,
100 Errno::ADDRNOTAVAIL => ErrorCode::AddressNotBindable,
101 Errno::ALREADY => ErrorCode::ConcurrencyConflict,
102 Errno::TIMEDOUT => ErrorCode::Timeout,
103 Errno::CONNREFUSED => ErrorCode::ConnectionRefused,
104 Errno::CONNRESET => ErrorCode::ConnectionReset,
105 Errno::CONNABORTED => ErrorCode::ConnectionAborted,
106 Errno::INVAL => ErrorCode::InvalidArgument,
107 Errno::HOSTUNREACH => ErrorCode::RemoteUnreachable,
108 Errno::HOSTDOWN => ErrorCode::RemoteUnreachable,
109 Errno::NETDOWN => ErrorCode::RemoteUnreachable,
110 Errno::NETUNREACH => ErrorCode::RemoteUnreachable,
111 #[cfg(target_os = "linux")]
112 Errno::NONET => ErrorCode::RemoteUnreachable,
113 Errno::ISCONN => ErrorCode::InvalidState,
114 Errno::NOTCONN => ErrorCode::InvalidState,
115 Errno::DESTADDRREQ => ErrorCode::InvalidState,
116 #[cfg(not(windows))]
117 Errno::NFILE => ErrorCode::NewSocketLimit,
118 Errno::MFILE => ErrorCode::NewSocketLimit,
119 Errno::MSGSIZE => ErrorCode::DatagramTooLarge,
120 #[cfg(not(windows))]
121 Errno::NOMEM => ErrorCode::OutOfMemory,
122 Errno::NOBUFS => ErrorCode::OutOfMemory,
123 Errno::OPNOTSUPP => ErrorCode::NotSupported,
124 Errno::NOPROTOOPT => ErrorCode::NotSupported,
125 Errno::PFNOSUPPORT => ErrorCode::NotSupported,
126 Errno::PROTONOSUPPORT => ErrorCode::NotSupported,
127 Errno::PROTOTYPE => ErrorCode::NotSupported,
128 Errno::SOCKTNOSUPPORT => ErrorCode::NotSupported,
129 Errno::AFNOSUPPORT => ErrorCode::NotSupported,
130
131 _ => {
133 tracing::debug!("unknown I/O error: {value}");
134 ErrorCode::Unknown
135 }
136 }
137 }
138}
139
140impl From<std::net::IpAddr> for IpAddress {
141 fn from(addr: std::net::IpAddr) -> Self {
142 match addr {
143 std::net::IpAddr::V4(v4) => Self::Ipv4(from_ipv4_addr(v4)),
144 std::net::IpAddr::V6(v6) => Self::Ipv6(from_ipv6_addr(v6)),
145 }
146 }
147}
148
149impl From<IpSocketAddress> for std::net::SocketAddr {
150 fn from(addr: IpSocketAddress) -> Self {
151 match addr {
152 IpSocketAddress::Ipv4(ipv4) => Self::V4(ipv4.into()),
153 IpSocketAddress::Ipv6(ipv6) => Self::V6(ipv6.into()),
154 }
155 }
156}
157
158impl From<std::net::SocketAddr> for IpSocketAddress {
159 fn from(addr: std::net::SocketAddr) -> Self {
160 match addr {
161 std::net::SocketAddr::V4(v4) => Self::Ipv4(v4.into()),
162 std::net::SocketAddr::V6(v6) => Self::Ipv6(v6.into()),
163 }
164 }
165}
166
167impl From<Ipv4SocketAddress> for std::net::SocketAddrV4 {
168 fn from(addr: Ipv4SocketAddress) -> Self {
169 Self::new(to_ipv4_addr(addr.address), addr.port)
170 }
171}
172
173impl From<std::net::SocketAddrV4> for Ipv4SocketAddress {
174 fn from(addr: std::net::SocketAddrV4) -> Self {
175 Self {
176 address: from_ipv4_addr(*addr.ip()),
177 port: addr.port(),
178 }
179 }
180}
181
182impl From<Ipv6SocketAddress> for std::net::SocketAddrV6 {
183 fn from(addr: Ipv6SocketAddress) -> Self {
184 Self::new(
185 to_ipv6_addr(addr.address),
186 addr.port,
187 addr.flow_info,
188 addr.scope_id,
189 )
190 }
191}
192
193impl From<std::net::SocketAddrV6> for Ipv6SocketAddress {
194 fn from(addr: std::net::SocketAddrV6) -> Self {
195 Self {
196 address: from_ipv6_addr(*addr.ip()),
197 port: addr.port(),
198 flow_info: addr.flowinfo(),
199 scope_id: addr.scope_id(),
200 }
201 }
202}
203
204impl std::net::ToSocketAddrs for IpSocketAddress {
205 type Iter = <std::net::SocketAddr as std::net::ToSocketAddrs>::Iter;
206
207 fn to_socket_addrs(&self) -> io::Result<Self::Iter> {
208 std::net::SocketAddr::from(*self).to_socket_addrs()
209 }
210}
211
212impl std::net::ToSocketAddrs for Ipv4SocketAddress {
213 type Iter = <std::net::SocketAddrV4 as std::net::ToSocketAddrs>::Iter;
214
215 fn to_socket_addrs(&self) -> io::Result<Self::Iter> {
216 std::net::SocketAddrV4::from(*self).to_socket_addrs()
217 }
218}
219
220impl std::net::ToSocketAddrs for Ipv6SocketAddress {
221 type Iter = <std::net::SocketAddrV6 as std::net::ToSocketAddrs>::Iter;
222
223 fn to_socket_addrs(&self) -> io::Result<Self::Iter> {
224 std::net::SocketAddrV6::from(*self).to_socket_addrs()
225 }
226}
227
228impl From<IpAddressFamily> for cap_net_ext::AddressFamily {
229 fn from(family: IpAddressFamily) -> Self {
230 match family {
231 IpAddressFamily::Ipv4 => cap_net_ext::AddressFamily::Ipv4,
232 IpAddressFamily::Ipv6 => cap_net_ext::AddressFamily::Ipv6,
233 }
234 }
235}
236
237impl From<cap_net_ext::AddressFamily> for IpAddressFamily {
238 fn from(family: cap_net_ext::AddressFamily) -> Self {
239 match family {
240 cap_net_ext::AddressFamily::Ipv4 => IpAddressFamily::Ipv4,
241 cap_net_ext::AddressFamily::Ipv6 => IpAddressFamily::Ipv6,
242 }
243 }
244}
245
246pub(crate) mod util {
247 use std::io;
248 use std::net::{IpAddr, Ipv6Addr, SocketAddr};
249 use std::time::Duration;
250
251 use crate::net::SocketAddressFamily;
252 use cap_net_ext::{AddressFamily, Blocking, UdpSocketExt};
253 use rustix::fd::{AsFd, OwnedFd};
254 use rustix::io::Errno;
255 use rustix::net::sockopt;
256
257 pub fn validate_unicast(addr: &SocketAddr) -> io::Result<()> {
258 match to_canonical(&addr.ip()) {
259 IpAddr::V4(ipv4) => {
260 if ipv4.is_multicast() || ipv4.is_broadcast() {
261 Err(io::Error::new(
262 io::ErrorKind::InvalidInput,
263 "Both IPv4 broadcast and multicast addresses are not supported",
264 ))
265 } else {
266 Ok(())
267 }
268 }
269 IpAddr::V6(ipv6) => {
270 if ipv6.is_multicast() {
271 Err(io::Error::new(
272 io::ErrorKind::InvalidInput,
273 "IPv6 multicast addresses are not supported",
274 ))
275 } else {
276 Ok(())
277 }
278 }
279 }
280 }
281
282 pub fn validate_remote_address(addr: &SocketAddr) -> io::Result<()> {
283 if to_canonical(&addr.ip()).is_unspecified() {
284 return Err(io::Error::new(
285 io::ErrorKind::InvalidInput,
286 "Remote address may not be `0.0.0.0` or `::`",
287 ));
288 }
289
290 if addr.port() == 0 {
291 return Err(io::Error::new(
292 io::ErrorKind::InvalidInput,
293 "Remote port may not be 0",
294 ));
295 }
296
297 Ok(())
298 }
299
300 pub fn validate_address_family(
301 addr: &SocketAddr,
302 socket_family: &SocketAddressFamily,
303 ) -> io::Result<()> {
304 match (socket_family, addr.ip()) {
305 (SocketAddressFamily::Ipv4, IpAddr::V4(_)) => Ok(()),
306 (SocketAddressFamily::Ipv6, IpAddr::V6(ipv6)) => {
307 if is_deprecated_ipv4_compatible(&ipv6) {
308 Err(io::Error::new(
313 io::ErrorKind::InvalidInput,
314 "IPv4-compatible IPv6 addresses are not supported",
315 ))
316 } else if ipv6.to_ipv4_mapped().is_some() {
317 Err(io::Error::new(
318 io::ErrorKind::InvalidInput,
319 "IPv4-mapped IPv6 address passed to an IPv6-only socket",
320 ))
321 } else {
322 Ok(())
323 }
324 }
325 _ => Err(io::Error::new(
326 io::ErrorKind::InvalidInput,
327 "Address family mismatch",
328 )),
329 }
330 }
331
332 pub fn to_canonical(addr: &IpAddr) -> IpAddr {
334 match addr {
335 IpAddr::V4(ipv4) => IpAddr::V4(*ipv4),
336 IpAddr::V6(ipv6) => {
337 if let Some(ipv4) = ipv6.to_ipv4_mapped() {
338 IpAddr::V4(ipv4)
339 } else {
340 IpAddr::V6(*ipv6)
341 }
342 }
343 }
344 }
345
346 fn is_deprecated_ipv4_compatible(addr: &Ipv6Addr) -> bool {
347 matches!(addr.segments(), [0, 0, 0, 0, 0, 0, _, _])
348 && *addr != Ipv6Addr::UNSPECIFIED
349 && *addr != Ipv6Addr::LOCALHOST
350 }
351
352 pub fn udp_socket(family: AddressFamily, blocking: Blocking) -> io::Result<OwnedFd> {
357 let socket = cap_std::net::UdpSocket::new(family, blocking)?;
363 Ok(OwnedFd::from(socket))
364 }
365
366 pub fn udp_bind<Fd: AsFd>(sockfd: Fd, addr: &SocketAddr) -> rustix::io::Result<()> {
367 rustix::net::bind(sockfd, addr).map_err(|error| match error {
368 #[cfg(windows)]
371 Errno::NOBUFS => Errno::ADDRINUSE,
372 _ => error,
373 })
374 }
375
376 pub fn udp_disconnect<Fd: AsFd>(sockfd: Fd) -> rustix::io::Result<()> {
377 match rustix::net::connect_unspec(sockfd) {
378 #[cfg(target_os = "macos")]
390 Err(Errno::INVAL | Errno::AFNOSUPPORT) => Ok(()),
391 r => r,
392 }
393 }
394
395 pub fn set_tcp_reuseaddr<Fd: AsFd>(sockfd: Fd, value: bool) -> rustix::io::Result<()> {
398 #[cfg(not(windows))]
417 sockopt::set_socket_reuseaddr(sockfd, value)?;
418 #[cfg(windows)]
419 let _ = (sockfd, value);
420
421 Ok(())
422 }
423
424 pub fn set_tcp_keepidle<Fd: AsFd>(sockfd: Fd, value: Duration) -> rustix::io::Result<()> {
425 if value <= Duration::ZERO {
426 return Err(Errno::INVAL);
428 }
429
430 const MIN_SECS: u64 = 1;
432
433 const MAX_SECS: u64 = i16::MAX as u64;
435
436 sockopt::set_tcp_keepidle(
437 sockfd,
438 value.clamp(Duration::from_secs(MIN_SECS), Duration::from_secs(MAX_SECS)),
439 )
440 }
441
442 pub fn set_tcp_keepintvl<Fd: AsFd>(sockfd: Fd, value: Duration) -> rustix::io::Result<()> {
443 if value <= Duration::ZERO {
444 return Err(Errno::INVAL);
446 }
447
448 const MIN_SECS: u64 = 1;
450
451 const MAX_SECS: u64 = i16::MAX as u64;
453
454 sockopt::set_tcp_keepintvl(
455 sockfd,
456 value.clamp(Duration::from_secs(MIN_SECS), Duration::from_secs(MAX_SECS)),
457 )
458 }
459
460 pub fn set_tcp_keepcnt<Fd: AsFd>(sockfd: Fd, value: u32) -> rustix::io::Result<()> {
461 if value == 0 {
462 return Err(Errno::INVAL);
464 }
465
466 const MIN_CNT: u32 = 1;
467 const MAX_CNT: u32 = i8::MAX as u32;
469
470 sockopt::set_tcp_keepcnt(sockfd, value.clamp(MIN_CNT, MAX_CNT))
471 }
472
473 pub fn get_ip_ttl<Fd: AsFd>(sockfd: Fd) -> rustix::io::Result<u8> {
474 sockopt::ip_ttl(sockfd)?
475 .try_into()
476 .map_err(|_| Errno::OPNOTSUPP)
477 }
478
479 pub fn get_ipv6_unicast_hops<Fd: AsFd>(sockfd: Fd) -> rustix::io::Result<u8> {
480 sockopt::ipv6_unicast_hops(sockfd)
481 }
482
483 pub fn set_ip_ttl<Fd: AsFd>(sockfd: Fd, value: u8) -> rustix::io::Result<()> {
484 match value {
485 0 => Err(Errno::INVAL),
491 _ => sockopt::set_ip_ttl(sockfd, value.into()),
492 }
493 }
494
495 pub fn set_ipv6_unicast_hops<Fd: AsFd>(sockfd: Fd, value: u8) -> rustix::io::Result<()> {
496 match value {
497 0 => Err(Errno::INVAL), _ => sockopt::set_ipv6_unicast_hops(sockfd, Some(value)),
499 }
500 }
501
502 fn normalize_get_buffer_size(value: usize) -> usize {
503 if cfg!(target_os = "linux") {
504 value / 2
510 } else {
511 value
512 }
513 }
514
515 fn normalize_set_buffer_size(value: usize) -> usize {
516 value.clamp(1, i32::MAX as usize)
517 }
518
519 pub fn get_socket_recv_buffer_size<Fd: AsFd>(sockfd: Fd) -> rustix::io::Result<usize> {
520 let value = sockopt::socket_recv_buffer_size(sockfd)?;
521 Ok(normalize_get_buffer_size(value))
522 }
523
524 pub fn get_socket_send_buffer_size<Fd: AsFd>(sockfd: Fd) -> rustix::io::Result<usize> {
525 let value = sockopt::socket_send_buffer_size(sockfd)?;
526 Ok(normalize_get_buffer_size(value))
527 }
528
529 pub fn set_socket_recv_buffer_size<Fd: AsFd>(
530 sockfd: Fd,
531 value: usize,
532 ) -> rustix::io::Result<()> {
533 if value == 0 {
534 return Err(Errno::INVAL);
536 }
537
538 let value = normalize_set_buffer_size(value);
539
540 match sockopt::set_socket_recv_buffer_size(sockfd, value) {
541 Err(Errno::NOBUFS) => Ok(()),
552 r => r,
553 }
554 }
555
556 pub fn set_socket_send_buffer_size<Fd: AsFd>(
557 sockfd: Fd,
558 value: usize,
559 ) -> rustix::io::Result<()> {
560 if value == 0 {
561 return Err(Errno::INVAL);
563 }
564
565 let value = normalize_set_buffer_size(value);
566
567 match sockopt::set_socket_send_buffer_size(sockfd, value) {
568 Err(Errno::NOBUFS) => Ok(()), r => r,
570 }
571 }
572}