Skip to content

Commit

Permalink
Added Ability To Create Sockets
Browse files Browse the repository at this point in the history
- Added Ability to create Wasi Sockets
- Managed conversion between IpAddr and IpAddressFamily
- Managed conversion between ErrorCodes and ErrorKind
  • Loading branch information
StaticallyTypedAnxiety committed Sep 23, 2024
1 parent b452c7e commit cba06b9
Show file tree
Hide file tree
Showing 16 changed files with 4,621 additions and 14 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ package = "component:wasm-runtime"
[package.metadata.component.target.dependencies]
"wasi:io" = {path = "wit/deps/io"}
"wasi:clocks" = {path = "wit/deps/clocks"}
"wasi:sockets" = {path = "wit/deps/sockets"}
3,630 changes: 3,624 additions & 6 deletions src/bindings.rs

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion src/engine.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crossbeam::channel::{Receiver, Sender};
use futures::pin_mut;
use futures::StreamExt;
use std::rc::Rc;
use std::{
future::Future,
sync::Arc,
Expand All @@ -24,7 +25,7 @@ pub struct Reactor {

impl Reactor {
//adds event to the queue
pub fn register<T: Into<Pollable>>(&mut self, event_name: String, pollable: T) {
pub fn register<T: Into<Rc<Pollable>>>(&mut self, event_name: String, pollable: T) {
self.events.push(event_name, pollable.into());
}

Expand Down
1 change: 1 addition & 0 deletions src/io/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod net;
pub mod timer;
pub use timer::Timer;
89 changes: 89 additions & 0 deletions src/io/net.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use crate::bindings::wasi::sockets::{
network::IpAddress,
tcp::{IpAddressFamily, TcpSocket},
tcp_create_socket::{create_tcp_socket, ErrorCode},
};
use std::io::ErrorKind;
use std::net::IpAddr;
struct TcpStream {
_socket: TcpSocket,
}
type IOResult<T> = std::io::Result<T>;
type IOError = std::io::Error;

impl TcpStream {
pub fn create<T: Into<IpAddressFamily>>(ip_address: T) -> IOResult<Self> {
Ok(Self {
_socket: create_tcp_socket(ip_address.into())?,
})
}
}

impl From<IpAddr> for IpAddress {
fn from(address: IpAddr) -> Self {
match address {
IpAddr::V4(v4) => {
let ocets = v4.octets();
IpAddress::Ipv4((ocets[0], ocets[1], ocets[2], ocets[3]))
}
IpAddr::V6(v6) => {
let segments = v6.segments();
IpAddress::Ipv6((
segments[0],
segments[1],
segments[2],
segments[3],
segments[4],
segments[5],
segments[6],
segments[7],
))
}
}
}
}

impl From<IpAddr> for IpAddressFamily {
fn from(address: IpAddr) -> Self {
if address.is_ipv4() {
IpAddressFamily::Ipv4
} else {
IpAddressFamily::Ipv6
}
}
}

impl From<ErrorCode> for IOError {
fn from(address: ErrorCode) -> Self {
let kind = (&address).into();
IOError::new(kind, address)
}
}

impl From<&ErrorCode> for ErrorKind {
fn from(address: &ErrorCode) -> Self {
match address {
ErrorCode::Unknown => ErrorKind::Other,
ErrorCode::AccessDenied => ErrorKind::PermissionDenied,
ErrorCode::NotSupported => ErrorKind::Unsupported,
ErrorCode::InvalidArgument => ErrorKind::InvalidInput,
ErrorCode::OutOfMemory => ErrorKind::OutOfMemory,
ErrorCode::Timeout => ErrorKind::TimedOut,
ErrorCode::ConcurrencyConflict => ErrorKind::Other,
ErrorCode::NotInProgress => ErrorKind::Other,
ErrorCode::WouldBlock => ErrorKind::WouldBlock,
ErrorCode::InvalidState => ErrorKind::Other,
ErrorCode::NewSocketLimit => ErrorKind::Other,
ErrorCode::AddressNotBindable => ErrorKind::Other,
ErrorCode::AddressInUse => ErrorKind::AddrInUse,
ErrorCode::RemoteUnreachable => ErrorKind::NotFound,
ErrorCode::ConnectionRefused => ErrorKind::ConnectionRefused,
ErrorCode::ConnectionReset => ErrorKind::ConnectionReset,
ErrorCode::ConnectionAborted => ErrorKind::ConnectionAborted,
ErrorCode::DatagramTooLarge => ErrorKind::Other,
ErrorCode::NameUnresolvable => ErrorKind::Other,
ErrorCode::TemporaryResolverFailure => ErrorKind::Other,
ErrorCode::PermanentResolverFailure => ErrorKind::Other,
}
}
}
5 changes: 3 additions & 2 deletions src/io/timer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::bindings::wasi::{
clocks::monotonic_clock::{subscribe_duration, subscribe_instant},
io::poll::Pollable,
};
use std::rc::Rc;
#[derive(Debug)]
pub struct Timer {
pollable: Pollable,
Expand All @@ -21,8 +22,8 @@ impl Timer {
}
}
}
impl From<Timer> for Pollable {
impl From<Timer> for Rc<Pollable> {
fn from(value: Timer) -> Self {
value.pollable
Rc::new(value.pollable)
}
}
15 changes: 10 additions & 5 deletions src/poll_tasks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,19 @@ use std::{
task::{Context, Poll},
};

use futures::Stream;

use crate::bindings::wasi::io::poll::{poll, Pollable};
use futures::Stream;
use std::rc::Rc;
type PollableCell = Rc<Pollable>;

///Future that is used to poll changes from the host\
#[derive(Default, Debug)]
pub struct PollTasks {
pendings: HashMap<String, Pollable>,
pendings: HashMap<String, PollableCell>,
}

impl PollTasks {
pub(crate) fn push(&mut self, event_name: String, pollable: Pollable) {
pub(crate) fn push(&mut self, event_name: String, pollable: PollableCell) {
self.pendings.insert(event_name, pollable);
}
}
Expand All @@ -27,7 +28,11 @@ impl Stream for PollTasks {
if this.pendings.is_empty() {
return Poll::Ready(None);
}
let pending_polls = this.pendings.values().collect::<Vec<_>>();
let pending_polls = this
.pendings
.values()
.map(|cell| cell.as_ref())
.collect::<Vec<_>>();
poll(pending_polls.as_slice());
let ready_set = this
.pendings
Expand Down
9 changes: 9 additions & 0 deletions wit/deps/sockets/instance-network.wit
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

/// This interface provides a value-export of the default network handle..
interface instance-network {
use network.{network};

/// Get a handle to the default network.
instance-network: func() -> network;

}
51 changes: 51 additions & 0 deletions wit/deps/sockets/ip-name-lookup.wit
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@

interface ip-name-lookup {
use wasi:io/poll@0.2.0.{pollable};
use network.{network, error-code, ip-address};


/// Resolve an internet host name to a list of IP addresses.
///
/// Unicode domain names are automatically converted to ASCII using IDNA encoding.
/// If the input is an IP address string, the address is parsed and returned
/// as-is without making any external requests.
///
/// See the wasi-socket proposal README.md for a comparison with getaddrinfo.
///
/// This function never blocks. It either immediately fails or immediately
/// returns successfully with a `resolve-address-stream` that can be used
/// to (asynchronously) fetch the results.
///
/// # Typical errors
/// - `invalid-argument`: `name` is a syntactically invalid domain name or IP address.
///
/// # References:
/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getaddrinfo.html>
/// - <https://man7.org/linux/man-pages/man3/getaddrinfo.3.html>
/// - <https://learn.microsoft.com/en-us/windows/win32/api/ws2tcpip/nf-ws2tcpip-getaddrinfo>
/// - <https://man.freebsd.org/cgi/man.cgi?query=getaddrinfo&sektion=3>
resolve-addresses: func(network: borrow<network>, name: string) -> result<resolve-address-stream, error-code>;

resource resolve-address-stream {
/// Returns the next address from the resolver.
///
/// This function should be called multiple times. On each call, it will
/// return the next address in connection order preference. If all
/// addresses have been exhausted, this function returns `none`.
///
/// This function never returns IPv4-mapped IPv6 addresses.
///
/// # Typical errors
/// - `name-unresolvable`: Name does not exist or has no suitable associated IP addresses. (EAI_NONAME, EAI_NODATA, EAI_ADDRFAMILY)
/// - `temporary-resolver-failure`: A temporary failure in name resolution occurred. (EAI_AGAIN)
/// - `permanent-resolver-failure`: A permanent failure in name resolution occurred. (EAI_FAIL)
/// - `would-block`: A result is not available yet. (EWOULDBLOCK, EAGAIN)
resolve-next-address: func() -> result<option<ip-address>, error-code>;

/// Create a `pollable` which will resolve once the stream is ready for I/O.
///
/// Note: this function is here for WASI Preview2 only.
/// It's planned to be removed when `future` is natively supported in Preview3.
subscribe: func() -> pollable;
}
}
145 changes: 145 additions & 0 deletions wit/deps/sockets/network.wit
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@

interface network {
/// An opaque resource that represents access to (a subset of) the network.
/// This enables context-based security for networking.
/// There is no need for this to map 1:1 to a physical network interface.
resource network;

/// Error codes.
///
/// In theory, every API can return any error code.
/// In practice, API's typically only return the errors documented per API
/// combined with a couple of errors that are always possible:
/// - `unknown`
/// - `access-denied`
/// - `not-supported`
/// - `out-of-memory`
/// - `concurrency-conflict`
///
/// See each individual API for what the POSIX equivalents are. They sometimes differ per API.
enum error-code {
/// Unknown error
unknown,

/// Access denied.
///
/// POSIX equivalent: EACCES, EPERM
access-denied,

/// The operation is not supported.
///
/// POSIX equivalent: EOPNOTSUPP
not-supported,

/// One of the arguments is invalid.
///
/// POSIX equivalent: EINVAL
invalid-argument,

/// Not enough memory to complete the operation.
///
/// POSIX equivalent: ENOMEM, ENOBUFS, EAI_MEMORY
out-of-memory,

/// The operation timed out before it could finish completely.
timeout,

/// This operation is incompatible with another asynchronous operation that is already in progress.
///
/// POSIX equivalent: EALREADY
concurrency-conflict,

/// Trying to finish an asynchronous operation that:
/// - has not been started yet, or:
/// - was already finished by a previous `finish-*` call.
///
/// Note: this is scheduled to be removed when `future`s are natively supported.
not-in-progress,

/// The operation has been aborted because it could not be completed immediately.
///
/// Note: this is scheduled to be removed when `future`s are natively supported.
would-block,


/// The operation is not valid in the socket's current state.
invalid-state,

/// A new socket resource could not be created because of a system limit.
new-socket-limit,

/// A bind operation failed because the provided address is not an address that the `network` can bind to.
address-not-bindable,

/// A bind operation failed because the provided address is already in use or because there are no ephemeral ports available.
address-in-use,

/// The remote address is not reachable
remote-unreachable,


/// The TCP connection was forcefully rejected
connection-refused,

/// The TCP connection was reset.
connection-reset,

/// A TCP connection was aborted.
connection-aborted,


/// The size of a datagram sent to a UDP socket exceeded the maximum
/// supported size.
datagram-too-large,


/// Name does not exist or has no suitable associated IP addresses.
name-unresolvable,

/// A temporary failure in name resolution occurred.
temporary-resolver-failure,

/// A permanent failure in name resolution occurred.
permanent-resolver-failure,
}

enum ip-address-family {
/// Similar to `AF_INET` in POSIX.
ipv4,

/// Similar to `AF_INET6` in POSIX.
ipv6,
}

type ipv4-address = tuple<u8, u8, u8, u8>;
type ipv6-address = tuple<u16, u16, u16, u16, u16, u16, u16, u16>;

variant ip-address {
ipv4(ipv4-address),
ipv6(ipv6-address),
}

record ipv4-socket-address {
/// sin_port
port: u16,
/// sin_addr
address: ipv4-address,
}

record ipv6-socket-address {
/// sin6_port
port: u16,
/// sin6_flowinfo
flow-info: u32,
/// sin6_addr
address: ipv6-address,
/// sin6_scope_id
scope-id: u32,
}

variant ip-socket-address {
ipv4(ipv4-socket-address),
ipv6(ipv6-socket-address),
}

}
Loading

0 comments on commit cba06b9

Please sign in to comment.