Skip to content

Commit a2f7f6f

Browse files
committed
Add support for WASI sockets to C API
Add support for WASI sockets in the C API by adding a new API to handle preopening sockets for clients. This uses HashMap instead of Vec for preopened sockets to identify if caller has called in more than once with the same FD number. If so, then we return false so caller is given hint that they are attempting to overwrite an already existing socket FD.
1 parent fef9f64 commit a2f7f6f

2 files changed

Lines changed: 56 additions & 4 deletions

File tree

crates/c-api/include/wasi.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#ifndef WASI_H
88
#define WASI_H
99

10+
#include <stdint.h>
1011
#include "wasm.h"
1112

1213
#ifndef WASI_API_EXTERN
@@ -156,6 +157,19 @@ WASI_API_EXTERN void wasi_config_inherit_stderr(wasi_config_t* config);
156157
*/
157158
WASI_API_EXTERN bool wasi_config_preopen_dir(wasi_config_t* config, const char* path, const char* guest_path);
158159

160+
/**
161+
* \brief Configures a "preopened socket" to be available to WASI APIs.
162+
*
163+
* By default WASI programs do not have access to open up network sockets on
164+
* the host. This API can be used to grant WASI programs access to a network
165+
* socket file descriptor on the host.
166+
*
167+
* The fd_num argument is the number of the file descriptor by which it will be
168+
* known in WASM and the host_port is the IP address and port (e.g.
169+
* "127.0.0.1:8080") requested to be open.
170+
*/
171+
WASI_API_EXTERN bool wasi_config_preopen_socket(wasi_config_t* config, uint32_t fd_num, const char* host_port);
172+
159173
#undef own
160174

161175
#ifdef __cplusplus

crates/c-api/src/wasi.rs

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,27 @@
33
use crate::wasm_byte_vec_t;
44
use anyhow::Result;
55
use cap_std::ambient_authority;
6+
use std::collections::HashMap;
67
use std::ffi::CStr;
78
use std::fs::File;
89
use std::os::raw::{c_char, c_int};
910
use std::path::{Path, PathBuf};
1011
use std::slice;
1112
use wasi_common::pipe::ReadPipe;
1213
use wasmtime_wasi::{
13-
sync::{Dir, WasiCtxBuilder},
14+
sync::{Dir, TcpListener, WasiCtxBuilder},
1415
WasiCtx,
1516
};
1617

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

22+
fn cstr_to_string(c_char: *const c_char) -> String {
23+
let cstr = unsafe { CStr::from_ptr(c_char) };
24+
String::from_utf8_lossy(cstr.to_bytes()).to_string()
25+
}
26+
2127
unsafe fn open_file(path: *const c_char) -> Option<File> {
2228
File::open(cstr_to_path(path)?).ok()
2329
}
@@ -34,7 +40,8 @@ pub struct wasi_config_t {
3440
stdin: WasiConfigReadPipe,
3541
stdout: WasiConfigWritePipe,
3642
stderr: WasiConfigWritePipe,
37-
preopens: Vec<(Dir, PathBuf)>,
43+
preopen_dirs: Vec<(Dir, PathBuf)>,
44+
preopen_sockets: HashMap<u32, TcpListener>,
3845
inherit_args: bool,
3946
inherit_env: bool,
4047
}
@@ -118,9 +125,12 @@ impl wasi_config_t {
118125
builder.stderr(Box::new(file))
119126
}
120127
};
121-
for (dir, path) in self.preopens {
128+
for (dir, path) in self.preopen_dirs {
122129
builder = builder.preopened_dir(dir, path)?;
123130
}
131+
for (fd_num, listener) in self.preopen_sockets {
132+
builder = builder.preopened_socket(fd_num as u32, listener)?;
133+
}
124134
Ok(builder.build())
125135
}
126136
}
@@ -266,7 +276,35 @@ pub unsafe extern "C" fn wasi_config_preopen_dir(
266276
None => return false,
267277
};
268278

269-
(*config).preopens.push((dir, guest_path.to_owned()));
279+
(*config).preopen_dirs.push((dir, guest_path.to_owned()));
280+
281+
true
282+
}
283+
284+
#[no_mangle]
285+
pub unsafe extern "C" fn wasi_config_preopen_socket(
286+
config: &mut wasi_config_t,
287+
fd_num: u32,
288+
tcplisten: *const c_char,
289+
) -> bool {
290+
let address = &cstr_to_string(tcplisten);
291+
let stdlistener = match std::net::TcpListener::bind(address) {
292+
Ok(listener) => listener,
293+
Err(_) => return false,
294+
};
295+
296+
if let Err(_) = stdlistener.set_nonblocking(true) {
297+
return false;
298+
}
299+
300+
// Caller cannot call in more than once with the same FD number so return an error.
301+
if (*config).preopen_sockets.contains_key(&fd_num) {
302+
return false;
303+
}
304+
305+
(*config)
306+
.preopen_sockets
307+
.insert(fd_num, TcpListener::from_std(stdlistener));
270308

271309
true
272310
}

0 commit comments

Comments
 (0)