Skip to content
This repository was archived by the owner on Jul 16, 2020. It is now read-only.

Add 'wasmtime: custom syscall' and 'wasmtime: UDP echo-server' demos #33

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
1,305 changes: 999 additions & 306 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
[workspace]

members = [
"common",
"amd-sev",
"wasmtime-basic",
"wasmtime-native-embed",
"wasmtime-custom-syscall",
"wasmtime-udp-echoserver",
]
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -15,3 +15,9 @@ A demonstration of remote attestation for an SGX enclave. The attesting enclave
Quoting Enclave, which verifies the Report and signs it with its Attestation Key, producing a Quote. The Quote is
sent off-platform to the tenant, who can verify the Quote with a certificate chain from Intel. An attestation daemon
(located on the enclave's same platform) communicates between the tenant and enclave.

## [Wasmtime - Simple Custom Syscall](wasmtime-custom-syscall)
Providing a simple _println_ syscall to a pre-compiled WASM binary written in Rust.

## [Wasmtime - UDP Echo-Server](wasmtime-udp-echoserver)
Providing a rudimentary UDP implementation to a pre-compiled WASM binary written in Rust, which implements a UDP Echo-Server on top of the provided syscalls.
9 changes: 9 additions & 0 deletions common/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "common"
version = "0.1.0"
authors = ["Stefan Junker <mail@stefanjunker.de>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
1 change: 1 addition & 0 deletions common/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod wasm;
86 changes: 86 additions & 0 deletions common/src/wasm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
pub mod enarx_syscalls {
use std::convert::TryInto;

#[link(wasm_import_module = "enarx_syscalls_stdio")]
extern "C" {
fn __print_str(ptr: *const u8, len: u64);
}

#[link(wasm_import_module = "enarx_syscalls_net")]
extern "C" {
fn __socket_udp_bind(ptr: *const u8, len: u64);

fn __socket_udp_receive(
data_ptr: *mut u8,
data_len: u64,
src_ptr: *mut u8,
src_len: u64,
) -> u64;

fn __socket_udp_send_to(
data_ptr: *const u8,
data_len: u64,
dst_ptr: *const u8,
dst_len: u64,
) -> u64;
}

fn _print_str(s: &str) {
unsafe { __print_str(s.as_ptr(), s.len().try_into().unwrap()) }
}

pub fn print(s: &str) {
_print_str(s)
}

pub fn println(s: &str) {
_print_str(&format!("{}\n", s));
}

pub fn socket_udp_bind(addr: &str) {
unsafe { __socket_udp_bind(addr.as_ptr(), addr.len().try_into().unwrap()) }
}

pub fn socket_udp_receive(content_buf: &mut [u8], source_buf: &mut [u8]) -> u64 {
unsafe {
__socket_udp_receive(
content_buf.as_mut_ptr(),
content_buf.len().try_into().unwrap(),
source_buf.as_mut_ptr(),
source_buf.len().try_into().unwrap(),
)
}
}

pub fn socket_udp_send_to(data: &[u8], dst: &[u8]) -> u64 {
unsafe {
__socket_udp_send_to(
data.as_ptr(),
data.len().try_into().unwrap(),
dst.as_ptr(),
dst.len().try_into().unwrap(),
)
}
}
}

pub mod wasmstr {
use std::convert::TryInto;

pub struct WasmStr(pub *const u8, pub u64);

impl WasmStr {
pub fn to_str(&self) -> &str {
self.into()
}
}

impl std::convert::From<&WasmStr> for &str {
fn from(wasm_string: &WasmStr) -> Self {
std::str::from_utf8(unsafe {
std::slice::from_raw_parts(wasm_string.0, wasm_string.1.try_into().unwrap())
})
.unwrap()
}
}
}
1,537 changes: 1,537 additions & 0 deletions wasmtime-custom-syscall/Cargo.lock

Large diffs are not rendered by default.

17 changes: 17 additions & 0 deletions wasmtime-custom-syscall/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "wasmtime-custom-syscall"
version = "0.1.0"
authors = ["Stefan Junker <sjunker@redhat.com>"]
edition = "2018"
build = "build.rs"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
common = { path = "../common" }
wabt = "0.9"

wasmtime = { git = "https://github.com/steveeJ-forks/wasmtime", rev = "16421f31b622c7e11fda038909d1e82cb4432cee" }
wasmtime-bindings = { git = "https://github.com/steveeJ-forks/wasmtime", rev = "16421f31b622c7e11fda038909d1e82cb4432cee" }
wasmtime-bindings-macro = { git = "https://github.com/steveeJ-forks/wasmtime", rev = "16421f31b622c7e11fda038909d1e82cb4432cee" }
wasmtime-wasi = { git = "https://github.com/steveeJ-forks/wasmtime", rev = "16421f31b622c7e11fda038909d1e82cb4432cee" }
8 changes: 8 additions & 0 deletions wasmtime-custom-syscall/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Wasmtime Custom Syscall Demo
This demo implements a WASM runtime based on Wasmtime, providing custom
syscalls to a WASM application.
The WASM application lives in _app.rs_, and is compiled as per the
instructions in _build.rs_.

## Running the demo
The demo runs with `cargo run` out of the box.
34 changes: 34 additions & 0 deletions wasmtime-custom-syscall/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright 2019 Red Hat
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::process::Command;

fn main() {
let out = std::env::var("OUT_DIR").unwrap();

println!("Compiling Rust source to WASM...");
assert!(Command::new("rustc")
.arg("-C")
.arg("lto")
.arg("-C")
.arg("opt-level=3")
.arg("--target")
.arg("wasm32-unknown-unknown")
.arg("-o")
.arg(format!("{}/app.wasm", out))
.arg("src/app.rs")
.status()
.expect("failed to compile WASM module")
.success());
}
26 changes: 26 additions & 0 deletions wasmtime-custom-syscall/src/app.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright 2019 Red Hat
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

mod wasm {
include!("../../common/src/wasm.rs");
}

use wasm::enarx_syscalls;

fn main() {
let (a, b) = (5, 7);
let result = a + b;

enarx_syscalls::println(&format!("{} + {} = {}", a, b, result));
}
147 changes: 147 additions & 0 deletions wasmtime-custom-syscall/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
// Copyright 2019 Red Hat
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::error::Error;

// The basic WASM demo itself.
fn main() -> Result<(), Box<dyn Error>> {
use wasmtime_api::{
map_to_wasmtime_trait, wrap_wasmtime_module, Config, Engine, HostRef, Instance, Module,
Store,
};

// Init.
let engine = HostRef::new(Engine::new(Config::default()));
let store = HostRef::new(Store::new(&engine));

// Instantiate the stdio syscalls.
let syscalls_stdio_module = HostRef::new(wrap_wasmtime_module!(
&store, |_imports| enarx_syscalls_impl::stdio::Stdio::default(); module(enarx_syscalls_impl::stdio::impl_mod)
)?);
let syscalls_stdio_instance = Instance::new(
&store,
&syscalls_stdio_module,
// TODO: what's this array for?
&[],
)?;

// Load the app's WASM binary.
let app_wasm_binary = std::fs::read(concat!(env!("OUT_DIR"), "/app.wasm"))?;

// Compile the app module.
let app_module = HostRef::new(Module::new(&store, &app_wasm_binary)?);

// Create the app instance with the enarx_syscalls imported
let app_instance = {
// Prepare a lookup-map of module imports and their index
let app_module_imports = app_module
.borrow()
.imports()
.iter()
.enumerate()
.map(|(i, import)| {
(
format!(
"{}/{}",
import.module().to_string(),
import.name().to_string(),
),
i,
)
})
.collect::<std::collections::HashMap<_, _>>();
println!("[RUNTIME] App imports: {:?}", app_module_imports);

// Instance imports for the app must only include the ones which the module actually references.
// They need to be in the same order they are referenced.
// let app_instance_imports = exports_iterator
let app_instance_imports = std::iter::empty()
.chain(
std::iter::repeat(enarx_syscalls_impl::stdio::MODULE_NAME)
.zip(syscalls_stdio_instance.exports().iter().cloned())
.zip(syscalls_stdio_module.borrow().exports().iter()),
)
.filter_map(|((module_name, ext), exp)| match (&ext, exp) {
(wasmtime_api::Extern::Func(_), exp) => {
// instance imports may only include references which are imported by the module
let lookup = format!("{}/{}", module_name, exp.name().to_string());
if let Some(index) = app_module_imports.get(&lookup) {
println!("[RUNTIME] Including export '{}' at index {}", lookup, index);
Some((index, ext))
} else {
println!("[RUNTIME] Skipping export '{}'", lookup);
None
}
}
// TODO: figure out what to do with non-Func externs
_ => None,
})
// ensure the same order as observed in the imports
.collect::<std::collections::BTreeMap<_, _>>()
.values()
.cloned()
.collect::<Vec<wasmtime_api::Extern>>();

HostRef::new(Instance::new(
&store,
&app_module,
app_instance_imports.as_slice(),
)?)
};

// Map the trait to the App module
let app_wrapper = map_to_wasmtime_trait!(app_instance; module(app_mod));

// Call its main function
app_wrapper.main();

Ok(())
}

use wasmtime_bindings_macro::wasmtime_trait;
#[wasmtime_trait(module(app_mod))]
trait App {
fn main(&self);
}

pub(crate) mod enarx_syscalls_impl {
use common::wasm::wasmstr::WasmStr;
use wasmtime_bindings_macro::wasmtime_impl;

pub(crate) mod stdio {
use super::*;

pub static MODULE_NAME: &str = "enarx_syscalls_stdio";
pub struct Stdio;

impl Default for Stdio {
fn default() -> Self {
Self
}
}

#[wasmtime_impl(
module(impl_mod),
// we're passing a context type here which satisfies `impl WasmMem`
context(wasmtime_wasi::WasiMem),
visibility(pub),
)]
impl Stdio {
fn __print_str(&self, ptr: *const u8, len: u64) {
let wasm_str = WasmStr(ptr, len);
print!("[WASM] {}", wasm_str.to_str());
}
}
}
}
1,537 changes: 1,537 additions & 0 deletions wasmtime-udp-echoserver/Cargo.lock

