Resource

udp-socket

A UDP socket handle.

resource udp-socket;

F bind

bind: func(local-address: ip-socket-address) -> result​<_, 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

F connect

connect: func(remote-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

F disconnect

disconnect: func() -> result​<_, error-code>;

Dissociate this socket from its peer address.

After calling this method, send & receive are free to communicate with any remote 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

F send

send: async func(data: list​<u8>, remote-address: option​<ip-socket-address>) -> 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.

If the socket has not been explicitly bound, it will be implicitly bound to a random free port.

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)
  • address-in-use: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE)

Implementors note

WASI requires send to perform an implicit bind if the socket has not been bound. Not all platforms (notably Windows) exhibit this behavior natively. On such platforms, the WASI implementation should emulate it by performing the bind if the guest has not already done so.

References

F receive

receive: async func() -> result​<tuple​<list​<u8>, ip-socket-address>, 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

F get-local-address

get-local-address: func() -> result​<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

F get-remote-address

get-remote-address: func() -> result​<ip-socket-address, error-code>;

F get-address-family

get-address-family: func() -> ip-address-family;

Whether this is a IPv4 or IPv6 socket.

This is the value passed to the constructor.

Equivalent to the SO_DOMAIN socket option.

F get-unicast-hop-limit

get-unicast-hop-limit: func() -> result​<u8, 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.

F set-unicast-hop-limit

set-unicast-hop-limit: func(value: u8) -> result​<_, error-code>;

F get-receive-buffer-size

get-receive-buffer-size: func() -> result​<u64, error-code>;

Kernel buffer space reserved for sending/receiving on this socket. Implementations usually treat this as a cap the buffer can grow to, rather than allocating the full amount immediately.

If the provided value is 0, an invalid-argument error is returned. All other values are accepted without error, but may be clamped or rounded. As a result, the value read back from this setting may differ from the value that was set.

Equivalent to the SO_RCVBUF and SO_SNDBUF socket options.

Typical errors

  • invalid-argument: (set) The provided value was 0.

F set-receive-buffer-size

set-receive-buffer-size: func(value: u64) -> result​<_, error-code>;

F get-send-buffer-size

get-send-buffer-size: func() -> result​<u64, error-code>;

F set-send-buffer-size

set-send-buffer-size: func(value: u64) -> result​<_, error-code>;

F create

create: func(address-family: ip-address-family) -> result​<udp-socket, error-code>;

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: