Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions lib/virtual-net/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -813,6 +813,7 @@ pub struct UnsupportedVirtualNetworking {}
#[async_trait::async_trait]
impl VirtualNetworking for UnsupportedVirtualNetworking {}

#[non_exhaustive]
#[derive(Error, Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum NetworkError {
/// The handle given was not usable
Expand Down Expand Up @@ -888,6 +889,12 @@ pub enum NetworkError {
/// The operation is not supported.
#[error("unsupported")]
Unsupported,
/// The network containing the remote host is not reachable.
#[error("network unreachable")]
NetworkUnreachable,
/// The remote host is not reachable.
#[error("host unreachable")]
HostUnreachable,
Comment on lines 889 to +897

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... oh. Right. Well that's fun ^^'

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

virtual-net is not stable yet and breaking changes aren't unexpected. Indeed, with each release, we bump the minor.

/// Some other unhandled error. If you see this, it's probably a bug.
#[error("unknown error found")]
UnknownError,
Expand Down Expand Up @@ -917,6 +924,8 @@ pub fn io_err_into_net_error(net_error: std::io::Error) -> NetworkError {
ErrorKind::TimedOut => NetworkError::TimedOut,
ErrorKind::UnexpectedEof => NetworkError::UnexpectedEof,
ErrorKind::WouldBlock => NetworkError::WouldBlock,
ErrorKind::NetworkUnreachable => NetworkError::NetworkUnreachable,
ErrorKind::HostUnreachable => NetworkError::HostUnreachable,
ErrorKind::WriteZero => NetworkError::WriteZero,
ErrorKind::Unsupported => NetworkError::Unsupported,

Expand All @@ -938,6 +947,8 @@ pub fn io_err_into_net_error(net_error: std::io::Error) -> NetworkError {
libc::EINVAL => NetworkError::InvalidInput,
libc::EMSGSIZE => NetworkError::MessageSize,
libc::EPIPE => NetworkError::BrokenPipe,
libc::ENETUNREACH => NetworkError::NetworkUnreachable,
libc::EHOSTUNREACH => NetworkError::HostUnreachable,
err => {
tracing::trace!("unknown os error {}", err);
NetworkError::UnknownError
Expand Down Expand Up @@ -974,6 +985,8 @@ pub fn net_error_into_io_err(net_error: NetworkError) -> std::io::Error {
NetworkError::TimedOut => ErrorKind::TimedOut.into(),
NetworkError::UnexpectedEof => ErrorKind::UnexpectedEof.into(),
NetworkError::WouldBlock => ErrorKind::WouldBlock.into(),
NetworkError::NetworkUnreachable => ErrorKind::NetworkUnreachable.into(),
NetworkError::HostUnreachable => ErrorKind::HostUnreachable.into(),
NetworkError::WriteZero => ErrorKind::WriteZero.into(),
NetworkError::Unsupported => ErrorKind::Unsupported.into(),
NetworkError::UnknownError => ErrorKind::BrokenPipe.into(),
Expand Down
3 changes: 3 additions & 0 deletions lib/wasix/src/net/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -396,10 +396,13 @@ pub fn net_error_into_wasi_err(net_error: NetworkError) -> Errno {
NetworkError::TimedOut => Errno::Timedout,
NetworkError::UnexpectedEof => Errno::Proto,
NetworkError::WouldBlock => Errno::Again,
NetworkError::NetworkUnreachable => Errno::Netunreach,
NetworkError::HostUnreachable => Errno::Hostunreach,
NetworkError::WriteZero => Errno::Nospc,
NetworkError::TooManyOpenFiles => Errno::Mfile,
NetworkError::InsufficientMemory => Errno::Nomem,
NetworkError::Unsupported => Errno::Notsup,
NetworkError::UnknownError => Errno::Io,
_ => Errno::Io,
}
}
13 changes: 8 additions & 5 deletions lib/wasix/src/syscalls/wasix/sock_connect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,13 @@ pub fn sock_connect<M: MemorySize>(

fn nonblocking_connect_result(status: crate::net::socket::WasiSocketStatus) -> Result<(), Errno> {
match status {
crate::net::socket::WasiSocketStatus::Opening => Err(Errno::Inprogress),
// This is called immediately after initiating a nonblocking connect.
// A failure observed here is asynchronous and must remain available
// via SO_ERROR, so report EINPROGRESS rather than consume it.
crate::net::socket::WasiSocketStatus::Opening
| crate::net::socket::WasiSocketStatus::Closed
| crate::net::socket::WasiSocketStatus::Failed => Err(Errno::Inprogress),
crate::net::socket::WasiSocketStatus::Opened => Ok(()),
crate::net::socket::WasiSocketStatus::Closed
| crate::net::socket::WasiSocketStatus::Failed => Err(Errno::Notconn),
}
}

Expand Down Expand Up @@ -116,11 +119,11 @@ mod tests {
assert_eq!(nonblocking_connect_result(WasiSocketStatus::Opened), Ok(()));
assert_eq!(
nonblocking_connect_result(WasiSocketStatus::Failed),
Err(Errno::Notconn)
Err(Errno::Inprogress)
);
assert_eq!(
nonblocking_connect_result(WasiSocketStatus::Closed),
Err(Errno::Notconn)
Err(Errno::Inprogress)
);
}
}
50 changes: 38 additions & 12 deletions lib/wasix/tests/wasm_tests/socket/get-last-socket-error/main.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
//#MinimalLibc: v2026-06-18.1
//#ExpectedStdout: SO_ERROR: Connection refused, Success

#include <arpa/inet.h>
#include <errno.h>
Expand All @@ -11,6 +10,10 @@

int main(void) {
int fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
if (fd < 0) {
perror("socket");
return 1;
}

struct sockaddr_in addr = {0};
addr.sin_family = AF_INET;
Expand All @@ -19,38 +22,61 @@ int main(void) {

errno = 0;
int connect_res = connect(fd, (struct sockaddr*)&addr, sizeof(addr));
if (connect_res == 0 ||
(errno != EINPROGRESS && errno != EWOULDBLOCK && errno != ENOTCONN)) {
fprintf(stderr, "connect did not enter async failure path: %s\n",
strerror(errno));
/* Even if the host rejects the connection immediately, a nonblocking
connect reports EINPROGRESS and leaves the failure for SO_ERROR. */
if (connect_res != -1 || errno != EINPROGRESS) {
fprintf(stderr,
"connect returned %d with errno %d (%s), expected -1 with "
"EINPROGRESS\n",
connect_res, errno, strerror(errno));
close(fd);
return 1;
}

struct pollfd pfd = {.fd = fd, .events = POLLOUT};
poll(&pfd, 1, 1000);
int poll_res = poll(&pfd, 1, 1000);
if (poll_res != 1) {
if (poll_res < 0) {
perror("poll");
} else {
fprintf(stderr, "poll timed out waiting for the connect result\n");
}
close(fd);
return 1;
}

int err = 0;
socklen_t errlen = sizeof(err);
int res = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errlen);

if (res) {
if (res < 0) {
perror("getsockopt(SO_ERROR)");
close(fd);
return 1;
}
if (err != ECONNREFUSED) {
fprintf(stderr, "SO_ERROR returned %d (%s), expected ECONNREFUSED\n", err,
strerror(err));
close(fd);
fprintf(stderr, "Cannot get socket error\n");
return 1;
}

int err2 = 0;
socklen_t errlen2 = sizeof(err);
res = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err2, &errlen2);

if (res) {
if (res < 0) {
perror("getsockopt(SO_ERROR) after clearing");
close(fd);
return 1;
}
if (err2 != 0) {
fprintf(stderr, "second SO_ERROR returned %d (%s), expected success\n",
err2, strerror(err2));
close(fd);
fprintf(stderr, "Cannot get socket error (2)\n");
return 1;
}

close(fd);
printf("SO_ERROR: %s, %s\n", strerror(err), strerror(err2));

return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//#MinimalLibc: v2026-06-18.1

#include <arpa/inet.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>

int main(void) {
int fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
if (fd < 0) {
perror("socket");
return 1;
}

struct sockaddr_in addr = {0};
addr.sin_family = AF_INET;
addr.sin_port = htons(9);
addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);

errno = 0;
int connect_res = connect(fd, (struct sockaddr*)&addr, sizeof(addr));
if (connect_res != -1 || errno != ENETUNREACH) {
fprintf(stderr,
"connect returned %d with errno %d (%s), expected -1 with "
"ENETUNREACH\n",
connect_res, errno, strerror(errno));
close(fd);
return 1;
}

close(fd);
return 0;
}
Loading