Large diffs are not rendered by default.

17 changes: 17 additions & 0 deletions wasmtime-udp-echoserver/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "wasmtime-udp-echoserver"
version = "0.1.0"
authors = ["Stefan Junker <sjunker@redhat.com>"]
edition = "2018"
build = "build.rs"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
common = { path = "../common" }
wabt = "0.9"

wasmtime = { git = "https://github.com/steveeJ-forks/wasmtime", rev = "16421f31b622c7e11fda038909d1e82cb4432cee" }
wasmtime-bindings = { git = "https://github.com/steveeJ-forks/wasmtime", rev = "16421f31b622c7e11fda038909d1e82cb4432cee" }
wasmtime-bindings-macro = { git = "https://github.com/steveeJ-forks/wasmtime", rev = "16421f31b622c7e11fda038909d1e82cb4432cee" }
wasmtime-wasi = { git = "https://github.com/steveeJ-forks/wasmtime", rev = "16421f31b622c7e11fda038909d1e82cb4432cee" }
37 changes: 37 additions & 0 deletions wasmtime-udp-echoserver/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Wasmtime UDP Echo-Server Demo
This demo shows the extension of the previously introduced _stdio_ syscall
module, and adds a _net_ syscall module, which implements rudimentary
UDP functionality.
The WASM guest application uses these syscalls to build an UDP echo
server.

