1package wasi:[email protected]; 2 3@since(version = 0.2.0) 4interface network { 5 @unstable(feature = network-error-code) 6 use wasi:io/error@0.2.6.{error}; 7 8 /// An opaque resource that represents access to (a subset of) the network. 9 /// This enables context-based security for networking. 10 /// There is no need for this to map 1:1 to a physical network interface. 11 @since(version = 0.2.0) 12 resource network; 13 14 /// Error codes. 15 /// 16 /// In theory, every API can return any error code. 17 /// In practice, API's typically only return the errors documented per API 18 /// combined with a couple of errors that are always possible: 19 /// - `unknown` 20 /// - `access-denied` 21 /// - `not-supported` 22 /// - `out-of-memory` 23 /// - `concurrency-conflict` 24 /// 25 /// See each individual API for what the POSIX equivalents are. They sometimes differ per API. 26 @since(version = 0.2.0) 27 enum error-code { 28 /// Unknown error 29 unknown, 30 /// Access denied. 31 /// 32 /// POSIX equivalent: EACCES, EPERM 33 access-denied, 34 /// The operation is not supported. 35 /// 36 /// POSIX equivalent: EOPNOTSUPP 37 not-supported, 38 /// One of the arguments is invalid. 39 /// 40 /// POSIX equivalent: EINVAL 41 invalid-argument, 42 /// Not enough memory to complete the operation. 43 /// 44 /// POSIX equivalent: ENOMEM, ENOBUFS, EAI_MEMORY 45 out-of-memory, 46 /// The operation timed out before it could finish completely. 47 timeout, 48 /// This operation is incompatible with another asynchronous operation that is already in progress. 49 /// 50 /// POSIX equivalent: EALREADY 51 concurrency-conflict, 52 /// Trying to finish an asynchronous operation that: 53 /// - has not been started yet, or: 54 /// - was already finished by a previous `finish-*` call. 55 /// 56 /// Note: this is scheduled to be removed when `future`s are natively supported. 57 not-in-progress, 58 /// The operation has been aborted because it could not be completed immediately. 59 /// 60 /// Note: this is scheduled to be removed when `future`s are natively supported. 61 would-block, 62 /// The operation is not valid in the socket's current state. 63 invalid-state, 64 /// A new socket resource could not be created because of a system limit. 65 new-socket-limit, 66 /// A bind operation failed because the provided address is not an address that the `network` can bind to. 67 address-not-bindable, 68 /// A bind operation failed because the provided address is already in use or because there are no ephemeral ports available. 69 address-in-use, 70 /// The remote address is not reachable 71 remote-unreachable, 72 /// The TCP connection was forcefully rejected 73 connection-refused, 74 /// The TCP connection was reset. 75 connection-reset, 76 /// A TCP connection was aborted. 77 connection-aborted, 78 /// The size of a datagram sent to a UDP socket exceeded the maximum 79 /// supported size. 80 datagram-too-large, 81 /// Name does not exist or has no suitable associated IP addresses. 82 name-unresolvable, 83 /// A temporary failure in name resolution occurred. 84 temporary-resolver-failure, 85 /// A permanent failure in name resolution occurred. 86 permanent-resolver-failure, 87 } 88 89 @since(version = 0.2.0) 90 enum ip-address-family { 91 /// Similar to `AF_INET` in POSIX. 92 ipv4, 93 /// Similar to `AF_INET6` in POSIX. 94 ipv6, 95 } 96 97 @since(version = 0.2.0) 98 type ipv4-address = tuple<u8, u8, u8, u8>; 99 100 @since(version = 0.2.0) 101 type ipv6-address = tuple<u16, u16, u16, u16, u16, u16, u16, u16>; 102 103 @since(version = 0.2.0) 104 variant ip-address { 105 ipv4(ipv4-address), 106 ipv6(ipv6-address), 107 } 108 109 @since(version = 0.2.0) 110 record ipv4-socket-address { 111 /// sin_port 112 port: u16, 113 /// sin_addr 114 address: ipv4-address, 115 } 116 117 @since(version = 0.2.0) 118 record ipv6-socket-address { 119 /// sin6_port 120 port: u16, 121 /// sin6_flowinfo 122 flow-info: u32, 123 /// sin6_addr 124 address: ipv6-address, 125 /// sin6_scope_id 126 scope-id: u32, 127 } 128 129 @since(version = 0.2.0) 130 variant ip-socket-address { 131 ipv4(ipv4-socket-address), 132 ipv6(ipv6-socket-address), 133 } 134 135 /// Attempts to extract a network-related `error-code` from the stream 136 /// `error` provided. 137 /// 138 /// Stream operations which return `stream-error::last-operation-failed` 139 /// have a payload with more information about the operation that failed. 140 /// This payload can be passed through to this function to see if there's 141 /// network-related information about the error to return. 142 /// 143 /// Note that this function is fallible because not all stream-related 144 /// errors are network-related errors. 145 @unstable(feature = network-error-code) 146 network-error-code: func(err: borrow<error>) -> option<error-code>; 147} 148 149/// This interface provides a value-export of the default network handle.. 150@since(version = 0.2.0) 151interface instance-network { 152 @since(version = 0.2.0) 153 use network.{network}; 154 155 /// Get a handle to the default network. 156 @since(version = 0.2.0) 157 instance-network: func() -> network; 158} 159 160@since(version = 0.2.0) 161interface ip-name-lookup { 162 @since(version = 0.2.0) 163 use wasi:io/poll@0.2.6.{pollable}; 164 @since(version = 0.2.0) 165 use network.{network, error-code, ip-address}; 166 167 @since(version = 0.2.0) 168 resource resolve-address-stream { 169 /// Returns the next address from the resolver. 170 /// 171 /// This function should be called multiple times. On each call, it will 172 /// return the next address in connection order preference. If all 173 /// addresses have been exhausted, this function returns `none`. 174 /// 175 /// This function never returns IPv4-mapped IPv6 addresses. 176 /// 177 /// # Typical errors 178 /// - `name-unresolvable`: Name does not exist or has no suitable associated IP addresses. (EAI_NONAME, EAI_NODATA, EAI_ADDRFAMILY) 179 /// - `temporary-resolver-failure`: A temporary failure in name resolution occurred. (EAI_AGAIN) 180 /// - `permanent-resolver-failure`: A permanent failure in name resolution occurred. (EAI_FAIL) 181 /// - `would-block`: A result is not available yet. (EWOULDBLOCK, EAGAIN) 182 @since(version = 0.2.0) 183 resolve-next-address: func() -> result<option<ip-address>, error-code>; 184 /// Create a `pollable` which will resolve once the stream is ready for I/O. 185 /// 186 /// Note: this function is here for WASI 0.2 only. 187 /// It's planned to be removed when `future` is natively supported in Preview3. 188 @since(version = 0.2.0) 189 subscribe: func() -> pollable; 190 } 191 192 /// Resolve an internet host name to a list of IP addresses. 193 /// 194 /// Unicode domain names are automatically converted to ASCII using IDNA encoding. 195 /// If the input is an IP address string, the address is parsed and returned 196 /// as-is without making any external requests. 197 /// 198 /// See the wasi-socket proposal README.md for a comparison with getaddrinfo. 199 /// 200 /// This function never blocks. It either immediately fails or immediately 201 /// returns successfully with a `resolve-address-stream` that can be used 202 /// to (asynchronously) fetch the results. 203 /// 204 /// # Typical errors 205 /// - `invalid-argument`: `name` is a syntactically invalid domain name or IP address. 206 /// 207 /// # References: 208 /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getaddrinfo.html> 209 /// - <https://man7.org/linux/man-pages/man3/getaddrinfo.3.html> 210 /// - <https://learn.microsoft.com/en-us/windows/win32/api/ws2tcpip/nf-ws2tcpip-getaddrinfo> 211 /// - <https://man.freebsd.org/cgi/man.cgi?query=getaddrinfo&sektion=3> 212 @since(version = 0.2.0) 213 resolve-addresses: func(network: borrow<network>, name: string) -> result<resolve-address-stream, error-code>; 214} 215 216@since(version = 0.2.0) 217interface tcp { 218 @since(version = 0.2.0) 219 use wasi:io/streams@0.2.6.{input-stream, output-stream}; 220 @since(version = 0.2.0) 221 use wasi:io/poll@0.2.6.{pollable}; 222 @since(version = 0.2.0) 223 use wasi:clocks/monotonic-clock@0.2.6.{duration}; 224 @since(version = 0.2.0) 225 use network.{network, error-code, ip-socket-address, ip-address-family}; 226 227 @since(version = 0.2.0) 228 enum shutdown-type { 229 /// Similar to `SHUT_RD` in POSIX. 230 receive, 231 /// Similar to `SHUT_WR` in POSIX. 232 send, 233 /// Similar to `SHUT_RDWR` in POSIX. 234 both, 235 } 236 237 /// A TCP socket resource. 238 /// 239 /// The socket can be in one of the following states: 240 /// - `unbound` 241 /// - `bind-in-progress` 242 /// - `bound` (See note below) 243 /// - `listen-in-progress` 244 /// - `listening` 245 /// - `connect-in-progress` 246 /// - `connected` 247 /// - `closed` 248 /// See <https://github.com/WebAssembly/wasi-sockets/blob/main/TcpSocketOperationalSemantics.md> 249 /// for more information. 250 /// 251 /// Note: Except where explicitly mentioned, whenever this documentation uses 252 /// the term "bound" without backticks it actually means: in the `bound` state *or higher*. 253 /// (i.e. `bound`, `listen-in-progress`, `listening`, `connect-in-progress` or `connected`) 254 /// 255 /// In addition to the general error codes documented on the 256 /// `network::error-code` type, TCP socket methods may always return 257 /// `error(invalid-state)` when in the `closed` state. 258 @since(version = 0.2.0) 259 resource tcp-socket { 260 /// Bind the socket to a specific network on the provided IP address and port. 261 /// 262 /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which 263 /// network interface(s) to bind to. 264 /// If the TCP/UDP port is zero, the socket will be bound to a random free port. 265 /// 266 /// Bind can be attempted multiple times on the same socket, even with 267 /// different arguments on each iteration. But never concurrently and 268 /// only as long as the previous bind failed. Once a bind succeeds, the 269 /// binding can't be changed anymore. 270 /// 271 /// # Typical errors 272 /// - `invalid-argument`: The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows) 273 /// - `invalid-argument`: `local-address` is not a unicast address. (EINVAL) 274 /// - `invalid-argument`: `local-address` is an IPv4-mapped IPv6 address. (EINVAL) 275 /// - `invalid-state`: The socket is already bound. (EINVAL) 276 /// - `address-in-use`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) 277 /// - `address-in-use`: Address is already in use. (EADDRINUSE) 278 /// - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) 279 /// - `not-in-progress`: A `bind` operation is not in progress. 280 /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) 281 /// 282 /// # Implementors note 283 /// When binding to a non-zero port, this bind operation shouldn't be affected by the TIME_WAIT 284 /// state of a recently closed socket on the same local address. In practice this means that the SO_REUSEADDR 285 /// socket option should be set implicitly on all platforms, except on Windows where this is the default behavior 286 /// and SO_REUSEADDR performs something different entirely. 287 /// 288 /// Unlike in POSIX, in WASI the bind operation is async. This enables 289 /// interactive WASI hosts to inject permission prompts. Runtimes that 290 /// don't want to make use of this ability can simply call the native 291 /// `bind` as part of either `start-bind` or `finish-bind`. 292 /// 293 /// # References 294 /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/bind.html> 295 /// - <https://man7.org/linux/man-pages/man2/bind.2.html> 296 /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-bind> 297 /// - <https://man.freebsd.org/cgi/man.cgi?query=bind&sektion=2&format=html> 298 @since(version = 0.2.0) 299 start-bind: func(network: borrow<network>, local-address: ip-socket-address) -> result<_, error-code>; 300 @since(version = 0.2.0) 301 finish-bind: func() -> result<_, error-code>; 302 /// Connect to a remote endpoint. 303 /// 304 /// On success: 305 /// - the socket is transitioned into the `connected` state. 306 /// - a pair of streams is returned that can be used to read & write to the connection 307 /// 308 /// After a failed connection attempt, the socket will be in the `closed` 309 /// state and the only valid action left is to `drop` the socket. A single 310 /// socket can not be used to connect more than once. 311 /// 312 /// # Typical errors 313 /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) 314 /// - `invalid-argument`: `remote-address` is not a unicast address. (EINVAL, ENETUNREACH on Linux, EAFNOSUPPORT on MacOS) 315 /// - `invalid-argument`: `remote-address` is an IPv4-mapped IPv6 address. (EINVAL, EADDRNOTAVAIL on Illumos) 316 /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EADDRNOTAVAIL on Windows) 317 /// - `invalid-argument`: The port in `remote-address` is set to 0. (EADDRNOTAVAIL on Windows) 318 /// - `invalid-argument`: The socket is already attached to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. 319 /// - `invalid-state`: The socket is already in the `connected` state. (EISCONN) 320 /// - `invalid-state`: The socket is already in the `listening` state. (EOPNOTSUPP, EINVAL on Windows) 321 /// - `timeout`: Connection timed out. (ETIMEDOUT) 322 /// - `connection-refused`: The connection was forcefully rejected. (ECONNREFUSED) 323 /// - `connection-reset`: The connection was reset. (ECONNRESET) 324 /// - `connection-aborted`: The connection was aborted. (ECONNABORTED) 325 /// - `remote-unreachable`: The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) 326 /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) 327 /// - `not-in-progress`: A connect operation is not in progress. 328 /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) 329 /// 330 /// # Implementors note 331 /// The POSIX equivalent of `start-connect` is the regular `connect` syscall. 332 /// Because all WASI sockets are non-blocking this is expected to return 333 /// EINPROGRESS, which should be translated to `ok()` in WASI. 334 /// 335 /// The POSIX equivalent of `finish-connect` is a `poll` for event `POLLOUT` 336 /// with a timeout of 0 on the socket descriptor. Followed by a check for 337 /// the `SO_ERROR` socket option, in case the poll signaled readiness. 338 /// 339 /// # References 340 /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html> 341 /// - <https://man7.org/linux/man-pages/man2/connect.2.html> 342 /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-connect> 343 /// - <https://man.freebsd.org/cgi/man.cgi?connect> 344 @since(version = 0.2.0) 345 start-connect: func(network: borrow<network>, remote-address: ip-socket-address) -> result<_, error-code>; 346 @since(version = 0.2.0) 347 finish-connect: func() -> result<tuple<input-stream, output-stream>, error-code>; 348 /// Start listening for new connections. 349 /// 350 /// Transitions the socket into the `listening` state. 351 /// 352 /// Unlike POSIX, the socket must already be explicitly bound. 353 /// 354 /// # Typical errors 355 /// - `invalid-state`: The socket is not bound to any local address. (EDESTADDRREQ) 356 /// - `invalid-state`: The socket is already in the `connected` state. (EISCONN, EINVAL on BSD) 357 /// - `invalid-state`: The socket is already in the `listening` state. 358 /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE) 359 /// - `not-in-progress`: A listen operation is not in progress. 360 /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) 361 /// 362 /// # Implementors note 363 /// Unlike in POSIX, in WASI the listen operation is async. This enables 364 /// interactive WASI hosts to inject permission prompts. Runtimes that 365 /// don't want to make use of this ability can simply call the native 366 /// `listen` as part of either `start-listen` or `finish-listen`. 367 /// 368 /// # References 369 /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/listen.html> 370 /// - <https://man7.org/linux/man-pages/man2/listen.2.html> 371 /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-listen> 372 /// - <https://man.freebsd.org/cgi/man.cgi?query=listen&sektion=2> 373 @since(version = 0.2.0) 374 start-listen: func() -> result<_, error-code>; 375 @since(version = 0.2.0) 376 finish-listen: func() -> result<_, error-code>; 377 /// Accept a new client socket. 378 /// 379 /// The returned socket is bound and in the `connected` state. The following properties are inherited from the listener socket: 380 /// - `address-family` 381 /// - `keep-alive-enabled` 382 /// - `keep-alive-idle-time` 383 /// - `keep-alive-interval` 384 /// - `keep-alive-count` 385 /// - `hop-limit` 386 /// - `receive-buffer-size` 387 /// - `send-buffer-size` 388 /// 389 /// On success, this function returns the newly accepted client socket along with 390 /// a pair of streams that can be used to read & write to the connection. 391 /// 392 /// # Typical errors 393 /// - `invalid-state`: Socket is not in the `listening` state. (EINVAL) 394 /// - `would-block`: No pending connections at the moment. (EWOULDBLOCK, EAGAIN) 395 /// - `connection-aborted`: An incoming connection was pending, but was terminated by the client before this listener could accept it. (ECONNABORTED) 396 /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) 397 /// 398 /// # References 399 /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/accept.html> 400 /// - <https://man7.org/linux/man-pages/man2/accept.2.html> 401 /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-accept> 402 /// - <https://man.freebsd.org/cgi/man.cgi?query=accept&sektion=2> 403 @since(version = 0.2.0) 404 accept: func() -> result<tuple<tcp-socket, input-stream, output-stream>, error-code>; 405 /// Get the bound local address. 406 /// 407 /// POSIX mentions: 408 /// > If the socket has not been bound to a local name, the value 409 /// > stored in the object pointed to by `address` is unspecified. 410 /// 411 /// WASI is stricter and requires `local-address` to return `invalid-state` when the socket hasn't been bound yet. 412 /// 413 /// # Typical errors 414 /// - `invalid-state`: The socket is not bound to any local address. 415 /// 416 /// # References 417 /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockname.html> 418 /// - <https://man7.org/linux/man-pages/man2/getsockname.2.html> 419 /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-getsockname> 420 /// - <https://man.freebsd.org/cgi/man.cgi?getsockname> 421 @since(version = 0.2.0) 422 local-address: func() -> result<ip-socket-address, error-code>; 423 /// Get the remote address. 424 /// 425 /// # Typical errors 426 /// - `invalid-state`: The socket is not connected to a remote address. (ENOTCONN) 427 /// 428 /// # References 429 /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpeername.html> 430 /// - <https://man7.org/linux/man-pages/man2/getpeername.2.html> 431 /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-getpeername> 432 /// - <https://man.freebsd.org/cgi/man.cgi?query=getpeername&sektion=2&n=1> 433 @since(version = 0.2.0) 434 remote-address: func() -> result<ip-socket-address, error-code>; 435 /// Whether the socket is in the `listening` state. 436 /// 437 /// Equivalent to the SO_ACCEPTCONN socket option. 438 @since(version = 0.2.0) 439 is-listening: func() -> bool; 440 /// Whether this is a IPv4 or IPv6 socket. 441 /// 442 /// Equivalent to the SO_DOMAIN socket option. 443 @since(version = 0.2.0) 444 address-family: func() -> ip-address-family; 445 /// Hints the desired listen queue size. Implementations are free to ignore this. 446 /// 447 /// If the provided value is 0, an `invalid-argument` error is returned. 448 /// Any other value will never cause an error, but it might be silently clamped and/or rounded. 449 /// 450 /// # Typical errors 451 /// - `not-supported`: (set) The platform does not support changing the backlog size after the initial listen. 452 /// - `invalid-argument`: (set) The provided value was 0. 453 /// - `invalid-state`: (set) The socket is in the `connect-in-progress` or `connected` state. 454 @since(version = 0.2.0) 455 set-listen-backlog-size: func(value: u64) -> result<_, error-code>; 456 /// Enables or disables keepalive. 457 /// 458 /// The keepalive behavior can be adjusted using: 459 /// - `keep-alive-idle-time` 460 /// - `keep-alive-interval` 461 /// - `keep-alive-count` 462 /// These properties can be configured while `keep-alive-enabled` is false, but only come into effect when `keep-alive-enabled` is true. 463 /// 464 /// Equivalent to the SO_KEEPALIVE socket option. 465 @since(version = 0.2.0) 466 keep-alive-enabled: func() -> result<bool, error-code>; 467 @since(version = 0.2.0) 468 set-keep-alive-enabled: func(value: bool) -> result<_, error-code>; 469 /// Amount of time the connection has to be idle before TCP starts sending keepalive packets. 470 /// 471 /// If the provided value is 0, an `invalid-argument` error is returned. 472 /// Any other value will never cause an error, but it might be silently clamped and/or rounded. 473 /// I.e. after setting a value, reading the same setting back may return a different value. 474 /// 475 /// Equivalent to the TCP_KEEPIDLE socket option. (TCP_KEEPALIVE on MacOS) 476 /// 477 /// # Typical errors 478 /// - `invalid-argument`: (set) The provided value was 0. 479 @since(version = 0.2.0) 480 keep-alive-idle-time: func() -> result<duration, error-code>; 481 @since(version = 0.2.0) 482 set-keep-alive-idle-time: func(value: duration) -> result<_, error-code>; 483 /// The time between keepalive packets. 484 /// 485 /// If the provided value is 0, an `invalid-argument` error is returned. 486 /// Any other value will never cause an error, but it might be silently clamped and/or rounded. 487 /// I.e. after setting a value, reading the same setting back may return a different value. 488 /// 489 /// Equivalent to the TCP_KEEPINTVL socket option. 490 /// 491 /// # Typical errors 492 /// - `invalid-argument`: (set) The provided value was 0. 493 @since(version = 0.2.0) 494 keep-alive-interval: func() -> result<duration, error-code>; 495 @since(version = 0.2.0) 496 set-keep-alive-interval: func(value: duration) -> result<_, error-code>; 497 /// The maximum amount of keepalive packets TCP should send before aborting the connection. 498 /// 499 /// If the provided value is 0, an `invalid-argument` error is returned. 500 /// Any other value will never cause an error, but it might be silently clamped and/or rounded. 501 /// I.e. after setting a value, reading the same setting back may return a different value. 502 /// 503 /// Equivalent to the TCP_KEEPCNT socket option. 504 /// 505 /// # Typical errors 506 /// - `invalid-argument`: (set) The provided value was 0. 507 @since(version = 0.2.0) 508 keep-alive-count: func() -> result<u32, error-code>; 509 @since(version = 0.2.0) 510 set-keep-alive-count: func(value: u32) -> result<_, error-code>; 511 /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. 512 /// 513 /// If the provided value is 0, an `invalid-argument` error is returned. 514 /// 515 /// # Typical errors 516 /// - `invalid-argument`: (set) The TTL value must be 1 or higher. 517 @since(version = 0.2.0) 518 hop-limit: func() -> result<u8, error-code>; 519 @since(version = 0.2.0) 520 set-hop-limit: func(value: u8) -> result<_, error-code>; 521 /// The kernel buffer space reserved for sends/receives on this socket. 522 /// 523 /// If the provided value is 0, an `invalid-argument` error is returned. 524 /// Any other value will never cause an error, but it might be silently clamped and/or rounded. 525 /// I.e. after setting a value, reading the same setting back may return a different value. 526 /// 527 /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. 528 /// 529 /// # Typical errors 530 /// - `invalid-argument`: (set) The provided value was 0. 531 @since(version = 0.2.0) 532 receive-buffer-size: func() -> result<u64, error-code>; 533 @since(version = 0.2.0) 534 set-receive-buffer-size: func(value: u64) -> result<_, error-code>; 535 @since(version = 0.2.0) 536 send-buffer-size: func() -> result<u64, error-code>; 537 @since(version = 0.2.0) 538 set-send-buffer-size: func(value: u64) -> result<_, error-code>; 539 /// Create a `pollable` which can be used to poll for, or block on, 540 /// completion of any of the asynchronous operations of this socket. 541 /// 542 /// When `finish-bind`, `finish-listen`, `finish-connect` or `accept` 543 /// return `error(would-block)`, this pollable can be used to wait for 544 /// their success or failure, after which the method can be retried. 545 /// 546 /// The pollable is not limited to the async operation that happens to be 547 /// in progress at the time of calling `subscribe` (if any). Theoretically, 548 /// `subscribe` only has to be called once per socket and can then be 549 /// (re)used for the remainder of the socket's lifetime. 550 /// 551 /// See <https://github.com/WebAssembly/wasi-sockets/blob/main/TcpSocketOperationalSemantics.md#pollable-readiness> 552 /// for more information. 553 /// 554 /// Note: this function is here for WASI 0.2 only. 555 /// It's planned to be removed when `future` is natively supported in Preview3. 556 @since(version = 0.2.0) 557 subscribe: func() -> pollable; 558 /// Initiate a graceful shutdown. 559 /// 560 /// - `receive`: The socket is not expecting to receive any data from 561 /// the peer. The `input-stream` associated with this socket will be 562 /// closed. Any data still in the receive queue at time of calling 563 /// this method will be discarded. 564 /// - `send`: The socket has no more data to send to the peer. The `output-stream` 565 /// associated with this socket will be closed and a FIN packet will be sent. 566 /// - `both`: Same effect as `receive` & `send` combined. 567 /// 568 /// This function is idempotent; shutting down a direction more than once 569 /// has no effect and returns `ok`. 570 /// 571 /// The shutdown function does not close (drop) the socket. 572 /// 573 /// # Typical errors 574 /// - `invalid-state`: The socket is not in the `connected` state. (ENOTCONN) 575 /// 576 /// # References 577 /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/shutdown.html> 578 /// - <https://man7.org/linux/man-pages/man2/shutdown.2.html> 579 /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-shutdown> 580 /// - <https://man.freebsd.org/cgi/man.cgi?query=shutdown&sektion=2> 581 @since(version = 0.2.0) 582 shutdown: func(shutdown-type: shutdown-type) -> result<_, error-code>; 583 } 584} 585 586@since(version = 0.2.0) 587interface tcp-create-socket { 588 @since(version = 0.2.0) 589 use network.{network, error-code, ip-address-family}; 590 @since(version = 0.2.0) 591 use tcp.{tcp-socket}; 592 593 /// Create a new TCP socket. 594 /// 595 /// Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` in POSIX. 596 /// On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise. 597 /// 598 /// This function does not require a network capability handle. This is considered to be safe because 599 /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`connect` 600 /// is called, the socket is effectively an in-memory configuration object, unable to communicate with the outside world. 601 /// 602 /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. 603 /// 604 /// # Typical errors 605 /// - `not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) 606 /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) 607 /// 608 /// # References 609 /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/socket.html> 610 /// - <https://man7.org/linux/man-pages/man2/socket.2.html> 611 /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketw> 612 /// - <https://man.freebsd.org/cgi/man.cgi?query=socket&sektion=2> 613 @since(version = 0.2.0) 614 create-tcp-socket: func(address-family: ip-address-family) -> result<tcp-socket, error-code>; 615} 616 617@since(version = 0.2.0) 618interface udp { 619 @since(version = 0.2.0) 620 use wasi:io/poll@0.2.6.{pollable}; 621 @since(version = 0.2.0) 622 use network.{network, error-code, ip-socket-address, ip-address-family}; 623 624 /// A received datagram. 625 @since(version = 0.2.0) 626 record incoming-datagram { 627 /// The payload. 628 /// 629 /// Theoretical max size: ~64 KiB. In practice, typically less than 1500 bytes. 630 data: list<u8>, 631 /// The source address. 632 /// 633 /// This field is guaranteed to match the remote address the stream was initialized with, if any. 634 /// 635 /// Equivalent to the `src_addr` out parameter of `recvfrom`. 636 remote-address: ip-socket-address, 637 } 638 639 /// A datagram to be sent out. 640 @since(version = 0.2.0) 641 record outgoing-datagram { 642 /// The payload. 643 data: list<u8>, 644 /// The destination address. 645 /// 646 /// The requirements on this field depend on how the stream was initialized: 647 /// - with a remote address: this field must be None or match the stream's remote address exactly. 648 /// - without a remote address: this field is required. 649 /// 650 /// If this value is None, the send operation is equivalent to `send` in POSIX. Otherwise it is equivalent to `sendto`. 651 remote-address: option<ip-socket-address>, 652 } 653 654 /// A UDP socket handle. 655 @since(version = 0.2.0) 656 resource udp-socket { 657 /// Bind the socket to a specific network on the provided IP address and port. 658 /// 659 /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which 660 /// network interface(s) to bind to. 661 /// If the port is zero, the socket will be bound to a random free port. 662 /// 663 /// # Typical errors 664 /// - `invalid-argument`: The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows) 665 /// - `invalid-state`: The socket is already bound. (EINVAL) 666 /// - `address-in-use`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) 667 /// - `address-in-use`: Address is already in use. (EADDRINUSE) 668 /// - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) 669 /// - `not-in-progress`: A `bind` operation is not in progress. 670 /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) 671 /// 672 /// # Implementors note 673 /// Unlike in POSIX, in WASI the bind operation is async. This enables 674 /// interactive WASI hosts to inject permission prompts. Runtimes that 675 /// don't want to make use of this ability can simply call the native 676 /// `bind` as part of either `start-bind` or `finish-bind`. 677 /// 678 /// # References 679 /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/bind.html> 680 /// - <https://man7.org/linux/man-pages/man2/bind.2.html> 681 /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-bind> 682 /// - <https://man.freebsd.org/cgi/man.cgi?query=bind&sektion=2&format=html> 683 @since(version = 0.2.0) 684 start-bind: func(network: borrow<network>, local-address: ip-socket-address) -> result<_, error-code>; 685 @since(version = 0.2.0) 686 finish-bind: func() -> result<_, error-code>; 687 /// Set up inbound & outbound communication channels, optionally to a specific peer. 688 /// 689 /// This function only changes the local socket configuration and does not generate any network traffic. 690 /// On success, the `remote-address` of the socket is updated. The `local-address` may be updated as well, 691 /// based on the best network path to `remote-address`. 692 /// 693 /// When a `remote-address` is provided, the returned streams are limited to communicating with that specific peer: 694 /// - `send` can only be used to send to this destination. 695 /// - `receive` will only return datagrams sent from the provided `remote-address`. 696 /// 697 /// This method may be called multiple times on the same socket to change its association, but 698 /// only the most recently returned pair of streams will be operational. Implementations may trap if 699 /// the streams returned by a previous invocation haven't been dropped yet before calling `stream` again. 700 /// 701 /// The POSIX equivalent in pseudo-code is: 702 /// ```text 703 /// if (was previously connected) { 704 /// connect(s, AF_UNSPEC) 705 /// } 706 /// if (remote_address is Some) { 707 /// connect(s, remote_address) 708 /// } 709 /// ``` 710 /// 711 /// Unlike in POSIX, the socket must already be explicitly bound. 712 /// 713 /// # Typical errors 714 /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) 715 /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) 716 /// - `invalid-argument`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) 717 /// - `invalid-state`: The socket is not bound. 718 /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) 719 /// - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) 720 /// - `connection-refused`: The connection was refused. (ECONNREFUSED) 721 /// 722 /// # References 723 /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html> 724 /// - <https://man7.org/linux/man-pages/man2/connect.2.html> 725 /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-connect> 726 /// - <https://man.freebsd.org/cgi/man.cgi?connect> 727 @since(version = 0.2.0) 728 %stream: func(remote-address: option<ip-socket-address>) -> result<tuple<incoming-datagram-stream, outgoing-datagram-stream>, error-code>; 729 /// Get the current bound address. 730 /// 731 /// POSIX mentions: 732 /// > If the socket has not been bound to a local name, the value 733 /// > stored in the object pointed to by `address` is unspecified. 734 /// 735 /// WASI is stricter and requires `local-address` to return `invalid-state` when the socket hasn't been bound yet. 736 /// 737 /// # Typical errors 738 /// - `invalid-state`: The socket is not bound to any local address. 739 /// 740 /// # References 741 /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockname.html> 742 /// - <https://man7.org/linux/man-pages/man2/getsockname.2.html> 743 /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-getsockname> 744 /// - <https://man.freebsd.org/cgi/man.cgi?getsockname> 745 @since(version = 0.2.0) 746 local-address: func() -> result<ip-socket-address, error-code>; 747 /// Get the address the socket is currently streaming to. 748 /// 749 /// # Typical errors 750 /// - `invalid-state`: The socket is not streaming to a specific remote address. (ENOTCONN) 751 /// 752 /// # References 753 /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpeername.html> 754 /// - <https://man7.org/linux/man-pages/man2/getpeername.2.html> 755 /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-getpeername> 756 /// - <https://man.freebsd.org/cgi/man.cgi?query=getpeername&sektion=2&n=1> 757 @since(version = 0.2.0) 758 remote-address: func() -> result<ip-socket-address, error-code>; 759 /// Whether this is a IPv4 or IPv6 socket. 760 /// 761 /// Equivalent to the SO_DOMAIN socket option. 762 @since(version = 0.2.0) 763 address-family: func() -> ip-address-family; 764 /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. 765 /// 766 /// If the provided value is 0, an `invalid-argument` error is returned. 767 /// 768 /// # Typical errors 769 /// - `invalid-argument`: (set) The TTL value must be 1 or higher. 770 @since(version = 0.2.0) 771 unicast-hop-limit: func() -> result<u8, error-code>; 772 @since(version = 0.2.0) 773 set-unicast-hop-limit: func(value: u8) -> result<_, error-code>; 774 /// The kernel buffer space reserved for sends/receives on this socket. 775 /// 776 /// If the provided value is 0, an `invalid-argument` error is returned. 777 /// Any other value will never cause an error, but it might be silently clamped and/or rounded. 778 /// I.e. after setting a value, reading the same setting back may return a different value. 779 /// 780 /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. 781 /// 782 /// # Typical errors 783 /// - `invalid-argument`: (set) The provided value was 0. 784 @since(version = 0.2.0) 785 receive-buffer-size: func() -> result<u64, error-code>; 786 @since(version = 0.2.0) 787 set-receive-buffer-size: func(value: u64) -> result<_, error-code>; 788 @since(version = 0.2.0) 789 send-buffer-size: func() -> result<u64, error-code>; 790 @since(version = 0.2.0) 791 set-send-buffer-size: func(value: u64) -> result<_, error-code>; 792 /// Create a `pollable` which will resolve once the socket is ready for I/O. 793 /// 794 /// Note: this function is here for WASI 0.2 only. 795 /// It's planned to be removed when `future` is natively supported in Preview3. 796 @since(version = 0.2.0) 797 subscribe: func() -> pollable; 798 } 799 800 @since(version = 0.2.0) 801 resource incoming-datagram-stream { 802 /// Receive messages on the socket. 803 /// 804 /// This function attempts to receive up to `max-results` datagrams on the socket without blocking. 805 /// The returned list may contain fewer elements than requested, but never more. 806 /// 807 /// This function returns successfully with an empty list when either: 808 /// - `max-results` is 0, or: 809 /// - `max-results` is greater than 0, but no results are immediately available. 810 /// This function never returns `error(would-block)`. 811 /// 812 /// # Typical errors 813 /// - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) 814 /// - `connection-refused`: The connection was refused. (ECONNREFUSED) 815 /// 816 /// # References 817 /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvfrom.html> 818 /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvmsg.html> 819 /// - <https://man7.org/linux/man-pages/man2/recv.2.html> 820 /// - <https://man7.org/linux/man-pages/man2/recvmmsg.2.html> 821 /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-recv> 822 /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-recvfrom> 823 /// - <https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms741687(v=vs.85)> 824 /// - <https://man.freebsd.org/cgi/man.cgi?query=recv&sektion=2> 825 @since(version = 0.2.0) 826 receive: func(max-results: u64) -> result<list<incoming-datagram>, error-code>; 827 /// Create a `pollable` which will resolve once the stream is ready to receive again. 828 /// 829 /// Note: this function is here for WASI 0.2 only. 830 /// It's planned to be removed when `future` is natively supported in Preview3. 831 @since(version = 0.2.0) 832 subscribe: func() -> pollable; 833 } 834 835 @since(version = 0.2.0) 836 resource outgoing-datagram-stream { 837 /// Check readiness for sending. This function never blocks. 838 /// 839 /// Returns the number of datagrams permitted for the next call to `send`, 840 /// or an error. Calling `send` with more datagrams than this function has 841 /// permitted will trap. 842 /// 843 /// When this function returns ok(0), the `subscribe` pollable will 844 /// become ready when this function will report at least ok(1), or an 845 /// error. 846 /// 847 /// Never returns `would-block`. 848 check-send: func() -> result<u64, error-code>; 849 /// Send messages on the socket. 850 /// 851 /// This function attempts to send all provided `datagrams` on the socket without blocking and 852 /// returns how many messages were actually sent (or queued for sending). This function never 853 /// returns `error(would-block)`. If none of the datagrams were able to be sent, `ok(0)` is returned. 854 /// 855 /// This function semantically behaves the same as iterating the `datagrams` list and sequentially 856 /// sending each individual datagram until either the end of the list has been reached or the first error occurred. 857 /// If at least one datagram has been sent successfully, this function never returns an error. 858 /// 859 /// If the input list is empty, the function returns `ok(0)`. 860 /// 861 /// Each call to `send` must be permitted by a preceding `check-send`. Implementations must trap if 862 /// either `check-send` was not called or `datagrams` contains more items than `check-send` permitted. 863 /// 864 /// # Typical errors 865 /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) 866 /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) 867 /// - `invalid-argument`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) 868 /// - `invalid-argument`: The socket is in "connected" mode and `remote-address` is `some` value that does not match the address passed to `stream`. (EISCONN) 869 /// - `invalid-argument`: The socket is not "connected" and no value for `remote-address` was provided. (EDESTADDRREQ) 870 /// - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) 871 /// - `connection-refused`: The connection was refused. (ECONNREFUSED) 872 /// - `datagram-too-large`: The datagram is too large. (EMSGSIZE) 873 /// 874 /// # References 875 /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/sendto.html> 876 /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/sendmsg.html> 877 /// - <https://man7.org/linux/man-pages/man2/send.2.html> 878 /// - <https://man7.org/linux/man-pages/man2/sendmmsg.2.html> 879 /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-send> 880 /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-sendto> 881 /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasendmsg> 882 /// - <https://man.freebsd.org/cgi/man.cgi?query=send&sektion=2> 883 @since(version = 0.2.0) 884 send: func(datagrams: list<outgoing-datagram>) -> result<u64, error-code>; 885 /// Create a `pollable` which will resolve once the stream is ready to send again. 886 /// 887 /// Note: this function is here for WASI 0.2 only. 888 /// It's planned to be removed when `future` is natively supported in Preview3. 889 @since(version = 0.2.0) 890 subscribe: func() -> pollable; 891 } 892} 893 894@since(version = 0.2.0) 895interface udp-create-socket { 896 @since(version = 0.2.0) 897 use network.{network, error-code, ip-address-family}; 898 @since(version = 0.2.0) 899 use udp.{udp-socket}; 900 901 /// Create a new UDP socket. 902 /// 903 /// Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` in POSIX. 904 /// On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise. 905 /// 906 /// This function does not require a network capability handle. This is considered to be safe because 907 /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind` is called, 908 /// the socket is effectively an in-memory configuration object, unable to communicate with the outside world. 909 /// 910 /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. 911 /// 912 /// # Typical errors 913 /// - `not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) 914 /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) 915 /// 916 /// # References: 917 /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/socket.html> 918 /// - <https://man7.org/linux/man-pages/man2/socket.2.html> 919 /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketw> 920 /// - <https://man.freebsd.org/cgi/man.cgi?query=socket&sektion=2> 921 @since(version = 0.2.0) 922 create-udp-socket: func(address-family: ip-address-family) -> result<udp-socket, error-code>; 923} 924 925@since(version = 0.2.0) 926world imports { 927 @since(version = 0.2.0) 928 import wasi:io/error@0.2.6; 929 @since(version = 0.2.0) 930 import network; 931 @since(version = 0.2.0) 932 import instance-network; 933 @since(version = 0.2.0) 934 import wasi:io/poll@0.2.6; 935 @since(version = 0.2.0) 936 import udp; 937 @since(version = 0.2.0) 938 import udp-create-socket; 939 @since(version = 0.2.0) 940 import wasi:io/streams@0.2.6; 941 @since(version = 0.2.0) 942 import wasi:clocks/monotonic-clock@0.2.6; 943 @since(version = 0.2.0) 944 import tcp; 945 @since(version = 0.2.0) 946 import tcp-create-socket; 947 @since(version = 0.2.0) 948 import ip-name-lookup; 949} 950