Skip to content
Merged
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
14 changes: 14 additions & 0 deletions crates/c-api/include/wasi.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#ifndef WASI_H
#define WASI_H

#include <stdint.h>
#include "wasm.h"

#ifndef WASI_API_EXTERN
Expand Down Expand Up @@ -156,6 +157,19 @@ WASI_API_EXTERN void wasi_config_inherit_stderr(wasi_config_t* config);
*/
WASI_API_EXTERN bool wasi_config_preopen_dir(wasi_config_t* config, const char* path, const char* guest_path);

/**
* \brief Configures a "preopened" listen socket to be available to WASI APIs.
*
* By default WASI programs do not have access to open up network sockets on
* the host. This API can be used to grant WASI programs access to a network
* socket file descriptor on the host.
*
* The fd_num argument is the number of the file descriptor by which it will be
* known in WASM and the host_port is the IP address and port (e.g.
* "127.0.0.1:8080") requested to listen on.
*/
WASI_API_EXTERN bool wasi_config_preopen_socket(wasi_config_t* config, uint32_t fd_num, const char* host_port);

#undef own

#ifdef __cplusplus
Expand Down
48 changes: 44 additions & 4 deletions crates/c-api/src/wasi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,26 @@
use crate::wasm_byte_vec_t;
use anyhow::Result;
use cap_std::ambient_authority;
use std::collections::HashMap;
use std::ffi::CStr;
use std::fs::File;
use std::os::raw::{c_char, c_int};
use std::path::{Path, PathBuf};
use std::slice;
use wasi_common::pipe::ReadPipe;
use wasmtime_wasi::{
sync::{Dir, WasiCtxBuilder},
sync::{Dir, TcpListener, WasiCtxBuilder},
WasiCtx,
};

unsafe fn cstr_to_path<'a>(path: *const c_char) -> Option<&'a Path> {
CStr::from_ptr(path).to_str().map(Path::new).ok()
}

unsafe fn cstr_to_str<'a>(s: *const c_char) -> Option<&'a str> {
CStr::from_ptr(s).to_str().ok()
}

unsafe fn open_file(path: *const c_char) -> Option<File> {
File::open(cstr_to_path(path)?).ok()
}
Expand All @@ -34,7 +39,8 @@ pub struct wasi_config_t {
stdin: WasiConfigReadPipe,
stdout: WasiConfigWritePipe,
stderr: WasiConfigWritePipe,
preopens: Vec<(Dir, PathBuf)>,
preopen_dirs: Vec<(Dir, PathBuf)>,
preopen_sockets: HashMap<u32, TcpListener>,
inherit_args: bool,
inherit_env: bool,
}
Expand Down Expand Up @@ -118,9 +124,12 @@ impl wasi_config_t {
builder.stderr(Box::new(file))
}
};
for (dir, path) in self.preopens {
for (dir, path) in self.preopen_dirs {
builder = builder.preopened_dir(dir, path)?;
}
for (fd_num, listener) in self.preopen_sockets {
builder = builder.preopened_socket(fd_num, listener)?;
}
Ok(builder.build())
}
}
Expand Down Expand Up @@ -266,7 +275,38 @@ pub unsafe extern "C" fn wasi_config_preopen_dir(
None => return false,
};

(*config).preopens.push((dir, guest_path.to_owned()));
(*config).preopen_dirs.push((dir, guest_path.to_owned()));

true
}

#[no_mangle]
pub unsafe extern "C" fn wasi_config_preopen_socket(
config: &mut wasi_config_t,
fd_num: u32,
host_port: *const c_char,
) -> bool {
let address = match cstr_to_str(host_port) {
Some(s) => s,
None => return false,
};
let listener = match std::net::TcpListener::bind(address) {
Ok(listener) => listener,
Err(_) => return false,
};

if let Err(_) = listener.set_nonblocking(true) {
return false;
}

// Caller cannot call in more than once with the same FD number so return an error.
if (*config).preopen_sockets.contains_key(&fd_num) {
return false;
}

(*config)
.preopen_sockets
.insert(fd_num, TcpListener::from_std(listener));

true
}