Skip to content

Commit

Permalink
Add wasi-sockets.
Browse files Browse the repository at this point in the history
This is derived from files in

WebAssembly/wasi-sockets#16

and minimally renames and adjusts things to fit the very latest
wasi-proposal-repo layout.
  • Loading branch information
sunfishcode committed Feb 24, 2023
1 parent dccd0d5 commit e54cad9
Show file tree
Hide file tree
Showing 9 changed files with 1,531 additions and 2 deletions.
1,002 changes: 1,000 additions & 2 deletions cli.md

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions wit/cli.wit
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ default world cli {
import instance-monotonic-clock: clocks.instance-monotonic-clock
import timezone: clocks.timezone
import filesystem: filesystem.filesystem
import instance-network: sockets.instance-network
import ip-name-lookup: sockets.ip-name-lookup
import network: sockets.network
import tcp-create-socket: sockets.tcp-create-socket
import tcp: sockets.tcp
import udp-create-socket: sockets.udp-create-socket
import udp: sockets.udp
import random: random.random
import poll: poll.poll
import io: io.streams
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..
default interface instance-network {
use pkg.network.{network}

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

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

default interface ip-name-lookup {
use poll.poll.{pollable}
use pkg.network.{network, error, ip-address, ip-address-family}


/// Resolve an internet host name to a list of IP addresses.
///
/// See the wasi-socket proposal README.md for a comparison with getaddrinfo.
///
/// Parameters:
/// - `name`: The name to look up. IP addresses are not allowed. Unicode domain names are automatically converted
/// to ASCII using IDNA encoding.
/// - `address-family`: If provided, limit the results to addresses of this specific address family.
/// - `include-unavailable`: When set to true, this function will also return addresses of which the runtime
/// thinks (or knows) can't be connected to at the moment. For example, this will return IPv6 addresses on
/// systems without an active IPv6 interface. Notes:
/// - Even when no public IPv6 interfaces are present or active, names like "localhost" can still resolve to an IPv6 address.
/// - Whatever is "available" or "unavailable" is volatile and can change everytime a network cable is unplugged.
///
/// This function never blocks. It either immediately returns successfully with a `resolve-address-stream`
/// that can be used to (asynchronously) fetch the results.
/// Or it immediately fails whenever `name` is:
/// - empty
/// - an IP address
/// - a syntactically invalid domain name in another way
///
/// References:
/// - https://pubs.opengroup.org/onlinepubs/9699919799/functions/getaddrinfo.html
/// - https://man7.org/linux/man-pages/man3/getaddrinfo.3.html
///
resolve-addresses: func(network: network, name: string, address-family: option<ip-address-family>, include-unavailable: bool) -> result<resolve-address-stream, error>



type resolve-address-stream = u32

/// 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`.
/// After which, you should release the stream with `drop-resolve-address-stream`.
///
/// This function never returns IPv4-mapped IPv6 addresses.
resolve-next-address: func(this: resolve-address-stream) -> result<option<ip-address>, error>



/// Dispose of the specified `resolve-address-stream`, after which it may no longer be used.
///
/// Note: this function is scheduled to be removed when Resources are natively supported in Wit.
drop-resolve-address-stream: func(this: resolve-address-stream)

/// Get/set the blocking mode of the stream.
///
/// By default a stream is in "blocking" mode, meaning that any function blocks and waits for its completion.
/// When switched to "non-blocking" mode, operations that would block return an `again` error. After which
/// the API consumer is expected to call `subscribe` and wait for completion using the wasi-poll module.
///
/// Note: these functions are here for WASI Preview2 only.
/// They're planned to be removed when `future` is natively supported in Preview3.
non-blocking: func(this: resolve-address-stream) -> result<bool, error>
set-non-blocking: func(this: resolve-address-stream, value: bool) -> result<_, error>

/// 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(this: resolve-address-stream) -> pollable
}
56 changes: 56 additions & 0 deletions wit/deps/sockets/network.wit
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@

default 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.
///
/// FYI, In the future this will be replaced by handle types.
type network = u32

/// Dispose of the specified `network`, after which it may no longer be used.
///
/// Note: this function is scheduled to be removed when Resources are natively supported in Wit.
drop-network: func(this: network)



enum error {
unknown,
again,
// TODO ...
}

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 {
port: u16, // sin_port
address: ipv4-address, // sin_addr
}

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

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

}
19 changes: 19 additions & 0 deletions wit/deps/sockets/tcp-create-socket.wit
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@

default interface tcp-create-socket {
use pkg.network.{network, error, ip-address-family}
use pkg.tcp.{tcp-socket}

/// Create a new TCP socket.
///
/// Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` in POSIX.
///
/// This function does not require a network capability handle. This is considered to be safe because
/// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`listen`/`connect`
/// is called, the socket is effectively an in-memory configuration object, unable to communicate with the outside world.
///
/// References:
/// - https://pubs.opengroup.org/onlinepubs/9699919799/functions/socket.html
/// - https://man7.org/linux/man-pages/man2/socket.2.html
///
create-tcp-socket: func(address-family: ip-address-family) -> result<tcp-socket, error>
}
188 changes: 188 additions & 0 deletions wit/deps/sockets/tcp.wit
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@

default interface tcp {
use io.streams.{input-stream, output-stream}
use poll.poll.{pollable}
use pkg.network.{network, error, ip-socket-address, ip-address-family}

/// A TCP socket handle.
type tcp-socket = u32


enum shutdown-type {
/// Similar to `SHUT_RD` in POSIX.
receive,

/// Similar to `SHUT_WR` in POSIX.
send,

/// Similar to `SHUT_RDWR` in POSIX.
both,
}


/// Bind the socket to a specific network on the provided IP address and port.
///
/// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which
/// network interface(s) to bind to.
/// If the TCP/UDP port is zero, the socket will be bound to a random free port.
///
/// When a socket is not explicitly bound, the first invocation to a listen or connect operation will
/// implicitly bind the socket.
///
/// Fails when:
/// - the socket is already bound.
///
/// References
/// - https://pubs.opengroup.org/onlinepubs/9699919799/functions/bind.html
/// - https://man7.org/linux/man-pages/man2/bind.2.html
bind: func(this: tcp-socket, network: network, local-address: ip-socket-address) -> result<_, error>

/// Connect to a remote endpoint.
///
/// On success:
/// - the socket is transitioned into the Connection state
/// - a pair of streams is returned that can be used to read & write to the connection
///
/// Fails when:
/// - the socket is already bound to a different network.
/// - the provided network does not allow connections to the specified endpoint.
/// - the socket is already in the Connection or Listener state.
/// - either the remote IP address or port is 0.
///
/// References
/// - https://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html
/// - https://man7.org/linux/man-pages/man2/connect.2.html
connect: func(this: tcp-socket, network: network, remote-address: ip-socket-address) -> result<tuple<input-stream, output-stream>, error>

/// Start listening for new connections.
///
/// Transitions the socket into the Listener state.
///
/// Fails when:
/// - the socket is already bound to a different network.
/// - the provided network does not allow listening on the specified address.
/// - the socket is already in the Connection or Listener state.
///
/// References
/// - https://pubs.opengroup.org/onlinepubs/9699919799/functions/listen.html
/// - https://man7.org/linux/man-pages/man2/listen.2.html
listen: func(this: tcp-socket, network: network) -> result<_, error>

/// Accept a new client socket.
///
/// The returned socket is bound and in the Connection state.
///
/// On success, this function returns the newly accepted client socket along with
/// a pair of streams that can be used to read & write to the connection.
///
/// Fails when this socket is not in the Listening state.
///
/// References:
/// - https://pubs.opengroup.org/onlinepubs/9699919799/functions/accept.html
/// - https://man7.org/linux/man-pages/man2/accept.2.html
accept: func(this: tcp-socket) -> result<tuple<tcp-socket, input-stream, output-stream>, error>

/// Get the bound local address.
///
/// Returns an error if the socket is not bound.
///
/// References
/// - https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockname.html
/// - https://man7.org/linux/man-pages/man2/getsockname.2.html
local-address: func(this: tcp-socket) -> result<ip-socket-address, error>

/// Get the bound remote address.
///
/// Fails when the socket is not in the Connection state.
///
/// References
/// - https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpeername.html
/// - https://man7.org/linux/man-pages/man2/getpeername.2.html
remote-address: func(this: tcp-socket) -> result<ip-socket-address, error>

/// Whether this is a IPv4 or IPv6 socket.
///
/// Equivalent to the SO_DOMAIN socket option.
address-family: func(this: tcp-socket) -> result<ip-address-family, error>

/// Whether IPv4 compatibility (dual-stack) mode is disabled or not.
/// Implementations are not required to support dual-stack mode. Calling `set-ipv6-only(false)` might fail.
///
/// Fails when called on an IPv4 socket.
///
/// Equivalent to the IPV6_V6ONLY socket option.
ipv6-only: func(this: tcp-socket) -> result<bool, error>
set-ipv6-only: func(this: tcp-socket, value: bool) -> result<_, error>

/// Hints the desired listen queue size. Implementations are free to ignore this.
set-listen-backlog-size: func(this: tcp-socket, value: u64) -> result<_, error>

/// Equivalent to the SO_KEEPALIVE socket option.
keep-alive: func(this: tcp-socket) -> result<bool, error>
set-keep-alive: func(this: tcp-socket, value: bool) -> result<_, error>

/// Equivalent to the TCP_NODELAY socket option.
no-delay: func(this: tcp-socket) -> result<bool, error>
set-no-delay: func(this: tcp-socket, value: bool) -> result<_, error>

/// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options.
unicast-hop-limit: func(this: tcp-socket) -> result<u8, error>
set-unicast-hop-limit: func(this: tcp-socket, value: u8) -> result<_, error>

/// The kernel buffer space reserved for sends/receives on this socket.
///
/// Note #1: an implementation may choose to cap or round the buffer size when setting the value.
/// In other words, after setting a value, reading the same setting back may return a different value.
///
/// Note #2: there is not necessarily a direct relationship between the kernel buffer size and the bytes of
/// actual data to be sent/received by the application, because the kernel might also use the buffer space
/// for internal metadata structures.
///
/// Fails when this socket is in the Listening state.
///
/// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options.
receive-buffer-size: func(this: tcp-socket) -> result<u64, error>
set-receive-buffer-size: func(this: tcp-socket, value: u64) -> result<_, error>
send-buffer-size: func(this: tcp-socket) -> result<u64, error>
set-send-buffer-size: func(this: tcp-socket, value: u64) -> result<_, error>

/// Get/set the blocking mode of the socket.
///
/// By default a socket is in "blocking" mode, meaning that any function blocks and waits for its completion.
/// When switched to "non-blocking" mode, operations that would block return an `again` error. After which
/// the API consumer is expected to call `subscribe` and wait for completion using the wasi-poll module.
///
/// Note: these functions are here for WASI Preview2 only.
/// They're planned to be removed when `future` is natively supported in Preview3.
non-blocking: func(this: tcp-socket) -> result<bool, error>
set-non-blocking: func(this: tcp-socket, value: bool) -> result<_, error>

/// Create a `pollable` which will resolve once the socket 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(this: tcp-socket) -> pollable

/// Gracefully shut down the connection.
///
/// - receive: the socket is not expecting to receive any more data from the peer. All subsequent read
/// operations on the `input-stream` associated with this socket will return an End Of Stream indication.
/// Any data still in the receive queue at time of calling `shutdown` will be discarded.
/// - send: the socket is not expecting to send any more data to the peer. All subsequent write
/// operations on the `output-stream` associated with this socket will return an error.
/// - both: same effect as receive & send combined.
///
/// The shutdown function does not close the socket.
///
/// Fails when the socket is not in the Connection state.
///
/// References
/// - https://pubs.opengroup.org/onlinepubs/9699919799/functions/shutdown.html
/// - https://man7.org/linux/man-pages/man2/shutdown.2.html
shutdown: func(this: tcp-socket, shutdown-type: shutdown-type) -> result<_, error>

/// Dispose of the specified `tcp-socket`, after which it may no longer be used.
///
/// Note: this function is scheduled to be removed when Resources are natively supported in Wit.
drop-tcp-socket: func(this: tcp-socket)
}
19 changes: 19 additions & 0 deletions wit/deps/sockets/udp-create-socket.wit
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@

default interface udp-create-socket {
use pkg.network.{network, error, ip-address-family}
use pkg.udp.{udp-socket}

/// Create a new UDP socket.
///
/// Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` in POSIX.
///
/// This function does not require a network capability handle. This is considered to be safe because
/// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`connect` is called,
/// the socket is effectively an in-memory configuration object, unable to communicate with the outside world.
///
/// References:
/// - https://pubs.opengroup.org/onlinepubs/9699919799/functions/socket.html
/// - https://man7.org/linux/man-pages/man2/socket.2.html
///
create-udp-socket: func(address-family: ip-address-family) -> result<udp-socket, error>
}
Loading

0 comments on commit e54cad9

Please sign in to comment.