Skip to content

Commit

Permalink
Sync changes from wasmtime:
Browse files Browse the repository at this point in the history
# TCP
- Allow `accept()` to return transient errors.
  The original provision was added to align with preview3 streams that may only fail once. However, after discussing with Dan Gohman, we came to the conclusion that a `stream` of `result<>` could do the trick fine too.
    Fixes: #22
- Fold `ephemeral-ports-exhausted` into `address-in-use`. There is no cross-platform way to know the distinction between them.
- Remove `concurrency-conflict` clutter,
  and just document it to be always possible.
- Simplify "not supported", "invalid argument" and "invalid state" error cases.
  There is a myriad of reasons why an argument might be invalid or an operation might be not supported. But there is few cross platform consistency in which of those error cases result in which error codes. Many wasi-sockets codes were unnecessarily detailed and had no standardized equivalent in POSIX, so wasi-libc will probably just map them all back into a single EOPNOTSUPP or EINVAL or ...
- Remove create-tcp/udp-socket not supported errors.
  These stem from back when the entire wasi-sockets proposal was one big single thing. In this day and age, when an implementation doesn't want to support TCP and/or UDP, it can simply _not_ implement that interface, rather than returning an error at runtime.
- Document that `connect` may return ECONNABORTED
- Document the set of socket options that are inherited through `accept`
- Clarify `connect` failure state:
```md
	POSIX mentions:
	> If connect() fails, the state of the socket is unspecified. Conforming applications should
	> close the file descriptor and create a new socket before attempting to reconnect.

	WASI prescribes the following behavior:
	- If `connect` fails because an input/state validation error, the socket should remain usable.
	- If a connection was actually attempted but failed, the socket should become unusable for further network communication.
```
- Clarify `local-address` behavior on unbound socket:
```md
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 `local-address` to return `not-bound` when the socket hasn't been bound yet.
```
- Remove TCP_NODELAY for the time being. The semantics of TCP_NODELAY (and TCP_CORK for that matter) and its effects on the output-stream needs to investigated and specified. I don't expect there to be anything insurmountable. Its just that I haven't had the time to do so yet and I can't promise to have it done before the stabilization Preview2. So, in order to get wasi-sockets ready for Preview2, it was discussed to temporarily remove `no-delay` and reevaluate its inclusion before Preview3.

# UDP

- Introduce new `incoming-datagram-stream` and `outgoing-datagram-stream` types and moved `receive` and `send` methods to those respectively. These streams are returned by `stream` and can be individually subscribed to. This resolves a design issue where a UDP server would end up in a spin loop because `receive` returned EWOULDBLOCK but poll_* always returned immediately because the socket was ready for sending. In this new setup, users can poll each direction separately. Fixes #64
- Dropped the `network` parameter from the `connect` call, because `bind` is now _required_ to perform IO.
- Enable send-like behaviour by making `outgoing-datagram::remote-address` optional. Fixes #57

# IP name lookup
- Remove the non-essential parameters for now. Post-preview2 these can be reevaluated again.
- Lift the restriction against parsing IP addresses. Before, implementations still needed to parse IP addresses to decide whether or not to return an error.
  • Loading branch information
badeend committed Nov 8, 2023
1 parent ecce14c commit bc6c19a
Show file tree
Hide file tree
Showing 6 changed files with 336 additions and 309 deletions.
46 changes: 18 additions & 28 deletions wit/ip-name-lookup.wit
Original file line number Diff line number Diff line change
@@ -1,50 +1,40 @@

interface ip-name-lookup {
use wasi:io/poll.{pollable};
use network.{network, error-code, ip-address, ip-address-family};
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.
///
/// # 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 fails or immediately returns successfully with a `resolve-address-stream`
/// that can be used to (asynchronously) fetch the results.
///
/// At the moment, the stream never completes successfully with 0 items. Ie. the first call
/// to `resolve-next-address` never returns `ok(none)`. This may change in the future.
///
///
/// 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-name`: `name` is a syntactically invalid domain name.
/// - `invalid-name`: `name` is an IP address.
/// - `address-family-not-supported`: The specified `address-family` is not supported. (EAI_FAMILY)
///
/// - `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, address-family: option<ip-address-family>, include-unavailable: bool) -> result<resolve-address-stream, error-code>;
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)
Expand All @@ -53,7 +43,7 @@ interface ip-name-lookup {
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;
Expand Down
81 changes: 25 additions & 56 deletions wit/network.wit
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@ interface network {
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 {
// ### GENERAL ERRORS ###
Expand All @@ -23,114 +24,82 @@ interface network {
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,


// ### IP ERRORS ###

/// The specified address-family is not supported.
address-family-not-supported,

/// An IPv4 address was passed to an IPv6 resource, or vice versa.
address-family-mismatch,

/// The socket address is not a valid remote address. E.g. the IP address is set to INADDR_ANY, or the port is set to 0.
invalid-remote-address,

/// The operation is only supported on IPv4 resources.
ipv4-only-operation,

/// The operation is only supported on IPv6 resources.
ipv6-only-operation,



// ### TCP & UDP SOCKET ERRORS ###

/// 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,

/// The socket is already attached to another network.
already-attached,

/// The socket is already bound.
already-bound,

/// The socket is already in the Connection state.
already-connected,

/// The socket is not bound to any local address.
not-bound,

/// The socket is not in the Connection state.
not-connected,

/// 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.
/// A bind operation failed because the provided address is already in use or because there are no ephemeral ports available.
address-in-use,

/// A bind operation failed because there are no ephemeral ports available.
ephemeral-ports-exhausted,

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


// ### TCP SOCKET ERRORS ###

/// The socket is already in the Listener state.
already-listening,

/// The socket is already in the Listener state.
not-listening,
// ### TCP SOCKET ERRORS ###

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

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


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


// ### UDP SOCKET ERRORS ###
datagram-too-large,


// ### NAME LOOKUP ERRORS ###

/// The provided name is a syntactically invalid domain name.
invalid-name,

/// Name does not exist or has no suitable associated IP addresses.
name-unresolvable,
Expand All @@ -144,7 +113,7 @@ interface network {

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

/// Similar to `AF_INET6` in POSIX.
ipv6,
Expand Down
15 changes: 7 additions & 8 deletions wit/tcp-create-socket.wit
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,19 @@ interface tcp-create-socket {
use 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.
///
///
/// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations.
///
///
/// # Typical errors
/// - `not-supported`: The host does not support TCP sockets. (EOPNOTSUPP)
/// - `address-family-not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT)
/// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE)
///
/// - `not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT)
/// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE)
///
/// # References
/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/socket.html>
/// - <https://man7.org/linux/man-pages/man2/socket.2.html>
Expand Down
Loading

0 comments on commit bc6c19a

Please sign in to comment.