Skip to content

Commit b0cc902

Browse files
committed
Hook up std::net to wasi-libc on wasm32-wasip2 target
1 parent 3aae770 commit b0cc902

File tree

3 files changed

+381
-2
lines changed

3 files changed

+381
-2
lines changed

std/src/sys/pal/wasip2/mod.rs

-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ pub mod futex;
2020
#[path = "../wasi/io.rs"]
2121
pub mod io;
2222

23-
#[path = "../wasi/net.rs"]
2423
pub mod net;
2524
#[path = "../wasi/os.rs"]
2625
pub mod os;

std/src/sys/pal/wasip2/net.rs

+379
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,379 @@
1+
#![deny(unsafe_op_in_unsafe_fn)]
2+
3+
use libc::{c_int, c_void, size_t};
4+
5+
use super::fd::WasiFd;
6+
use crate::ffi::CStr;
7+
use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut};
8+
use crate::net::{Shutdown, SocketAddr};
9+
use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
10+
use crate::sys::unsupported;
11+
use crate::sys_common::net::{TcpListener, getsockopt, setsockopt, sockaddr_to_addr};
12+
use crate::sys_common::{AsInner, FromInner, IntoInner};
13+
use crate::time::{Duration, Instant};
14+
use crate::{cmp, mem, str};
15+
16+
pub extern crate libc as netc;
17+
18+
#[allow(non_camel_case_types)]
19+
pub type wrlen_t = size_t;
20+
21+
#[doc(hidden)]
22+
pub trait IsMinusOne {
23+
fn is_minus_one(&self) -> bool;
24+
}
25+
26+
macro_rules! impl_is_minus_one {
27+
($($t:ident)*) => ($(impl IsMinusOne for $t {
28+
fn is_minus_one(&self) -> bool {
29+
*self == -1
30+
}
31+
})*)
32+
}
33+
34+
impl_is_minus_one! { i8 i16 i32 i64 isize }
35+
36+
pub fn cvt<T: IsMinusOne>(t: T) -> crate::io::Result<T> {
37+
if t.is_minus_one() { Err(crate::io::Error::last_os_error()) } else { Ok(t) }
38+
}
39+
40+
pub fn cvt_r<T, F>(mut f: F) -> crate::io::Result<T>
41+
where
42+
T: IsMinusOne,
43+
F: FnMut() -> T,
44+
{
45+
loop {
46+
match cvt(f()) {
47+
Err(ref e) if e.is_interrupted() => {}
48+
other => return other,
49+
}
50+
}
51+
}
52+
53+
pub fn cvt_gai(err: c_int) -> io::Result<()> {
54+
if err == 0 {
55+
return Ok(());
56+
}
57+
58+
if err == netc::EAI_SYSTEM {
59+
return Err(io::Error::last_os_error());
60+
}
61+
62+
let detail = unsafe {
63+
str::from_utf8(CStr::from_ptr(netc::gai_strerror(err)).to_bytes()).unwrap().to_owned()
64+
};
65+
66+
Err(io::Error::new(
67+
io::ErrorKind::Uncategorized,
68+
&format!("failed to lookup address information: {detail}")[..],
69+
))
70+
}
71+
72+
pub fn init() {}
73+
74+
pub struct Socket(WasiFd);
75+
76+
impl Socket {
77+
pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result<Socket> {
78+
let fam = match *addr {
79+
SocketAddr::V4(..) => netc::AF_INET,
80+
SocketAddr::V6(..) => netc::AF_INET6,
81+
};
82+
Socket::new_raw(fam, ty)
83+
}
84+
85+
pub fn new_raw(fam: c_int, ty: c_int) -> io::Result<Socket> {
86+
let fd = cvt(unsafe { netc::socket(fam, ty, 0) })?;
87+
Ok(unsafe { Self::from_raw_fd(fd) })
88+
}
89+
90+
pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> {
91+
let (addr, len) = addr.into_inner();
92+
cvt_r(|| unsafe { netc::connect(self.as_raw_fd(), addr.as_ptr(), len) })?;
93+
Ok(())
94+
}
95+
96+
pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> {
97+
self.set_nonblocking(true)?;
98+
let r = self.connect(addr);
99+
self.set_nonblocking(false)?;
100+
101+
match r {
102+
Ok(_) => return Ok(()),
103+
// there's no ErrorKind for EINPROGRESS
104+
Err(ref e) if e.raw_os_error() == Some(netc::EINPROGRESS) => {}
105+
Err(e) => return Err(e),
106+
}
107+
108+
let mut pollfd = netc::pollfd { fd: self.as_raw_fd(), events: netc::POLLOUT, revents: 0 };
109+
110+
if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 {
111+
return Err(io::Error::ZERO_TIMEOUT);
112+
}
113+
114+
let start = Instant::now();
115+
116+
loop {
117+
let elapsed = start.elapsed();
118+
if elapsed >= timeout {
119+
return Err(io::const_io_error!(io::ErrorKind::TimedOut, "connection timed out"));
120+
}
121+
122+
let timeout = timeout - elapsed;
123+
let mut timeout = timeout
124+
.as_secs()
125+
.saturating_mul(1_000)
126+
.saturating_add(timeout.subsec_nanos() as u64 / 1_000_000);
127+
if timeout == 0 {
128+
timeout = 1;
129+
}
130+
131+
let timeout = cmp::min(timeout, c_int::MAX as u64) as c_int;
132+
133+
match unsafe { netc::poll(&mut pollfd, 1, timeout) } {
134+
-1 => {
135+
let err = io::Error::last_os_error();
136+
if !err.is_interrupted() {
137+
return Err(err);
138+
}
139+
}
140+
0 => {}
141+
_ => {
142+
// WASI poll does not return POLLHUP or POLLERR in revents. Check if the
143+
// connnection actually succeeded and return ok only when the socket is
144+
// ready and no errors were found.
145+
if let Some(e) = self.take_error()? {
146+
return Err(e);
147+
}
148+
149+
return Ok(());
150+
}
151+
}
152+
}
153+
}
154+
155+
pub fn accept(
156+
&self,
157+
storage: *mut netc::sockaddr,
158+
len: *mut netc::socklen_t,
159+
) -> io::Result<Socket> {
160+
let fd = cvt_r(|| unsafe { netc::accept(self.as_raw_fd(), storage, len) })?;
161+
Ok(unsafe { Self::from_raw_fd(fd) })
162+
}
163+
164+
pub fn duplicate(&self) -> io::Result<Socket> {
165+
unsupported()
166+
}
167+
168+
fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: c_int) -> io::Result<()> {
169+
let ret = cvt(unsafe {
170+
netc::recv(
171+
self.as_raw_fd(),
172+
buf.as_mut().as_mut_ptr() as *mut c_void,
173+
buf.capacity(),
174+
flags,
175+
)
176+
})?;
177+
unsafe {
178+
buf.advance_unchecked(ret as usize);
179+
}
180+
Ok(())
181+
}
182+
183+
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
184+
let mut buf = BorrowedBuf::from(buf);
185+
self.recv_with_flags(buf.unfilled(), 0)?;
186+
Ok(buf.len())
187+
}
188+
189+
pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
190+
let mut buf = BorrowedBuf::from(buf);
191+
self.recv_with_flags(buf.unfilled(), netc::MSG_PEEK)?;
192+
Ok(buf.len())
193+
}
194+
195+
pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> {
196+
self.recv_with_flags(buf, 0)
197+
}
198+
199+
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
200+
io::default_read_vectored(|b| self.read(b), bufs)
201+
}
202+
203+
#[inline]
204+
pub fn is_read_vectored(&self) -> bool {
205+
false
206+
}
207+
208+
fn recv_from_with_flags(
209+
&self,
210+
buf: &mut [u8],
211+
flags: c_int,
212+
) -> io::Result<(usize, SocketAddr)> {
213+
let mut storage: netc::sockaddr_storage = unsafe { mem::zeroed() };
214+
let mut addrlen = mem::size_of_val(&storage) as netc::socklen_t;
215+
216+
let n = cvt(unsafe {
217+
netc::recvfrom(
218+
self.as_raw_fd(),
219+
buf.as_mut_ptr() as *mut c_void,
220+
buf.len(),
221+
flags,
222+
core::ptr::addr_of_mut!(storage) as *mut _,
223+
&mut addrlen,
224+
)
225+
})?;
226+
Ok((n as usize, sockaddr_to_addr(&storage, addrlen as usize)?))
227+
}
228+
229+
pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
230+
self.recv_from_with_flags(buf, 0)
231+
}
232+
233+
pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
234+
self.recv_from_with_flags(buf, netc::MSG_PEEK)
235+
}
236+
237+
fn write(&self, buf: &[u8]) -> io::Result<usize> {
238+
let len = cmp::min(buf.len(), <wrlen_t>::MAX as usize) as wrlen_t;
239+
let ret = cvt(unsafe {
240+
netc::send(self.as_raw(), buf.as_ptr() as *const c_void, len, netc::MSG_NOSIGNAL)
241+
})?;
242+
Ok(ret as usize)
243+
}
244+
245+
pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
246+
io::default_write_vectored(|b| self.write(b), bufs)
247+
}
248+
249+
#[inline]
250+
pub fn is_write_vectored(&self) -> bool {
251+
false
252+
}
253+
254+
pub fn set_timeout(&self, dur: Option<Duration>, kind: c_int) -> io::Result<()> {
255+
let timeout = match dur {
256+
Some(dur) => {
257+
if dur.as_secs() == 0 && dur.subsec_nanos() == 0 {
258+
return Err(io::Error::ZERO_TIMEOUT);
259+
}
260+
261+
let secs = dur.as_secs().try_into().unwrap_or(netc::time_t::MAX);
262+
let mut timeout = netc::timeval {
263+
tv_sec: secs,
264+
tv_usec: dur.subsec_micros() as netc::suseconds_t,
265+
};
266+
if timeout.tv_sec == 0 && timeout.tv_usec == 0 {
267+
timeout.tv_usec = 1;
268+
}
269+
timeout
270+
}
271+
None => netc::timeval { tv_sec: 0, tv_usec: 0 },
272+
};
273+
setsockopt(self, netc::SOL_SOCKET, kind, timeout)
274+
}
275+
276+
pub fn timeout(&self, kind: c_int) -> io::Result<Option<Duration>> {
277+
let raw: netc::timeval = getsockopt(self, netc::SOL_SOCKET, kind)?;
278+
if raw.tv_sec == 0 && raw.tv_usec == 0 {
279+
Ok(None)
280+
} else {
281+
let sec = raw.tv_sec as u64;
282+
let nsec = (raw.tv_usec as u32) * 1000;
283+
Ok(Some(Duration::new(sec, nsec)))
284+
}
285+
}
286+
287+
pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
288+
let how = match how {
289+
Shutdown::Write => netc::SHUT_WR,
290+
Shutdown::Read => netc::SHUT_RD,
291+
Shutdown::Both => netc::SHUT_RDWR,
292+
};
293+
cvt(unsafe { netc::shutdown(self.as_raw_fd(), how) })?;
294+
Ok(())
295+
}
296+
297+
pub fn set_linger(&self, _linger: Option<Duration>) -> io::Result<()> {
298+
unsupported()
299+
}
300+
301+
pub fn linger(&self) -> io::Result<Option<Duration>> {
302+
unsupported()
303+
}
304+
305+
pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> {
306+
setsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY, nodelay as c_int)
307+
}
308+
309+
pub fn nodelay(&self) -> io::Result<bool> {
310+
let raw: c_int = getsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY)?;
311+
Ok(raw != 0)
312+
}
313+
314+
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
315+
let mut nonblocking = nonblocking as c_int;
316+
cvt(unsafe { netc::ioctl(self.as_raw_fd(), netc::FIONBIO, &mut nonblocking) }).map(drop)
317+
}
318+
319+
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
320+
let raw: c_int = getsockopt(self, netc::SOL_SOCKET, netc::SO_ERROR)?;
321+
if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) }
322+
}
323+
324+
// This is used by sys_common code to abstract over Windows and Unix.
325+
pub fn as_raw(&self) -> RawFd {
326+
self.as_raw_fd()
327+
}
328+
}
329+
330+
impl AsInner<WasiFd> for Socket {
331+
#[inline]
332+
fn as_inner(&self) -> &WasiFd {
333+
&self.0
334+
}
335+
}
336+
337+
impl IntoInner<WasiFd> for Socket {
338+
fn into_inner(self) -> WasiFd {
339+
self.0
340+
}
341+
}
342+
343+
impl FromInner<WasiFd> for Socket {
344+
fn from_inner(inner: WasiFd) -> Socket {
345+
Socket(inner)
346+
}
347+
}
348+
349+
impl AsFd for Socket {
350+
fn as_fd(&self) -> BorrowedFd<'_> {
351+
self.0.as_fd()
352+
}
353+
}
354+
355+
impl AsRawFd for Socket {
356+
#[inline]
357+
fn as_raw_fd(&self) -> RawFd {
358+
self.0.as_raw_fd()
359+
}
360+
}
361+
362+
impl IntoRawFd for Socket {
363+
fn into_raw_fd(self) -> RawFd {
364+
self.0.into_raw_fd()
365+
}
366+
}
367+
368+
impl FromRawFd for Socket {
369+
unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
370+
unsafe { Self(FromRawFd::from_raw_fd(raw_fd)) }
371+
}
372+
}
373+
374+
impl AsInner<Socket> for TcpListener {
375+
#[inline]
376+
fn as_inner(&self) -> &Socket {
377+
&self.socket()
378+
}
379+
}

std/src/sys_common/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ cfg_if::cfg_if! {
3232
all(unix, not(target_os = "l4re")),
3333
windows,
3434
target_os = "hermit",
35-
target_os = "solid_asp3"
35+
target_os = "solid_asp3",
36+
all(target_os = "wasi", target_env = "p2")
3637
))] {
3738
pub mod net;
3839
} else {

0 commit comments

Comments
 (0)