diff --git a/cli.md b/cli.md index 10b8003..5947097 100644 --- a/cli.md +++ b/cli.md @@ -10,6 +10,13 @@
  • interface timezone
  • interface io
  • interface filesystem
  • +
  • interface network
  • +
  • interface instance-network
  • +
  • interface ip-name-lookup
  • +
  • interface tcp
  • +
  • interface tcp-create-socket
  • +
  • interface udp
  • +
  • interface udp-create-socket
  • interface random
  • interface exit
  • type input-stream
  • @@ -805,7 +812,7 @@ implementations which can set this to a non-none value should do so.

    Write mode: Data can be written to.

  • -

    non-blocking:

    +

    non-blocking:

    Requests non-blocking operation.

    When this flag is enabled, functions may return immediately with an error-code::would-block error code in situations where they would @@ -1048,7 +1055,7 @@ from fdstat_get in earlier versions of WASI.

    set-flags: func

    Set status flags associated with a descriptor.

    -

    This function may only change the non-blocking flag.

    +

    This function may only change the non-blocking flag.

    Note: This is similar to fcntl(fd, F_SETFL, flags) in POSIX.

    Note: This was called fd_fdstat_set_flags in earlier versions of WASI.

    Params
    @@ -1483,6 +1490,997 @@ be used.

    +

    Import interface network

    +
    +

    Types

    +

    type network

    +

    u32

    +

    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.

    +

    tuple ipv6-address

    +
    Tuple Fields
    + +

    record ipv6-socket-address

    +
    Record Fields
    + +

    tuple ipv4-address

    +
    Tuple Fields
    + +

    record ipv4-socket-address

    +
    Record Fields
    + +

    variant ip-socket-address

    +
    Variant Cases
    + +

    enum ip-address-family

    +
    Enum Cases
    + +

    variant ip-address

    +
    Variant Cases
    + +

    enum error

    +
    Enum Cases
    + +
    +

    Functions

    +

    drop-network: func

    +

    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.

    +
    Params
    + +

    Import interface instance-network

    +

    This interface provides a value-export of the default network handle..

    +
    +

    Types

    +

    type network

    +

    network

    +

    +---- +

    Functions

    +

    instance-network: func

    +

    Get a handle to the default network.

    +
    Return values
    + +

    Import interface ip-name-lookup

    +
    +

    Types

    +

    type pollable

    +

    pollable

    +

    +#### `type network` +[`network`](#network) +

    +#### `type error` +[`error`](#error) +

    +#### `type ip-address` +[`ip-address`](#ip_address) +

    +#### `type ip-address-family` +[`ip-address-family`](#ip_address_family) +

    +#### `type resolve-address-stream` +`u32` +

    +---- +

    Functions

    +

    resolve-addresses: func

    +

    Resolve an internet host name to a list of IP addresses.

    +

    See the wasi-socket proposal README.md for a comparison with getaddrinfo.

    +

    Parameters:

    + +

    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:

    + +

    References:

    + +
    Params
    + +
    Return values
    + +

    resolve-next-address: func

    +

    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.

    +
    Params
    + +
    Return values
    + +

    drop-resolve-address-stream: func

    +

    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.

    +
    Params
    + +

    non-blocking: func

    +

    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.

    +
    Params
    + +
    Return values
    + +

    set-non-blocking: func

    +
    Params
    + +
    Return values
    + +

    subscribe: func

    +

    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.

    +
    Params
    + +
    Return values
    + +

    Import interface tcp

    +
    +

    Types

    +

    type input-stream

    +

    input-stream

    +

    +#### `type output-stream` +[`output-stream`](#output_stream) +

    +#### `type pollable` +[`pollable`](#pollable) +

    +#### `type network` +[`network`](#network) +

    +#### `type error` +[`error`](#error) +

    +#### `type ip-socket-address` +[`ip-socket-address`](#ip_socket_address) +

    +#### `type ip-address-family` +[`ip-address-family`](#ip_address_family) +

    +#### `type tcp-socket` +`u32` +

    A TCP socket handle. +

    enum shutdown-type

    +
    Enum Cases
    + +
    +

    Functions

    +

    bind: func

    +

    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:

    + +

    References

    + +
    Params
    + +
    Return values
    + +

    connect: func

    +

    Connect to a remote endpoint.

    +

    On success:

    + +

    Fails when:

    + +

    References

    + +
    Params
    + +
    Return values
    + +

    listen: func

    +

    Start listening for new connections.

    +

    Transitions the socket into the Listener state.

    +

    Fails when:

    + +

    References

    + +
    Params
    + +
    Return values
    + +

    accept: func

    +

    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:

    + +
    Params
    + +
    Return values
    + +

    local-address: func

    +

    Get the bound local address.

    +

    Returns an error if the socket is not bound.

    +

    References

    + +
    Params
    + +
    Return values
    + +

    remote-address: func

    +

    Get the bound remote address.

    +

    Fails when the socket is not in the Connection state.

    +

    References

    + +
    Params
    + +
    Return values
    + +

    address-family: func

    +

    Whether this is a IPv4 or IPv6 socket.

    +

    Equivalent to the SO_DOMAIN socket option.

    +
    Params
    + +
    Return values
    + +

    ipv6-only: func

    +

    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.

    +
    Params
    + +
    Return values
    + +

    set-ipv6-only: func

    +
    Params
    + +
    Return values
    + +

    set-listen-backlog-size: func

    +

    Hints the desired listen queue size. Implementations are free to ignore this.

    +
    Params
    + +
    Return values
    + +

    keep-alive: func

    +

    Equivalent to the SO_KEEPALIVE socket option.

    +
    Params
    + +
    Return values
    + +

    set-keep-alive: func

    +
    Params
    + +
    Return values
    + +

    no-delay: func

    +

    Equivalent to the TCP_NODELAY socket option.

    +
    Params
    + +
    Return values
    + +

    set-no-delay: func

    +
    Params
    + +
    Return values
    + +

    unicast-hop-limit: func

    +

    Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options.

    +
    Params
    + +
    Return values
    + +

    set-unicast-hop-limit: func

    +
    Params
    + +
    Return values
    + +

    receive-buffer-size: func

    +

    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.

    +
    Params
    + +
    Return values
    + +

    set-receive-buffer-size: func

    +
    Params
    + +
    Return values
    + +

    send-buffer-size: func

    +
    Params
    + +
    Return values
    + +

    set-send-buffer-size: func

    +
    Params
    + +
    Return values
    + +

    non-blocking: func

    +

    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.

    +
    Params
    + +
    Return values
    + +

    set-non-blocking: func

    +
    Params
    + +
    Return values
    + +

    subscribe: func

    +

    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.

    +
    Params
    + +
    Return values
    + +

    shutdown: func

    +

    Gracefully shut down the connection.

    + +

    The shutdown function does not close the socket.

    +

    Fails when the socket is not in the Connection state.

    +

    References

    + +
    Params
    + +
    Return values
    + +

    drop-tcp-socket: func

    +

    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.

    +
    Params
    + +

    Import interface tcp-create-socket

    +
    +

    Types

    +

    type network

    +

    network

    +

    +#### `type error` +[`error`](#error) +

    +#### `type ip-address-family` +[`ip-address-family`](#ip_address_family) +

    +#### `type tcp-socket` +[`tcp-socket`](#tcp_socket) +

    +---- +

    Functions

    +

    create-tcp-socket: func

    +

    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:

    + +
    Params
    + +
    Return values
    + +

    Import interface udp

    +
    +

    Types

    +

    type pollable

    +

    pollable

    +

    +#### `type network` +[`network`](#network) +

    +#### `type error` +[`error`](#error) +

    +#### `type ip-socket-address` +[`ip-socket-address`](#ip_socket_address) +

    +#### `type ip-address-family` +[`ip-address-family`](#ip_address_family) +

    +#### `type udp-socket` +`u32` +

    A UDP socket handle. +

    record datagram

    +
    Record Fields
    + +
    +

    Functions

    +

    bind: func

    +

    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 connect, send or receive operation will +implicitly bind the socket.

    +

    Fails when:

    + +

    References

    + +
    Params
    + +
    Return values
    + +

    connect: func

    +

    Set the destination address.

    +

    The local-address is updated based on the best network path to remote-address.

    +

    When a destination address is set:

    + +

    Note that this function does not generate any network traffic and the peer is not aware of this "connection".

    +

    Fails when:

    + +

    References

    + +
    Params
    + +
    Return values
    + +

    receive: func

    +

    Receive a message.

    +

    Returns:

    + +

    Fails when:

    + +

    References

    + +
    Params
    + +
    Return values
    + +

    send: func

    +

    Send a message to a specific destination address.

    +

    The remote address option is required. To send a message to the "connected" peer, +call remote-address to get their address.

    +

    Fails when:

    + +

    References

    + +
    Params
    + +
    Return values
    + +

    local-address: func

    +

    Get the current bound address.

    +

    Returns an error if the socket is not bound.

    +

    References

    + +
    Params
    + +
    Return values
    + +

    remote-address: func

    +

    Get the address set with connect.

    +

    References

    + +
    Params
    + +
    Return values
    + +

    address-family: func

    +

    Whether this is a IPv4 or IPv6 socket.

    +

    Equivalent to the SO_DOMAIN socket option.

    +
    Params
    + +
    Return values
    + +

    ipv6-only: func

    +

    Whether IPv4 compatibility (dual-stack) mode is disabled or not. +Implementations are not required to support dual-stack mode, so calling set-ipv6-only(false) might fail.

    +

    Fails when called on an IPv4 socket.

    +

    Equivalent to the IPV6_V6ONLY socket option.

    +
    Params
    + +
    Return values
    + +

    set-ipv6-only: func

    +
    Params
    + +
    Return values
    + +

    unicast-hop-limit: func

    +

    Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options.

    +
    Params
    + +
    Return values
    + +

    set-unicast-hop-limit: func

    +
    Params
    + +
    Return values
    + +

    receive-buffer-size: func

    +

    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.

    +
    Params
    + +
    Return values
    + +

    set-receive-buffer-size: func

    +
    Params
    + +
    Return values
    + +

    send-buffer-size: func

    +
    Params
    + +
    Return values
    + +

    set-send-buffer-size: func

    +
    Params
    + +
    Return values
    + +

    non-blocking: func

    +

    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.

    +
    Params
    + +
    Return values
    + +

    set-non-blocking: func

    +
    Params
    + +
    Return values
    + +

    subscribe: func

    +

    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.

    +
    Params
    + +
    Return values
    + +

    drop-udp-socket: func

    +

    Dispose of the specified udp-socket, after which it may no longer be used.

    +

    Note: this function is scheduled to be removed when Resources are natively supported in Wit.

    +
    Params
    + +

    Import interface udp-create-socket

    +
    +

    Types

    +

    type network

    +

    network

    +

    +#### `type error` +[`error`](#error) +

    +#### `type ip-address-family` +[`ip-address-family`](#ip_address_family) +

    +#### `type udp-socket` +[`udp-socket`](#udp_socket) +

    +---- +

    Functions

    +

    create-udp-socket: func

    +

    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:

    + +
    Params
    + +
    Return values
    +

    Import interface random

    WASI Random is a random data API.

    It is intended to be portable at least between Unix-family platforms and diff --git a/wit/cli.wit b/wit/cli.wit index 3946023..9eccbb7 100644 --- a/wit/cli.wit +++ b/wit/cli.wit @@ -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 diff --git a/wit/deps/sockets/instance-network.wit b/wit/deps/sockets/instance-network.wit new file mode 100644 index 0000000..b1f5c98 --- /dev/null +++ b/wit/deps/sockets/instance-network.wit @@ -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 + +} diff --git a/wit/deps/sockets/ip-name-lookup.wit b/wit/deps/sockets/ip-name-lookup.wit new file mode 100644 index 0000000..dd85d58 --- /dev/null +++ b/wit/deps/sockets/ip-name-lookup.wit @@ -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, include-unavailable: bool) -> result + + + + 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, 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 + 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 +} diff --git a/wit/deps/sockets/network.wit b/wit/deps/sockets/network.wit new file mode 100644 index 0000000..5f2ffd3 --- /dev/null +++ b/wit/deps/sockets/network.wit @@ -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 + type ipv6-address = tuple + + 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), + } + +} diff --git a/wit/deps/sockets/tcp-create-socket.wit b/wit/deps/sockets/tcp-create-socket.wit new file mode 100644 index 0000000..11c4145 --- /dev/null +++ b/wit/deps/sockets/tcp-create-socket.wit @@ -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 +} diff --git a/wit/deps/sockets/tcp.wit b/wit/deps/sockets/tcp.wit new file mode 100644 index 0000000..a21c480 --- /dev/null +++ b/wit/deps/sockets/tcp.wit @@ -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, 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, 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 + + /// 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 + + /// Whether this is a IPv4 or IPv6 socket. + /// + /// Equivalent to the SO_DOMAIN socket option. + address-family: func(this: tcp-socket) -> result + + /// 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 + 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 + 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 + 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 + 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 + set-receive-buffer-size: func(this: tcp-socket, value: u64) -> result<_, error> + send-buffer-size: func(this: tcp-socket) -> result + 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 + 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) +} diff --git a/wit/deps/sockets/udp-create-socket.wit b/wit/deps/sockets/udp-create-socket.wit new file mode 100644 index 0000000..46d5012 --- /dev/null +++ b/wit/deps/sockets/udp-create-socket.wit @@ -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 +} diff --git a/wit/deps/sockets/udp.wit b/wit/deps/sockets/udp.wit new file mode 100644 index 0000000..c867827 --- /dev/null +++ b/wit/deps/sockets/udp.wit @@ -0,0 +1,162 @@ + +default interface udp { + use poll.poll.{pollable} + use pkg.network.{network, error, ip-socket-address, ip-address-family} + + + /// A UDP socket handle. + type udp-socket = u32 + + + record datagram { + data: list, // Theoretical max size: ~64 KiB. In practice, typically less than 1500 bytes. + remote-address: ip-socket-address, + + /// Possible future additions: + /// local-address: ip-socket-address, // IP_PKTINFO / IP_RECVDSTADDR / IPV6_PKTINFO + /// local-interface: u32, // IP_PKTINFO / IP_RECVIF + /// ttl: u8, // IP_RECVTTL + /// dscp: u6, // IP_RECVTOS + /// ecn: u2, // IP_RECVTOS + } + + + + /// 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 connect, send or receive 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: udp-socket, network: network, local-address: ip-socket-address) -> result<_, error> + + /// Set the destination address. + /// + /// The local-address is updated based on the best network path to `remote-address`. + /// + /// When a destination address is set: + /// - all receive operations will only return datagrams sent from the provided `remote-address`. + /// - the `send` function can only be used to send to this destination. + /// + /// Note that this function does not generate any network traffic and the peer is not aware of this "connection". + /// + /// Fails when: + /// - the socket is already bound to a different network. + /// + /// References + /// - https://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html + /// - https://man7.org/linux/man-pages/man2/connect.2.html + connect: func(this: udp-socket, network: network, remote-address: ip-socket-address) -> result<_, error> + + /// Receive a message. + /// + /// Returns: + /// - The sender address of the datagram + /// - The number of bytes read. + /// + /// Fails when: + /// - the socket is not bound. + /// + /// 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 + receive: func(this: udp-socket) -> result + + /// Send a message to a specific destination address. + /// + /// The remote address option is required. To send a message to the "connected" peer, + /// call `remote-address` to get their address. + /// + /// Fails when: + /// - the socket is not bound. Unlike POSIX, this function does not perform an implicit bind. + /// - the socket is in "connected" mode and the `datagram.remote-address` does not match the address passed to `connect`. + /// + /// 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 + send: func(this: udp-socket, datagram: datagram) -> result<_, error> + + /// Get the current bound 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: udp-socket) -> result + + /// Get the address set with `connect`. + /// + /// References + /// - https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpeername.html + /// - https://man7.org/linux/man-pages/man2/getpeername.2.html + remote-address: func(this: udp-socket) -> result + + /// Whether this is a IPv4 or IPv6 socket. + /// + /// Equivalent to the SO_DOMAIN socket option. + address-family: func(this: udp-socket) -> result + + /// Whether IPv4 compatibility (dual-stack) mode is disabled or not. + /// Implementations are not required to support dual-stack mode, so 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: udp-socket) -> result + set-ipv6-only: func(this: udp-socket, value: bool) -> result<_, error> + + /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. + unicast-hop-limit: func(this: udp-socket) -> result + set-unicast-hop-limit: func(this: udp-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: udp-socket) -> result + set-receive-buffer-size: func(this: udp-socket, value: u64) -> result<_, error> + send-buffer-size: func(this: udp-socket) -> result + set-send-buffer-size: func(this: udp-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: udp-socket) -> result + set-non-blocking: func(this: udp-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: udp-socket) -> pollable + + /// Dispose of the specified `udp-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-udp-socket: func(this: udp-socket) +}