753 lines
37 KiB
Plaintext
753 lines
37 KiB
Plaintext
package wasi:sockets@0.3.0-rc-2026-01-06;
|
|
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
interface types {
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
use wasi:clocks/types@0.3.0-rc-2026-01-06.{duration};
|
|
|
|
/// 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`
|
|
///
|
|
/// See each individual API for what the POSIX equivalents are. They sometimes differ per API.
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
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,
|
|
/// The operation is not valid in the socket's current state.
|
|
invalid-state,
|
|
/// 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,
|
|
}
|
|
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
enum ip-address-family {
|
|
/// Similar to `AF_INET` in POSIX.
|
|
ipv4,
|
|
/// Similar to `AF_INET6` in POSIX.
|
|
ipv6,
|
|
}
|
|
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
type ipv4-address = tuple<u8, u8, u8, u8>;
|
|
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
type ipv6-address = tuple<u16, u16, u16, u16, u16, u16, u16, u16>;
|
|
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
variant ip-address {
|
|
ipv4(ipv4-address),
|
|
ipv6(ipv6-address),
|
|
}
|
|
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
record ipv4-socket-address {
|
|
/// sin_port
|
|
port: u16,
|
|
/// sin_addr
|
|
address: ipv4-address,
|
|
}
|
|
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
record ipv6-socket-address {
|
|
/// sin6_port
|
|
port: u16,
|
|
/// sin6_flowinfo
|
|
flow-info: u32,
|
|
/// sin6_addr
|
|
address: ipv6-address,
|
|
/// sin6_scope_id
|
|
scope-id: u32,
|
|
}
|
|
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
variant ip-socket-address {
|
|
ipv4(ipv4-socket-address),
|
|
ipv6(ipv6-socket-address),
|
|
}
|
|
|
|
/// A TCP socket resource.
|
|
///
|
|
/// The socket can be in one of the following states:
|
|
/// - `unbound`
|
|
/// - `bound` (See note below)
|
|
/// - `listening`
|
|
/// - `connecting`
|
|
/// - `connected`
|
|
/// - `closed`
|
|
/// See <https://github.com/WebAssembly/wasi-sockets/blob/main/TcpSocketOperationalSemantics-0.3.0-draft.md>
|
|
/// for more information.
|
|
///
|
|
/// Note: Except where explicitly mentioned, whenever this documentation uses
|
|
/// the term "bound" without backticks it actually means: in the `bound` state *or higher*.
|
|
/// (i.e. `bound`, `listening`, `connecting` or `connected`)
|
|
///
|
|
/// In addition to the general error codes documented on the
|
|
/// `types::error-code` type, TCP socket methods may always return
|
|
/// `error(invalid-state)` when in the `closed` state.
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
resource tcp-socket {
|
|
/// Create a new TCP socket.
|
|
///
|
|
/// Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` in POSIX.
|
|
/// On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise.
|
|
///
|
|
/// Unlike POSIX, WASI sockets have no notion of a socket-level
|
|
/// `O_NONBLOCK` flag. Instead they fully rely on the Component Model's
|
|
/// async support.
|
|
///
|
|
/// # References
|
|
/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/socket.html>
|
|
/// - <https://man7.org/linux/man-pages/man2/socket.2.html>
|
|
/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketw>
|
|
/// - <https://man.freebsd.org/cgi/man.cgi?query=socket&sektion=2>
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
create: static func(address-family: ip-address-family) -> result<tcp-socket, error-code>;
|
|
/// Bind the socket to 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.
|
|
///
|
|
/// Bind can be attempted multiple times on the same socket, even with
|
|
/// different arguments on each iteration. But never concurrently and
|
|
/// only as long as the previous bind failed. Once a bind succeeds, the
|
|
/// binding can't be changed anymore.
|
|
///
|
|
/// # Typical errors
|
|
/// - `invalid-argument`: The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows)
|
|
/// - `invalid-argument`: `local-address` is not a unicast address. (EINVAL)
|
|
/// - `invalid-argument`: `local-address` is an IPv4-mapped IPv6 address. (EINVAL)
|
|
/// - `invalid-state`: The socket is already bound. (EINVAL)
|
|
/// - `address-in-use`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows)
|
|
/// - `address-in-use`: Address is already in use. (EADDRINUSE)
|
|
/// - `address-not-bindable`: `local-address` is not an address that can be bound to. (EADDRNOTAVAIL)
|
|
///
|
|
/// # Implementors note
|
|
/// When binding to a non-zero port, this bind operation shouldn't be affected by the TIME_WAIT
|
|
/// state of a recently closed socket on the same local address. In practice this means that the SO_REUSEADDR
|
|
/// socket option should be set implicitly on all platforms, except on Windows where this is the default behavior
|
|
/// and SO_REUSEADDR performs something different entirely.
|
|
///
|
|
/// # References
|
|
/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/bind.html>
|
|
/// - <https://man7.org/linux/man-pages/man2/bind.2.html>
|
|
/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-bind>
|
|
/// - <https://man.freebsd.org/cgi/man.cgi?query=bind&sektion=2&format=html>
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
bind: func(local-address: ip-socket-address) -> result<_, error-code>;
|
|
/// Connect to a remote endpoint.
|
|
///
|
|
/// On success, the socket is transitioned into the `connected` state and this function returns a connection resource.
|
|
///
|
|
/// After a failed connection attempt, the socket will be in the `closed`
|
|
/// state and the only valid action left is to `drop` the socket. A single
|
|
/// socket can not be used to connect more than once.
|
|
///
|
|
/// # Typical errors
|
|
/// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT)
|
|
/// - `invalid-argument`: `remote-address` is not a unicast address. (EINVAL, ENETUNREACH on Linux, EAFNOSUPPORT on MacOS)
|
|
/// - `invalid-argument`: `remote-address` is an IPv4-mapped IPv6 address. (EINVAL, EADDRNOTAVAIL on Illumos)
|
|
/// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EADDRNOTAVAIL on Windows)
|
|
/// - `invalid-argument`: The port in `remote-address` is set to 0. (EADDRNOTAVAIL on Windows)
|
|
/// - `invalid-state`: The socket is already in the `connecting` state. (EALREADY)
|
|
/// - `invalid-state`: The socket is already in the `connected` state. (EISCONN)
|
|
/// - `invalid-state`: The socket is already in the `listening` state. (EOPNOTSUPP, EINVAL on Windows)
|
|
/// - `timeout`: Connection timed out. (ETIMEDOUT)
|
|
/// - `connection-refused`: The connection was forcefully rejected. (ECONNREFUSED)
|
|
/// - `connection-reset`: The connection was reset. (ECONNRESET)
|
|
/// - `connection-aborted`: The connection was aborted. (ECONNABORTED)
|
|
/// - `remote-unreachable`: The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET)
|
|
/// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD)
|
|
///
|
|
/// # References
|
|
/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html>
|
|
/// - <https://man7.org/linux/man-pages/man2/connect.2.html>
|
|
/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-connect>
|
|
/// - <https://man.freebsd.org/cgi/man.cgi?connect>
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
connect: async func(remote-address: ip-socket-address) -> result<_, error-code>;
|
|
/// Start listening and return a stream of new inbound connections.
|
|
///
|
|
/// Transitions the socket into the `listening` state. This can be called
|
|
/// at most once per socket.
|
|
///
|
|
/// If the socket is not already explicitly bound, this function will
|
|
/// implicitly bind the socket to a random free port.
|
|
///
|
|
/// Normally, the returned sockets are bound, in the `connected` state
|
|
/// and immediately ready for I/O. Though, depending on exact timing and
|
|
/// circumstances, a newly accepted connection may already be `closed`
|
|
/// by the time the server attempts to perform its first I/O on it. This
|
|
/// is true regardless of whether the WASI implementation uses
|
|
/// "synthesized" sockets or not (see Implementors Notes below).
|
|
///
|
|
/// The following properties are inherited from the listener socket:
|
|
/// - `address-family`
|
|
/// - `keep-alive-enabled`
|
|
/// - `keep-alive-idle-time`
|
|
/// - `keep-alive-interval`
|
|
/// - `keep-alive-count`
|
|
/// - `hop-limit`
|
|
/// - `receive-buffer-size`
|
|
/// - `send-buffer-size`
|
|
///
|
|
/// # Typical errors
|
|
/// - `invalid-state`: The socket is already in the `connected` state. (EISCONN, EINVAL on BSD)
|
|
/// - `invalid-state`: The socket is already in the `listening` state.
|
|
/// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE)
|
|
///
|
|
/// # Implementors note
|
|
/// This method returns a single perpetual stream that should only close
|
|
/// on fatal errors (if any). Yet, the POSIX' `accept` function may also
|
|
/// return transient errors (e.g. ECONNABORTED). The exact details differ
|
|
/// per operation system. For example, the Linux manual mentions:
|
|
///
|
|
/// > Linux accept() passes already-pending network errors on the new
|
|
/// > socket as an error code from accept(). This behavior differs from
|
|
/// > other BSD socket implementations. For reliable operation the
|
|
/// > application should detect the network errors defined for the
|
|
/// > protocol after accept() and treat them like EAGAIN by retrying.
|
|
/// > In the case of TCP/IP, these are ENETDOWN, EPROTO, ENOPROTOOPT,
|
|
/// > EHOSTDOWN, ENONET, EHOSTUNREACH, EOPNOTSUPP, and ENETUNREACH.
|
|
/// Source: https://man7.org/linux/man-pages/man2/accept.2.html
|
|
///
|
|
/// WASI implementations have two options to handle this:
|
|
/// - Optionally log it and then skip over non-fatal errors returned by
|
|
/// `accept`. Guest code never gets to see these failures. Or:
|
|
/// - Synthesize a `tcp-socket` resource that exposes the error when
|
|
/// attempting to send or receive on it. Guest code then sees these
|
|
/// failures as regular I/O errors.
|
|
///
|
|
/// In either case, the stream returned by this `listen` method remains
|
|
/// operational.
|
|
///
|
|
/// # References
|
|
/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/listen.html>
|
|
/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/accept.html>
|
|
/// - <https://man7.org/linux/man-pages/man2/listen.2.html>
|
|
/// - <https://man7.org/linux/man-pages/man2/accept.2.html>
|
|
/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-listen>
|
|
/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-accept>
|
|
/// - <https://man.freebsd.org/cgi/man.cgi?query=listen&sektion=2>
|
|
/// - <https://man.freebsd.org/cgi/man.cgi?query=accept&sektion=2>
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
listen: func() -> result<stream<tcp-socket>, error-code>;
|
|
/// Transmit data to peer.
|
|
///
|
|
/// The caller should close the stream when it has no more data to send
|
|
/// to the peer. Under normal circumstances this will cause a FIN packet
|
|
/// to be sent out. Closing the stream is equivalent to calling
|
|
/// `shutdown(SHUT_WR)` in POSIX.
|
|
///
|
|
/// This function may be called at most once and returns once the full
|
|
/// contents of the stream are transmitted or an error is encountered.
|
|
///
|
|
/// # Typical errors
|
|
/// - `invalid-state`: The socket is not in the `connected` state. (ENOTCONN)
|
|
/// - `connection-reset`: The connection was reset. (ECONNRESET)
|
|
/// - `remote-unreachable`: The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET)
|
|
///
|
|
/// # References
|
|
/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/send.html>
|
|
/// - <https://man7.org/linux/man-pages/man2/send.2.html>
|
|
/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-send>
|
|
/// - <https://man.freebsd.org/cgi/man.cgi?query=send&sektion=2>
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
send: async func(data: stream<u8>) -> result<_, error-code>;
|
|
/// Read data from peer.
|
|
///
|
|
/// This function returns a `stream` which provides the data received from the
|
|
/// socket, and a `future` providing additional error information in case the
|
|
/// socket is closed abnormally.
|
|
///
|
|
/// If the socket is closed normally, `stream.read` on the `stream` will return
|
|
/// `read-status::closed` with no `error-context` and the future resolves to
|
|
/// the value `ok`. If the socket is closed abnormally, `stream.read` on the
|
|
/// `stream` returns `read-status::closed` with an `error-context` and the future
|
|
/// resolves to `err` with an `error-code`.
|
|
///
|
|
/// `receive` is meant to be called only once per socket. If it is called more
|
|
/// than once, the subsequent calls return a new `stream` that fails as if it
|
|
/// were closed abnormally.
|
|
///
|
|
/// If the caller is not expecting to receive any data from the peer,
|
|
/// they may drop the stream. Any data still in the receive queue
|
|
/// will be discarded. This is equivalent to calling `shutdown(SHUT_RD)`
|
|
/// in POSIX.
|
|
///
|
|
/// # Typical errors
|
|
/// - `invalid-state`: The socket is not in the `connected` state. (ENOTCONN)
|
|
/// - `connection-reset`: The connection was reset. (ECONNRESET)
|
|
/// - `remote-unreachable`: The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET)
|
|
///
|
|
/// # References
|
|
/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/recv.html>
|
|
/// - <https://man7.org/linux/man-pages/man2/recv.2.html>
|
|
/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-recv>
|
|
/// - <https://man.freebsd.org/cgi/man.cgi?query=recv&sektion=2>
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
receive: func() -> tuple<stream<u8>, future<result<_, error-code>>>;
|
|
/// Get the bound local address.
|
|
///
|
|
/// POSIX mentions:
|
|
/// > If the socket has not been bound to a local name, the value
|
|
/// > stored in the object pointed to by `address` is unspecified.
|
|
///
|
|
/// WASI is stricter and requires `get-local-address` to return `invalid-state` when the socket hasn't been bound yet.
|
|
///
|
|
/// # Typical errors
|
|
/// - `invalid-state`: The socket is not bound to any local address.
|
|
///
|
|
/// # References
|
|
/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockname.html>
|
|
/// - <https://man7.org/linux/man-pages/man2/getsockname.2.html>
|
|
/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-getsockname>
|
|
/// - <https://man.freebsd.org/cgi/man.cgi?getsockname>
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
get-local-address: func() -> result<ip-socket-address, error-code>;
|
|
/// Get the remote address.
|
|
///
|
|
/// # Typical errors
|
|
/// - `invalid-state`: The socket is not connected to a remote address. (ENOTCONN)
|
|
///
|
|
/// # References
|
|
/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpeername.html>
|
|
/// - <https://man7.org/linux/man-pages/man2/getpeername.2.html>
|
|
/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-getpeername>
|
|
/// - <https://man.freebsd.org/cgi/man.cgi?query=getpeername&sektion=2&n=1>
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
get-remote-address: func() -> result<ip-socket-address, error-code>;
|
|
/// Whether the socket is in the `listening` state.
|
|
///
|
|
/// Equivalent to the SO_ACCEPTCONN socket option.
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
get-is-listening: func() -> bool;
|
|
/// Whether this is a IPv4 or IPv6 socket.
|
|
///
|
|
/// This is the value passed to the constructor.
|
|
///
|
|
/// Equivalent to the SO_DOMAIN socket option.
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
get-address-family: func() -> ip-address-family;
|
|
/// Hints the desired listen queue size. Implementations are free to ignore this.
|
|
///
|
|
/// If the provided value is 0, an `invalid-argument` error is returned.
|
|
/// Any other value will never cause an error, but it might be silently clamped and/or rounded.
|
|
///
|
|
/// # Typical errors
|
|
/// - `not-supported`: (set) The platform does not support changing the backlog size after the initial listen.
|
|
/// - `invalid-argument`: (set) The provided value was 0.
|
|
/// - `invalid-state`: (set) The socket is in the `connecting` or `connected` state.
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
set-listen-backlog-size: func(value: u64) -> result<_, error-code>;
|
|
/// Enables or disables keepalive.
|
|
///
|
|
/// The keepalive behavior can be adjusted using:
|
|
/// - `keep-alive-idle-time`
|
|
/// - `keep-alive-interval`
|
|
/// - `keep-alive-count`
|
|
/// These properties can be configured while `keep-alive-enabled` is false, but only come into effect when `keep-alive-enabled` is true.
|
|
///
|
|
/// Equivalent to the SO_KEEPALIVE socket option.
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
get-keep-alive-enabled: func() -> result<bool, error-code>;
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
set-keep-alive-enabled: func(value: bool) -> result<_, error-code>;
|
|
/// Amount of time the connection has to be idle before TCP starts sending keepalive packets.
|
|
///
|
|
/// If the provided value is 0, an `invalid-argument` error is returned.
|
|
/// Any other value will never cause an error, but it might be silently clamped and/or rounded.
|
|
/// I.e. after setting a value, reading the same setting back may return a different value.
|
|
///
|
|
/// Equivalent to the TCP_KEEPIDLE socket option. (TCP_KEEPALIVE on MacOS)
|
|
///
|
|
/// # Typical errors
|
|
/// - `invalid-argument`: (set) The provided value was 0.
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
get-keep-alive-idle-time: func() -> result<duration, error-code>;
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
set-keep-alive-idle-time: func(value: duration) -> result<_, error-code>;
|
|
/// The time between keepalive packets.
|
|
///
|
|
/// If the provided value is 0, an `invalid-argument` error is returned.
|
|
/// Any other value will never cause an error, but it might be silently clamped and/or rounded.
|
|
/// I.e. after setting a value, reading the same setting back may return a different value.
|
|
///
|
|
/// Equivalent to the TCP_KEEPINTVL socket option.
|
|
///
|
|
/// # Typical errors
|
|
/// - `invalid-argument`: (set) The provided value was 0.
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
get-keep-alive-interval: func() -> result<duration, error-code>;
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
set-keep-alive-interval: func(value: duration) -> result<_, error-code>;
|
|
/// The maximum amount of keepalive packets TCP should send before aborting the connection.
|
|
///
|
|
/// If the provided value is 0, an `invalid-argument` error is returned.
|
|
/// Any other value will never cause an error, but it might be silently clamped and/or rounded.
|
|
/// I.e. after setting a value, reading the same setting back may return a different value.
|
|
///
|
|
/// Equivalent to the TCP_KEEPCNT socket option.
|
|
///
|
|
/// # Typical errors
|
|
/// - `invalid-argument`: (set) The provided value was 0.
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
get-keep-alive-count: func() -> result<u32, error-code>;
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
set-keep-alive-count: func(value: u32) -> result<_, error-code>;
|
|
/// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options.
|
|
///
|
|
/// If the provided value is 0, an `invalid-argument` error is returned.
|
|
///
|
|
/// # Typical errors
|
|
/// - `invalid-argument`: (set) The TTL value must be 1 or higher.
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
get-hop-limit: func() -> result<u8, error-code>;
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
set-hop-limit: func(value: u8) -> result<_, error-code>;
|
|
/// The kernel buffer space reserved for sends/receives on this socket.
|
|
///
|
|
/// If the provided value is 0, an `invalid-argument` error is returned.
|
|
/// Any other value will never cause an error, but it might be silently clamped and/or rounded.
|
|
/// I.e. after setting a value, reading the same setting back may return a different value.
|
|
///
|
|
/// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options.
|
|
///
|
|
/// # Typical errors
|
|
/// - `invalid-argument`: (set) The provided value was 0.
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
get-receive-buffer-size: func() -> result<u64, error-code>;
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
set-receive-buffer-size: func(value: u64) -> result<_, error-code>;
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
get-send-buffer-size: func() -> result<u64, error-code>;
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
set-send-buffer-size: func(value: u64) -> result<_, error-code>;
|
|
}
|
|
|
|
/// A UDP socket handle.
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
resource udp-socket {
|
|
/// Create a new UDP socket.
|
|
///
|
|
/// Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` in POSIX.
|
|
/// On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise.
|
|
///
|
|
/// Unlike POSIX, WASI sockets have no notion of a socket-level
|
|
/// `O_NONBLOCK` flag. Instead they fully rely on the Component Model's
|
|
/// async support.
|
|
///
|
|
/// # References:
|
|
/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/socket.html>
|
|
/// - <https://man7.org/linux/man-pages/man2/socket.2.html>
|
|
/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketw>
|
|
/// - <https://man.freebsd.org/cgi/man.cgi?query=socket&sektion=2>
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
create: static func(address-family: ip-address-family) -> result<udp-socket, error-code>;
|
|
/// Bind the socket to 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 port is zero, the socket will be bound to a random free port.
|
|
///
|
|
/// # Typical errors
|
|
/// - `invalid-argument`: The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows)
|
|
/// - `invalid-state`: The socket is already bound. (EINVAL)
|
|
/// - `address-in-use`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows)
|
|
/// - `address-in-use`: Address is already in use. (EADDRINUSE)
|
|
/// - `address-not-bindable`: `local-address` is not an address that can be bound to. (EADDRNOTAVAIL)
|
|
///
|
|
/// # References
|
|
/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/bind.html>
|
|
/// - <https://man7.org/linux/man-pages/man2/bind.2.html>
|
|
/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-bind>
|
|
/// - <https://man.freebsd.org/cgi/man.cgi?query=bind&sektion=2&format=html>
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
bind: func(local-address: ip-socket-address) -> result<_, error-code>;
|
|
/// Associate this socket with a specific peer address.
|
|
///
|
|
/// On success, the `remote-address` of the socket is updated.
|
|
/// The `local-address` may be updated as well, based on the best network
|
|
/// path to `remote-address`. If the socket was not already explicitly
|
|
/// bound, this function will implicitly bind the socket to a random
|
|
/// free port.
|
|
///
|
|
/// When a UDP socket is "connected", the `send` and `receive` methods
|
|
/// are limited to communicating with that peer only:
|
|
/// - `send` can only be used to send to this destination.
|
|
/// - `receive` will only return datagrams sent from the provided `remote-address`.
|
|
///
|
|
/// The name "connect" was kept to align with the existing POSIX
|
|
/// terminology. Other than that, this function only changes the local
|
|
/// socket configuration and does not generate any network traffic.
|
|
/// The peer is not aware of this "connection".
|
|
///
|
|
/// This method may be called multiple times on the same socket to change
|
|
/// its association, but only the most recent one will be effective.
|
|
///
|
|
/// # Typical errors
|
|
/// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT)
|
|
/// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL)
|
|
/// - `invalid-argument`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL)
|
|
/// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD)
|
|
///
|
|
/// # Implementors note
|
|
/// If the socket is already connected, some platforms (e.g. Linux)
|
|
/// require a disconnect before connecting to a different peer address.
|
|
///
|
|
/// # References
|
|
/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html>
|
|
/// - <https://man7.org/linux/man-pages/man2/connect.2.html>
|
|
/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-connect>
|
|
/// - <https://man.freebsd.org/cgi/man.cgi?connect>
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
connect: func(remote-address: ip-socket-address) -> result<_, error-code>;
|
|
/// Dissociate this socket from its peer address.
|
|
///
|
|
/// After calling this method, `send` & `receive` are free to communicate
|
|
/// with any address again.
|
|
///
|
|
/// The POSIX equivalent of this is calling `connect` with an `AF_UNSPEC` address.
|
|
///
|
|
/// # Typical errors
|
|
/// - `invalid-state`: The socket is not connected.
|
|
///
|
|
/// # References
|
|
/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html>
|
|
/// - <https://man7.org/linux/man-pages/man2/connect.2.html>
|
|
/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-connect>
|
|
/// - <https://man.freebsd.org/cgi/man.cgi?connect>
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
disconnect: func() -> result<_, error-code>;
|
|
/// Send a message on the socket to a particular peer.
|
|
///
|
|
/// If the socket is connected, the peer address may be left empty. In
|
|
/// that case this is equivalent to `send` in POSIX. Otherwise it is
|
|
/// equivalent to `sendto`.
|
|
///
|
|
/// Additionally, if the socket is connected, a `remote-address` argument
|
|
/// _may_ be provided but then it must be identical to the address
|
|
/// passed to `connect`.
|
|
///
|
|
/// Implementations may trap if the `data` length exceeds 64 KiB.
|
|
///
|
|
/// # Typical errors
|
|
/// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT)
|
|
/// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL)
|
|
/// - `invalid-argument`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL)
|
|
/// - `invalid-argument`: The socket is in "connected" mode and `remote-address` is `some` value that does not match the address passed to `connect`. (EISCONN)
|
|
/// - `invalid-argument`: The socket is not "connected" and no value for `remote-address` was provided. (EDESTADDRREQ)
|
|
/// - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET)
|
|
/// - `connection-refused`: The connection was refused. (ECONNREFUSED)
|
|
/// - `datagram-too-large`: The datagram is too large. (EMSGSIZE)
|
|
///
|
|
/// # References
|
|
/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/sendto.html>
|
|
/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/sendmsg.html>
|
|
/// - <https://man7.org/linux/man-pages/man2/send.2.html>
|
|
/// - <https://man7.org/linux/man-pages/man2/sendmmsg.2.html>
|
|
/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-send>
|
|
/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-sendto>
|
|
/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasendmsg>
|
|
/// - <https://man.freebsd.org/cgi/man.cgi?query=send&sektion=2>
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
send: async func(data: list<u8>, remote-address: option<ip-socket-address>) -> result<_, error-code>;
|
|
/// Receive a message on the socket.
|
|
///
|
|
/// On success, the return value contains a tuple of the received data
|
|
/// and the address of the sender. Theoretical maximum length of the
|
|
/// data is 64 KiB. Though in practice, it will typically be less than
|
|
/// 1500 bytes.
|
|
///
|
|
/// If the socket is connected, the sender address is guaranteed to
|
|
/// match the remote address passed to `connect`.
|
|
///
|
|
/// # Typical errors
|
|
/// - `invalid-state`: The socket has not been bound yet.
|
|
/// - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET)
|
|
/// - `connection-refused`: The connection was refused. (ECONNREFUSED)
|
|
///
|
|
/// # References
|
|
/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvfrom.html>
|
|
/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvmsg.html>
|
|
/// - <https://man7.org/linux/man-pages/man2/recv.2.html>
|
|
/// - <https://man7.org/linux/man-pages/man2/recvmmsg.2.html>
|
|
/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-recvfrom>
|
|
/// - <https://learn.microsoft.com/en-us/windows/win32/api/mswsock/nc-mswsock-lpfn_wsarecvmsg>
|
|
/// - <https://man.freebsd.org/cgi/man.cgi?query=recv&sektion=2>
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
receive: async func() -> result<tuple<list<u8>, ip-socket-address>, error-code>;
|
|
/// Get the current bound address.
|
|
///
|
|
/// POSIX mentions:
|
|
/// > If the socket has not been bound to a local name, the value
|
|
/// > stored in the object pointed to by `address` is unspecified.
|
|
///
|
|
/// WASI is stricter and requires `get-local-address` to return `invalid-state` when the socket hasn't been bound yet.
|
|
///
|
|
/// # Typical errors
|
|
/// - `invalid-state`: The socket is not bound to any local address.
|
|
///
|
|
/// # References
|
|
/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockname.html>
|
|
/// - <https://man7.org/linux/man-pages/man2/getsockname.2.html>
|
|
/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-getsockname>
|
|
/// - <https://man.freebsd.org/cgi/man.cgi?getsockname>
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
get-local-address: func() -> result<ip-socket-address, error-code>;
|
|
/// Get the address the socket is currently "connected" to.
|
|
///
|
|
/// # Typical errors
|
|
/// - `invalid-state`: The socket is not "connected" to a specific remote address. (ENOTCONN)
|
|
///
|
|
/// # References
|
|
/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpeername.html>
|
|
/// - <https://man7.org/linux/man-pages/man2/getpeername.2.html>
|
|
/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-getpeername>
|
|
/// - <https://man.freebsd.org/cgi/man.cgi?query=getpeername&sektion=2&n=1>
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
get-remote-address: func() -> result<ip-socket-address, error-code>;
|
|
/// Whether this is a IPv4 or IPv6 socket.
|
|
///
|
|
/// This is the value passed to the constructor.
|
|
///
|
|
/// Equivalent to the SO_DOMAIN socket option.
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
get-address-family: func() -> ip-address-family;
|
|
/// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options.
|
|
///
|
|
/// If the provided value is 0, an `invalid-argument` error is returned.
|
|
///
|
|
/// # Typical errors
|
|
/// - `invalid-argument`: (set) The TTL value must be 1 or higher.
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
get-unicast-hop-limit: func() -> result<u8, error-code>;
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
set-unicast-hop-limit: func(value: u8) -> result<_, error-code>;
|
|
/// The kernel buffer space reserved for sends/receives on this socket.
|
|
///
|
|
/// If the provided value is 0, an `invalid-argument` error is returned.
|
|
/// Any other value will never cause an error, but it might be silently clamped and/or rounded.
|
|
/// I.e. after setting a value, reading the same setting back may return a different value.
|
|
///
|
|
/// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options.
|
|
///
|
|
/// # Typical errors
|
|
/// - `invalid-argument`: (set) The provided value was 0.
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
get-receive-buffer-size: func() -> result<u64, error-code>;
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
set-receive-buffer-size: func(value: u64) -> result<_, error-code>;
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
get-send-buffer-size: func() -> result<u64, error-code>;
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
set-send-buffer-size: func(value: u64) -> result<_, error-code>;
|
|
}
|
|
}
|
|
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
interface ip-name-lookup {
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
use types.{ip-address};
|
|
|
|
/// Lookup error codes.
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
enum error-code {
|
|
/// Unknown error
|
|
unknown,
|
|
/// Access denied.
|
|
///
|
|
/// POSIX equivalent: EACCES, EPERM
|
|
access-denied,
|
|
/// `name` is a syntactically invalid domain name or IP address.
|
|
///
|
|
/// POSIX equivalent: EINVAL
|
|
invalid-argument,
|
|
/// Name does not exist or has no suitable associated IP addresses.
|
|
///
|
|
/// POSIX equivalent: EAI_NONAME, EAI_NODATA, EAI_ADDRFAMILY
|
|
name-unresolvable,
|
|
/// A temporary failure in name resolution occurred.
|
|
///
|
|
/// POSIX equivalent: EAI_AGAIN
|
|
temporary-resolver-failure,
|
|
/// A permanent failure in name resolution occurred.
|
|
///
|
|
/// POSIX equivalent: EAI_FAIL
|
|
permanent-resolver-failure,
|
|
}
|
|
|
|
/// 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.
|
|
///
|
|
/// The results are returned in connection order preference.
|
|
///
|
|
/// This function never succeeds with 0 results. It either fails or succeeds
|
|
/// with at least one address. Additionally, this function never returns
|
|
/// IPv4-mapped IPv6 addresses.
|
|
///
|
|
/// The returned future will resolve to an error code in case of failure.
|
|
/// It will resolve to success once the returned stream is exhausted.
|
|
///
|
|
/// # 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>
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
resolve-addresses: async func(name: string) -> result<list<ip-address>, error-code>;
|
|
}
|
|
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
world imports {
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
import wasi:clocks/types@0.3.0-rc-2026-01-06;
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
import types;
|
|
@since(version = 0.3.0-rc-2026-01-06)
|
|
import ip-name-lookup;
|
|
}
|