improve networking in emscripten

This commit is contained in:
Mark McCaskey
2019-07-01 16:36:30 -07:00
parent fada36a72c
commit 623bec001f
7 changed files with 170 additions and 73 deletions

View File

@ -72,14 +72,14 @@ use libc::{
F_GETFD,
F_SETFD,
SOL_SOCKET,
SO_REUSEADDR,
TIOCGWINSZ,
};
#[allow(unused_imports)]
use std::ffi::CStr;
use wasmer_runtime_core::vm::Ctx;
use wasmer_runtime_core::{memory::ptr::WasmPtr, vm::Ctx};
use crate::env::EmSockAddr;
use crate::utils;
#[allow(unused_imports)]
use std::io::Error;
@ -101,9 +101,9 @@ use libc::{fallocate, fdatasync, ftruncate64, lstat, madvise, wait4};
// Another conditional constant for name resolution: Macos et iOS use
// SO_NOSIGPIPE as a setsockopt flag to disable SIGPIPE emission on socket.
// Other platforms do otherwise.
#[cfg(target_os = "darwin")]
#[cfg(target_os = "macos")]
use libc::SO_NOSIGPIPE;
#[cfg(not(target_os = "darwin"))]
#[cfg(not(target_os = "macos"))]
const SO_NOSIGPIPE: c_int = 0;
/// open
@ -444,6 +444,7 @@ pub fn ___syscall102(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
let call: u32 = varargs.get(ctx);
let mut socket_varargs: VarArgs = varargs.get(ctx);
// migrating to EmSockAddr, port being separate here is nice, should update that too
#[repr(C)]
pub struct GuestSockaddrIn {
pub sin_family: sa_family_t, // u16
@ -458,13 +459,6 @@ pub fn ___syscall102(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
pub s_addr: in_addr_t, // u32
}
// debug!("GuestSockaddrIn = {}", size_of::<GuestSockaddrIn>());
pub struct LinuxSockAddr {
pub sa_family: u16,
pub sa_data: [c_char; 14],
}
match call {
1 => {
debug!("socket: socket");
@ -487,6 +481,7 @@ pub fn ___syscall102(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
unimplemented!("non blocking sockets");
}
// why is this here?
type T = u32;
let payload = 1 as *const T as _;
unsafe {
@ -500,7 +495,7 @@ pub fn ___syscall102(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
};
debug!(
"=> domain: {} (AF_INET/2), type: {} (SOCK_STREAM/1), protocol: {} = fd: {}",
"=> domain: {}, type: {}, protocol: {} = fd: {}",
domain, ty, protocol, fd
);
fd as _
@ -555,47 +550,75 @@ pub fn ___syscall102(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
5 => {
debug!("socket: accept");
// accept (socket: c_int, address: *mut sockaddr, address_len: *mut socklen_t) -> c_int
let socket = socket_varargs.get(ctx);
let address_addr: u32 = socket_varargs.get(ctx);
let address_len: u32 = socket_varargs.get(ctx);
let address = emscripten_memory_pointer!(ctx.memory(0), address_addr) as *mut sockaddr;
let socket: i32 = socket_varargs.get(ctx);
let address: WasmPtr<EmSockAddr> = socket_varargs.get(ctx);
let address_len: WasmPtr<u32> = socket_varargs.get(ctx);
debug!(
"=> socket: {}, address: {:?}, address_len: {}",
socket, address, address_len
socket,
address.deref(ctx.memory(0)).unwrap().get(),
address_len.deref(ctx.memory(0)).unwrap().get()
);
let address_len_addr =
emscripten_memory_pointer!(ctx.memory(0), address_len) as *mut socklen_t;
unsafe { address_len.deref_mut(ctx.memory(0)).unwrap().get_mut() };
// let mut address_len_addr: socklen_t = 0;
let fd = unsafe { accept(socket, address, address_len_addr) };
let (fd, host_address) = unsafe {
let mut host_address: sockaddr = std::mem::uninitialized();
let fd = accept(socket, &mut host_address, address_len_addr);
unsafe {
let address_linux =
emscripten_memory_pointer!(ctx.memory(0), address_addr) as *mut LinuxSockAddr;
(*address_linux).sa_family = (*address).sa_family as u16;
(*address_linux).sa_data = (*address).sa_data;
(fd, host_address)
};
let address_addr = unsafe { address.deref_mut(ctx.memory(0)).unwrap().get_mut() };
address_addr.sa_family = host_address.sa_family as _;
address_addr.sa_data = host_address.sa_data.clone();
// why is this here?
// set_cloexec
unsafe {
ioctl(fd, FIOCLEX);
};
debug!("fd: {}", fd);
debug!(
"address: {:?}, len: {}, result fd = {}",
address_addr, address_len_addr, fd
);
fd as _
}
6 => {
debug!("socket: getsockname");
// getsockname (socket: c_int, address: *mut sockaddr, address_len: *mut socklen_t) -> c_int
let socket = socket_varargs.get(ctx);
let address: u32 = socket_varargs.get(ctx);
let address_len: u32 = socket_varargs.get(ctx);
let address = emscripten_memory_pointer!(ctx.memory(0), address) as *mut sockaddr;
let socket: i32 = socket_varargs.get(ctx);
let address: WasmPtr<EmSockAddr> = socket_varargs.get(ctx);
let address_len: WasmPtr<u32> = socket_varargs.get(ctx);
let address_len_addr =
emscripten_memory_pointer!(ctx.memory(0), address_len) as *mut socklen_t;
unsafe { getsockname(socket, address, address_len_addr) }
unsafe { address_len.deref_mut(ctx.memory(0)).unwrap().get_mut() };
let (ret, sock_addr_host) = unsafe {
// read host data into new var
let mut address: sockaddr = std::mem::uninitialized();
let ret = getsockname(
socket,
&mut address as *mut sockaddr,
address_len_addr as *mut u32,
);
(ret, address)
};
// translate from host data into emscripten data
let mut address_mut = unsafe { address.deref_mut(ctx.memory(0)).unwrap().get_mut() };
address_mut.sa_family = sock_addr_host.sa_family as _;
address_mut.sa_data = sock_addr_host.sa_data.clone();
debug!(
"=> socket: {}, address, {:?}, address_len: {}, result = {}",
socket, address_mut, address_len_addr, ret
);
ret
}
7 => {
debug!("socket: getpeername");
@ -647,25 +670,23 @@ pub fn ___syscall102(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
}
14 => {
debug!("socket: setsockopt");
// NOTE: Emscripten seems to be passing the wrong values to this syscall
// level: Em passes 1 as SOL_SOCKET; SOL_SOCKET is 0xffff in BSD
// name: Em passes SO_ACCEPTCONN, but Nginx complains about REUSEADDR
// OSX and BSD have completely different values, be very careful here
// https://github.com/openbsd/src/blob/master/sys/sys/socket.h#L156
// setsockopt (socket: c_int, level: c_int, name: c_int, value: *const c_void, option_len: socklen_t) -> c_int
let socket = socket_varargs.get(ctx);
let level: i32 = socket_varargs.get(ctx);
// SOL_SOCKET = 0xffff (BSD, OSX)
let level = if level == 1 { SOL_SOCKET } else { level };
let name: i32 = socket_varargs.get(ctx);
// SO_REUSEADDR = 0x4 (BSD, OSX)
let name = if name == 2 { SO_REUSEADDR } else { name };
let untranslated_name: i32 = socket_varargs.get(ctx);
let value: u32 = socket_varargs.get(ctx);
let option_len = socket_varargs.get(ctx);
let value_addr = emscripten_memory_pointer!(ctx.memory(0), value) as _; // Endian problem
let option_len: u32 = socket_varargs.get(ctx);
let value_addr =
emscripten_memory_pointer!(ctx.memory(0), value) as *const libc::c_void;
let name: i32 = translate_socket_name_flag(untranslated_name);
let ret = unsafe { setsockopt(socket, level, name, value_addr, option_len) };
debug!("=> socketfd: {}, level: {}, name: {}, value_addr: {:?}, option_len: {} = status: {}", socket, level, name, value_addr, option_len, ret);
debug!("=> socketfd: {}, level: {}, name: {}, value_addr: {:?}, option_len: {} = status: {}", socket, level, untranslated_name, value_addr, option_len, ret);
ret
}
15 => {
@ -673,7 +694,9 @@ pub fn ___syscall102(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
// getsockopt (sockfd: c_int, level: c_int, optname: c_int, optval: *mut c_void, optlen: *mut socklen_t) -> c_int
let socket = socket_varargs.get(ctx);
let level: i32 = socket_varargs.get(ctx);
let name: i32 = socket_varargs.get(ctx);
let level = if level == 1 { SOL_SOCKET } else { level };
let untranslated_name: i32 = socket_varargs.get(ctx);
let name: i32 = translate_socket_name_flag(untranslated_name);
let value: u32 = socket_varargs.get(ctx);
let option_len: u32 = socket_varargs.get(ctx);
let value_addr = emscripten_memory_pointer!(ctx.memory(0), value) as _;
@ -706,6 +729,60 @@ pub fn ___syscall102(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
}
}
/// OSX and BSD have completely different values, we must translate from emscripten's Linuxy
/// value into one that we can pass to native syscalls
fn translate_socket_name_flag(name: i32) -> i32 {
match name {
2 => libc::SO_REUSEADDR,
3 => libc::SO_TYPE,
4 => libc::SO_ERROR,
5 => libc::SO_DONTROUTE,
6 => libc::SO_BROADCAST,
7 => libc::SO_SNDBUF,
8 => libc::SO_RCVBUF,
9 => libc::SO_KEEPALIVE,
10 => libc::SO_OOBINLINE,
13 => libc::SO_LINGER,
18 => libc::SO_RCVLOWAT,
19 => libc::SO_SNDLOWAT,
20 => libc::SO_RCVTIMEO,
21 => libc::SO_SNDTIMEO,
// SO_DEBUG missing
30 => libc::SO_ACCEPTCONN,
otherwise => otherwise,
}
}
#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub struct EmPollFd {
pub fd: i32,
pub events: i16,
pub revents: i16,
}
unsafe impl wasmer_runtime_core::types::ValueType for EmPollFd {}
/// poll
pub fn ___syscall168(ctx: &mut Ctx, _which: i32, mut varargs: VarArgs) -> i32 {
debug!("emscripten::___syscall168(poll)");
let fds: WasmPtr<EmPollFd> = varargs.get(ctx);
let nfds: u32 = varargs.get(ctx);
let timeout: i32 = varargs.get(ctx);
let fds_mut = unsafe { fds.deref_mut(ctx.memory(0)).unwrap().get_mut() };
let ret = unsafe {
libc::poll(
fds_mut as *mut EmPollFd as *mut libc::pollfd,
nfds as _,
timeout,
)
};
ret
}
// pread
pub fn ___syscall180(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int {
debug!("emscripten::___syscall180 (pread) {}", _which);
@ -885,11 +962,11 @@ pub fn ___syscall220(ctx: &mut Ctx, _which: i32, mut varargs: VarArgs) -> i32 {
let dirp = emscripten_memory_pointer!(ctx.memory(0), dirp_addr) as *mut u8;
let mut opened_dirs = &mut get_emscripten_data(ctx).opened_dirs;
let opened_dirs = &mut get_emscripten_data(ctx).opened_dirs;
// need to persist stream across calls?
// let dir: *mut libc::DIR = unsafe { libc::fdopendir(fd) };
let mut dir = &*opened_dirs
let dir = &*opened_dirs
.entry(fd)
.or_insert_with(|| unsafe { Box::new(libc::fdopendir(fd)) });