The WASM application lives in _app.rs_, and is compiled as per the
instructions in _build.rs_.

## Running the demo
The demo runs the UDP echo-server with `cargo run` out of the box.

To send something to it from the host, `nc` can be used as in the following example:

```console
$ nc -4u localhost 34254
Hello, WASM guest!
Hello, WASM guest!
^C
```

For the above example, the server logs should look something like this:

```console
[RUNTIME] App imports: {"enarx_syscalls_net/__socket_udp_bind": 0, "enarx_syscalls_net/__socket_udp_receive": 1, "enarx_syscalls_stdio/__print_str": 2, "enarx_syscalls_net/__socket_udp_send_to": 3}
[RUNTIME] Including export 'enarx_syscalls_stdio/__print_str' at index 2
[RUNTIME] Including export 'enarx_syscalls_net/__socket_udp_bind' at index 0
[RUNTIME] Including export 'enarx_syscalls_net/__socket_udp_receive' at index 1
[RUNTIME] Including export 'enarx_syscalls_net/__socket_udp_send_to' at index 3
[RUNTIME] receiving on UdpSocket { addr: V4(127.0.0.1:34254), fd: 3 }
[RUNTIME] received 19 bytes from 127.0.0.1:58520
[WASM] received 19 bytes: 'Hello, WASM guest!' from 127.0.0.1:58520
[WASM] sending back...
[RUNTIME] sent 19 bytes to 127.0.0.1:58520
[RUNTIME] receiving on UdpSocket { addr: V4(127.0.0.1:34254), fd: 3 }
```
34 changes: 34 additions & 0 deletions wasmtime-udp-echoserver/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright 2019 Red Hat
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::process::Command;

