async_wasi/snapshots/common/net/
mod.rs1#[cfg(all(unix, feature = "async_tokio"))]
2pub mod async_tokio;
3
4pub use super::vfs::*;
5
6use super::{
7 error::Errno,
8 types::{self as wasi_types, __wasi_subscription_t},
9};
10use std::{
11 future::Future,
12 io::{self, Read, Write},
13 net,
14 time::{Duration, SystemTime},
15};
16use wasi_types::{
17 __wasi_clockid_t::{
18 __WASI_CLOCKID_MONOTONIC as CLOCKID_MONOTONIC, __WASI_CLOCKID_REALTIME as CLOCKID_REALTIME,
19 },
20 __wasi_eventtype_t::{
21 __WASI_EVENTTYPE_CLOCK as CLOCK, __WASI_EVENTTYPE_FD_READ as RD,
22 __WASI_EVENTTYPE_FD_WRITE as WR,
23 },
24};
25
26#[derive(Debug, Clone, Copy, Default)]
27pub enum AddressFamily {
28 #[default]
29 Inet4,
30 Inet6,
31}
32
33#[derive(Debug, Clone, Copy, Default)]
34pub enum SocketType {
35 Datagram,
36 #[default]
37 Stream,
38}
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
41pub enum ConnectState {
42 #[default]
43 Empty,
44 Listening,
45 Connected,
46 Connecting,
47}
48
49#[derive(Debug, Clone, Default)]
50pub struct WasiSocketState {
51 pub sock_type: (AddressFamily, SocketType),
52 pub local_addr: Option<net::SocketAddr>,
53 pub peer_addr: Option<net::SocketAddr>,
54 pub bind_device: Vec<u8>,
55 pub backlog: u32,
56 pub shutdown: Option<net::Shutdown>,
57 pub nonblocking: bool,
58 pub so_reuseaddr: bool,
59 pub so_conn_state: ConnectState,
60 pub so_recv_buf_size: usize,
61 pub so_send_buf_size: usize,
62 pub so_recv_timeout: Option<Duration>,
63 pub so_send_timeout: Option<Duration>,
64 pub fs_rights: WASIRights,
65}
66
67#[derive(Debug, Clone, Copy)]
68pub enum SubscriptionFdType {
69 Read(wasi_types::__wasi_userdata_t),
70 Write(wasi_types::__wasi_userdata_t),
71 Both {
72 read: wasi_types::__wasi_userdata_t,
73 write: wasi_types::__wasi_userdata_t,
74 },
75}
76
77#[derive(Debug, Clone, Copy)]
78pub struct SubscriptionFd {
79 pub fd: wasi_types::__wasi_fd_t,
80 pub type_: SubscriptionFdType,
81}
82
83impl SubscriptionFd {
84 pub fn set_write(&mut self, userdata: wasi_types::__wasi_userdata_t) {
85 let read_userdata = match &mut self.type_ {
86 SubscriptionFdType::Read(v) => *v,
87 SubscriptionFdType::Write(v) => {
88 *v = userdata;
89 return;
90 }
91 SubscriptionFdType::Both { read, write } => {
92 *write = userdata;
93 return;
94 }
95 };
96 self.type_ = SubscriptionFdType::Both {
97 read: read_userdata,
98 write: userdata,
99 };
100 }
101
102 pub fn set_read(&mut self, userdata: wasi_types::__wasi_userdata_t) {
103 let write_userdata = match &mut self.type_ {
104 SubscriptionFdType::Write(v) => *v,
105 SubscriptionFdType::Read(v) => {
106 *v = userdata;
107 return;
108 }
109 SubscriptionFdType::Both { read, write } => {
110 *read = userdata;
111 return;
112 }
113 };
114 self.type_ = SubscriptionFdType::Both {
115 read: userdata,
116 write: write_userdata,
117 };
118 }
119}
120
121#[derive(Debug, Clone, Copy)]
122pub struct SubscriptionClock {
123 pub timeout: Option<SystemTime>,
124 pub userdata: wasi_types::__wasi_userdata_t,
125 pub err: Option<Errno>,
126}
127
128#[derive(Debug, Clone, Copy)]
129pub enum Subscription {
130 FD(SubscriptionFd),
131 RealClock(SubscriptionClock),
132}
133
134impl Subscription {
135 pub fn from(s: &__wasi_subscription_t) -> Result<Subscription, Errno> {
136 let userdata = s.userdata;
137 match s.u.tag {
138 CLOCK => {
139 let clock = unsafe { s.u.u.clock };
140 match clock.id {
141 CLOCKID_REALTIME | CLOCKID_MONOTONIC => {
142 if clock.flags == 1 {
143 if let Some(ddl) = std::time::UNIX_EPOCH
144 .checked_add(Duration::from_nanos(clock.timeout + clock.precision))
145 {
146 Ok(Subscription::RealClock(SubscriptionClock {
147 timeout: Some(ddl),
148 userdata,
149 err: None,
150 }))
151 } else {
152 Ok(Subscription::RealClock(SubscriptionClock {
153 timeout: None,
154 userdata,
155 err: Some(Errno::__WASI_ERRNO_INVAL),
156 }))
157 }
158 } else if clock.timeout == 0 {
159 Ok(Subscription::RealClock(SubscriptionClock {
160 timeout: None,
161 userdata,
162 err: None,
163 }))
164 } else {
165 let duration = Duration::from_nanos(clock.timeout + clock.precision);
166
167 let timeout = std::time::SystemTime::now().checked_add(duration);
168
169 Ok(Subscription::RealClock(SubscriptionClock {
170 timeout,
171 userdata,
172 err: None,
173 }))
174 }
175 }
176
177 _ => Ok(Subscription::RealClock(SubscriptionClock {
178 timeout: None,
179 userdata,
180 err: Some(Errno::__WASI_ERRNO_NODEV),
181 })),
182 }
183 }
184 RD => {
185 let fd_read = unsafe { s.u.u.fd_read };
186 Ok(Subscription::FD(SubscriptionFd {
187 fd: fd_read.file_descriptor,
188 type_: SubscriptionFdType::Read(userdata),
189 }))
190 }
191 WR => {
192 let fd_read = unsafe { s.u.u.fd_read };
193 Ok(Subscription::FD(SubscriptionFd {
194 fd: fd_read.file_descriptor,
195 type_: SubscriptionFdType::Write(userdata),
196 }))
197 }
198 _ => Err(Errno::__WASI_ERRNO_INVAL),
199 }
200 }
201}
202
203#[derive(Debug)]
204pub enum PrePoll {
205 OnlyFd(Vec<SubscriptionFd>),
206 OnlyClock(SubscriptionClock),
207 ClockAndFd(SubscriptionClock, Vec<SubscriptionFd>),
208}
209
210impl PrePoll {
211 pub fn from_wasi_subscription(
212 subs: &[wasi_types::__wasi_subscription_t],
213 ) -> Result<Self, Errno> {
214 use std::collections::HashMap;
215 let mut fds = HashMap::with_capacity(subs.len());
216
217 let mut timeout: Option<SubscriptionClock> = None;
218 for s in subs {
219 let s = Subscription::from(s)?;
220 match s {
221 Subscription::FD(fd) => {
222 let type_ = fd.type_;
223
224 fds.entry(fd.fd)
225 .and_modify(|e: &mut SubscriptionFd| match type_ {
226 SubscriptionFdType::Read(data) => e.set_read(data),
227 SubscriptionFdType::Write(data) => e.set_write(data),
228 SubscriptionFdType::Both { read, write } => {
229 e.type_ = SubscriptionFdType::Both { read, write };
230 }
231 })
232 .or_insert(fd);
233 }
234 Subscription::RealClock(clock) => {
235 if clock.err.is_some() {
236 return Ok(PrePoll::OnlyClock(clock));
237 }
238 if clock.timeout.is_none() {
239 return Ok(PrePoll::OnlyClock(clock));
240 }
241
242 if let Some(old_clock) = &mut timeout {
243 let new_timeout = clock.timeout.unwrap();
244 let old_timeout = old_clock.timeout.unwrap();
245
246 if new_timeout < old_timeout {
247 *old_clock = clock
248 }
249 } else {
250 timeout = Some(clock)
251 }
252 }
253 }
254 }
255
256 let fd_vec: Vec<SubscriptionFd> = fds.into_values().collect();
257
258 if let Some(clock) = timeout {
259 if fd_vec.is_empty() {
260 Ok(PrePoll::OnlyClock(clock))
261 } else {
262 Ok(PrePoll::ClockAndFd(clock, fd_vec))
263 }
264 } else {
265 Ok(PrePoll::OnlyFd(fd_vec))
266 }
267 }
268}