fn main() {
let out = std::env::var("OUT_DIR").unwrap();

println!("Compiling Rust source to WASM...");
assert!(Command::new("rustc")
.arg("-C")
.arg("lto")
.arg("-C")
.arg("opt-level=3")
.arg("--target")
.arg("wasm32-unknown-unknown")
.arg("-o")
.arg(format!("{}/app.wasm", out))
.arg("src/app.rs")
.status()
.expect("failed to compile WASM module")
.success());
}
42 changes: 42 additions & 0 deletions wasmtime-udp-echoserver/src/app.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright 2019 Red Hat
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

mod wasm {
include!("../../common/src/wasm.rs");
}

use wasm::enarx_syscalls;

fn main() -> ! {
enarx_syscalls::socket_udp_bind("127.0.0.1:34254");
loop {
let mut data_buf = [0; 1024];
let mut src_buf = [0; 1024];

let len = enarx_syscalls::socket_udp_receive(&mut data_buf, &mut src_buf);
let data = {
let data_str = std::str::from_utf8(&data_buf).unwrap();
let newline_index = data_str.find("\n").unwrap_or(data_str.len());
data_str.split_at(newline_index).0
};
let src = std::str::from_utf8(&src_buf)
.unwrap()
.split("\0")
.nth(0)
.unwrap();
enarx_syscalls::println(&format!("received {} bytes: '{}' from {}", len, data, src));
enarx_syscalls::println("sending back...");
enarx_syscalls::socket_udp_send_to(format!("{}\n", data).as_bytes(), src.as_bytes());
}
}
245 changes: 245 additions & 0 deletions wasmtime-udp-echoserver/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
// Copyright 2019 Red Hat
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::error::Error;

// The basic WASM demo itself.
fn main() -> Result<(), Box<dyn Error>> {
use wasmtime_api::{
map_to_wasmtime_trait, wrap_wasmtime_module, Config, Engine, HostRef, Instance, Module,
Store,
};

// Init.
let engine = HostRef::new(Engine::new(Config::default()));
let store = HostRef::new(Store::new(&engine));

// Instantiate the stdio syscalls.
let syscalls_stdio_module = HostRef::new(wrap_wasmtime_module!(
&store, |_imports| enarx_syscalls_impl::stdio::Stdio::default(); module(enarx_syscalls_impl::stdio::impl_mod)
)?);
let syscalls_stdio_instance = Instance::new(
&store,
&syscalls_stdio_module,
// TODO: what's this array for?
&[],
)?;

// Instantiate the net syscalls.
let syscalls_net_module = HostRef::new(wrap_wasmtime_module!(
&store, |_imports| enarx_syscalls_impl::net::Net::default(); module(enarx_syscalls_impl::net::impl_mod)
)?);
let syscalls_net_instance = Instance::new(
&store,
&syscalls_net_module,
// TODO: what's this array for?
&[],
)?;

// Load the app's WASM binary.
let app_wasm_binary = std::fs::read(concat!(env!("OUT_DIR"), "/app.wasm"))?;

// Compile the app module.
let app_module = HostRef::new(Module::new(&store, &app_wasm_binary)?);

// Create the app instance with the enarx_syscalls imported
let app_instance = {
// Prepare a lookup-map of module imports and their index
let app_module_imports = app_module
.borrow()
.imports()
.iter()
.enumerate()
.map(|(i, import)| {
(
format!(
"{}/{}",
import.module().to_string(),
import.name().to_string(),
),
i,
)
})
.collect::<std::collections::HashMap<_, _>>();
println!("[RUNTIME] App imports: {:?}", app_module_imports);

// Instance imports for the app must only include the ones which the module actually references.
// They need to be in the same order they are referenced.
// let app_instance_imports = exports_iterator
let app_instance_imports = std::iter::empty()
.chain(
std::iter::repeat(enarx_syscalls_impl::stdio::MODULE_NAME)
.zip(syscalls_stdio_instance.exports().iter().cloned())
.zip(syscalls_stdio_module.borrow().exports().iter()),
)
.chain(
std::iter::repeat(enarx_syscalls_impl::net::MODULE_NAME)
.zip(syscalls_net_instance.exports().iter().cloned())
.zip(syscalls_net_module.borrow().exports().iter()),
)
.filter_map(|((module_name, ext), exp)| match (&ext, exp) {
(wasmtime_api::Extern::Func(_), exp) => {
// instance imports may only include references which are imported by the module
let lookup = format!("{}/{}", module_name, exp.name().to_string());
if let Some(index) = app_module_imports.get(&lookup) {
println!("[RUNTIME] Including export '{}' at index {}", lookup, index);
Some((index, ext))
} else {
println!("[RUNTIME] Skipping export '{}'", lookup);
None
}
}
// TODO: figure out what to do with non-Func externs
_ => None,
})
// ensure the same order as observed in the imports
.collect::<std::collections::BTreeMap<_, _>>()
.values()
.cloned()
.collect::<Vec<wasmtime_api::Extern>>();

HostRef::new(Instance::new(
&store,
&app_module,
app_instance_imports.as_slice(),
)?)
};

// Map the trait to the App module
let app_wrapper = map_to_wasmtime_trait!(app_instance; module(app_mod));

// Call its main function
app_wrapper.main();

Ok(())
}

use wasmtime_bindings_macro::wasmtime_trait;
#[wasmtime_trait(module(app_mod))]
trait App {
fn main(&self);
}

pub(crate) mod enarx_syscalls_impl {
use common::wasm::wasmstr::WasmStr;
use std::convert::TryInto;
use wasmtime_bindings_macro::wasmtime_impl;

pub(crate) mod stdio {
use super::*;

pub static MODULE_NAME: &str = "enarx_syscalls_stdio";
pub struct Stdio;

impl Default for Stdio {
fn default() -> Self {
Self
}
}

#[wasmtime_impl(
module(impl_mod),
// we're passing a context type here which satisfies `impl WasmMem`
context(wasmtime_wasi::WasiMem),
visibility(pub),
)]
impl Stdio {
fn __print_str(&self, ptr: *const u8, len: u64) {
let wasm_str = WasmStr(ptr, len);
print!("[WASM] {}", wasm_str.to_str());
}
}
}

pub(crate) mod net {
use super::*;

pub static MODULE_NAME: &str = "enarx_syscalls_net";

pub struct Net {
pub udp_socket: Option<std::net::UdpSocket>,
}

impl Default for Net {
fn default() -> Self {
Net { udp_socket: None }
}
}

#[wasmtime_impl(
module(impl_mod),
// we're passing a context type here which satisfies `impl WasmMem`
context(wasmtime_wasi::WasiMem),
visibility(pub),
)]
impl Net {
fn __socket_udp_bind(&mut self, ptr: *const u8, len: u64) {
let addr_str = WasmStr(ptr, len);
self.udp_socket = Some(std::net::UdpSocket::bind(addr_str.to_str()).unwrap());
}

fn __socket_udp_receive(
&mut self,
data_ptr: *mut u8,
data_len: u64,
src_ptr: *mut u8,
src_len: u64,
) -> u64 {
let mut data_buf = unsafe {
std::slice::from_raw_parts_mut(data_ptr, data_len.try_into().unwrap())
};

if let Some(socket) = self.udp_socket.as_ref() {
println!("[RUNTIME] receiving on {:?}", socket);
let (amt, src) = socket.recv_from(&mut data_buf).unwrap();
println!("[RUNTIME] received {} bytes from {}", amt, src);

let src_str = format!("{}:{}\0", src.ip(), src.port());

let src_str_size = std::mem::size_of_val(&src_str);

assert!(src_str_size <= src_len.try_into().unwrap());

unsafe {
std::ptr::copy(src_str.as_ptr(), src_ptr, src_str_size);
};

amt.try_into().unwrap()
} else {
0
}
}

fn __socket_udp_send_to(
&mut self,
data_ptr: *const u8,
data_len: u64,
dst_ptr: *const u8,
dst_len: u64,
) -> u64 {
let data =
unsafe { std::slice::from_raw_parts(data_ptr, data_len.try_into().unwrap()) };
let dst = WasmStr(dst_ptr, dst_len.try_into().unwrap());

if let Some(socket) = self.udp_socket.as_ref() {
let amt = socket.send_to(&data, dst.to_str()).unwrap();
println!("[RUNTIME] sent {} bytes to {}", amt, dst.to_str());
amt.try_into().unwrap()
} else {
0
}
}
}
}
}