Repository: bytecodealliance/wasmtime
Pull request #7785 (2024-01-17 21:26:34)

release 17.0.0 branch: backport #7781
PR #7781 updated the WASI Preview 2 release candidate versions for sockets, cli, and http to `0.2.0-rc-2024-01-16`. The purpose of this update was to remove undesirable behavior and methods related to ipv4-mapped-ipv6 from wasi-sockets. This PR backports those changes to the 17.0.0 release branch via a trivial cherry-pick.

<!--
Please make sure you include the following information:

- If this work has been discussed elsewhere, please include a link to that
  conversation. If it was discussed in an issue, just mention "issue #...".

- Explain why this change is needed. If the details are in an issue already,
  this can be brief.

Our development process is documented in the Wasmtime book:
https://docs.wasmtime.dev/contributing-development-process.html

Please ensure all communication follows the code of conduct:
https://github.com/bytecodealliance/wasmtime/blob/main/CODE_OF_CONDUCT.md
-->



Patch:
diff --git a/crates/wasi-http/src/lib.rs b/crates/wasi-http/src/lib.rs
index acda4e7e9..d331eb56b 100644
--- a/crates/wasi-http/src/lib.rs
+++ b/crates/wasi-http/src/lib.rs
@@ -11,9 +11,9 @@ pub mod bindings {
     wasmtime::component::bindgen!({
         path: "wit",
         interfaces: "
-            import wasi:http/incoming-handler@0.2.0-rc-2023-12-05;
-            import wasi:http/outgoing-handler@0.2.0-rc-2023-12-05;
-            import wasi:http/types@0.2.0-rc-2023-12-05;
+            import wasi:http/incoming-handler@0.2.0-rc-2024-01-16;
+            import wasi:http/outgoing-handler@0.2.0-rc-2024-01-16;
+            import wasi:http/types@0.2.0-rc-2024-01-16;
         ",
         tracing: true,
         async: false,
diff --git a/crates/wasi-http/wit/command-extended.wit b/crates/wasi-http/wit/command-extended.wit
index 16f01bc23..2f1491ef9 100644
--- a/crates/wasi-http/wit/command-extended.wit
+++ b/crates/wasi-http/wit/command-extended.wit
@@ -1,6 +1,6 @@
 // All of the same imports and exports available in the wasi:cli/command world
 // with addition of HTTP proxy related imports:
 world command-extended {
-  include wasi:cli/command@0.2.0-rc-2023-12-05;
-  import wasi:http/outgoing-handler@0.2.0-rc-2023-12-05;
+  include wasi:cli/command@0.2.0-rc-2024-01-16;
+  import wasi:http/outgoing-handler@0.2.0-rc-2024-01-16;
 }
diff --git a/crates/wasi-http/wit/deps/cli/command.wit b/crates/wasi-http/wit/deps/cli/command.wit
index cc82ae5dc..a9889164f 100644
--- a/crates/wasi-http/wit/deps/cli/command.wit
+++ b/crates/wasi-http/wit/deps/cli/command.wit
@@ -1,4 +1,4 @@
-package wasi:cli@0.2.0-rc-2023-12-05;
+package wasi:cli@0.2.0-rc-2024-01-16;
 
 world command {
   include imports;
diff --git a/crates/wasi-http/wit/deps/cli/imports.wit b/crates/wasi-http/wit/deps/cli/imports.wit
index 9965ea35e..8ce1abec3 100644
--- a/crates/wasi-http/wit/deps/cli/imports.wit
+++ b/crates/wasi-http/wit/deps/cli/imports.wit
@@ -1,9 +1,9 @@
-package wasi:cli@0.2.0-rc-2023-12-05;
+package wasi:cli@0.2.0-rc-2024-01-16;
 
 world imports {
   include wasi:clocks/imports@0.2.0-rc-2023-11-10;
   include wasi:filesystem/imports@0.2.0-rc-2023-11-10;
-  include wasi:sockets/imports@0.2.0-rc-2023-11-10;
+  include wasi:sockets/imports@0.2.0-rc-2024-01-16;
   include wasi:random/imports@0.2.0-rc-2023-11-10;
   include wasi:io/imports@0.2.0-rc-2023-11-10;
 
diff --git a/crates/wasi-http/wit/deps/cli/terminal.wit b/crates/wasi-http/wit/deps/cli/terminal.wit
index 47495769b..38c724efc 100644
--- a/crates/wasi-http/wit/deps/cli/terminal.wit
+++ b/crates/wasi-http/wit/deps/cli/terminal.wit
@@ -1,19 +1,21 @@
+/// Terminal input.
+///
+/// In the future, this may include functions for disabling echoing,
+/// disabling input buffering so that keyboard events are sent through
+/// immediately, querying supported features, and so on.
 interface terminal-input {
     /// The input side of a terminal.
     resource terminal-input;
-
-    // In the future, this may include functions for disabling echoing,
-    // disabling input buffering so that keyboard events are sent through
-    // immediately, querying supported features, and so on.
 }
 
+/// Terminal output.
+///
+/// In the future, this may include functions for querying the terminal
+/// size, being notified of terminal size changes, querying supported
+/// features, and so on.
 interface terminal-output {
     /// The output side of a terminal.
     resource terminal-output;
-
-    // In the future, this may include functions for querying the terminal
-    // size, being notified of terminal size changes, querying supported
-    // features, and so on.
 }
 
 /// An interface providing an optional `terminal-input` for stdin as a
diff --git a/crates/wasi-http/wit/deps/http/proxy.wit b/crates/wasi-http/wit/deps/http/proxy.wit
index 0f466c93c..2b0ce4b8a 100644
--- a/crates/wasi-http/wit/deps/http/proxy.wit
+++ b/crates/wasi-http/wit/deps/http/proxy.wit
@@ -1,4 +1,4 @@
-package wasi:http@0.2.0-rc-2023-12-05;
+package wasi:http@0.2.0-rc-2024-01-16;
 
 /// The `wasi:http/proxy` world captures a widely-implementable intersection of
 /// hosts that includes HTTP forward and reverse proxies. Components targeting
@@ -11,14 +11,14 @@ world proxy {
 
   /// Proxies have standard output and error streams which are expected to
   /// terminate in a developer-facing console provided by the host.
-  import wasi:cli/stdout@0.2.0-rc-2023-12-05;
-  import wasi:cli/stderr@0.2.0-rc-2023-12-05;
+  import wasi:cli/stdout@0.2.0-rc-2024-01-16;
+  import wasi:cli/stderr@0.2.0-rc-2024-01-16;
 
   /// TODO: this is a temporary workaround until component tooling is able to
   /// gracefully handle the absence of stdin. Hosts must return an eof stream
   /// for this import, which is what wasi-libc + tooling will do automatically
   /// when this import is properly removed.
-  import wasi:cli/stdin@0.2.0-rc-2023-12-05;
+  import wasi:cli/stdin@0.2.0-rc-2024-01-16;
 
   /// This is the default handler to use when user code simply wants to make an
   /// HTTP request (e.g., via `fetch()`).
diff --git a/crates/wasi-http/wit/deps/sockets/network.wit b/crates/wasi-http/wit/deps/sockets/network.wit
index 6bb07cd6f..9cadf0650 100644
--- a/crates/wasi-http/wit/deps/sockets/network.wit
+++ b/crates/wasi-http/wit/deps/sockets/network.wit
@@ -18,8 +18,6 @@ interface network {
     ///
     /// See each individual API for what the POSIX equivalents are. They sometimes differ per API.
     enum error-code {
-        // ### GENERAL ERRORS ###
-
         /// Unknown error
         unknown,
 
@@ -64,9 +62,6 @@ interface network {
         would-block,
 
 
-
-        // ### TCP & UDP SOCKET ERRORS ###
-
         /// The operation is not valid in the socket's current state.
         invalid-state,
 
@@ -83,24 +78,21 @@ interface network {
         remote-unreachable,
 
 
-        // ### TCP SOCKET ERRORS ###
-
-        /// The connection was forcefully rejected
+        /// The TCP connection was forcefully rejected
         connection-refused,
 
-        /// The connection was reset.
+        /// The TCP connection was reset.
         connection-reset,
 
-        /// A connection was aborted.
+        /// A TCP connection was aborted.
         connection-aborted,
 
 
-        // ### UDP SOCKET ERRORS ###
+        /// The size of a datagram sent to a UDP socket exceeded the maximum
+        /// supported size.
         datagram-too-large,
 
 
-        // ### NAME LOOKUP ERRORS ###
-
         /// Name does not exist or has no suitable associated IP addresses.
         name-unresolvable,
 
@@ -128,15 +120,21 @@ interface network {
     }
 
     record ipv4-socket-address {
-        port: u16, // sin_port
-        address: ipv4-address, // sin_addr
+        /// sin_port
+        port: u16,
+        /// sin_addr
+        address: ipv4-address,
     }
 
     record ipv6-socket-address {
-        port: u16, // sin6_port
-        flow-info: u32, // sin6_flowinfo
-        address: ipv6-address, // sin6_addr
-        scope-id: u32, // sin6_scope_id
+        /// sin6_port
+        port: u16,
+        /// sin6_flowinfo
+        flow-info: u32,
+        /// sin6_addr
+        address: ipv6-address,
+        /// sin6_scope_id
+        scope-id: u32,
     }
 
     variant ip-socket-address {
diff --git a/crates/wasi-http/wit/deps/sockets/tcp-create-socket.wit b/crates/wasi-http/wit/deps/sockets/tcp-create-socket.wit
index 768a07c85..c7ddf1f22 100644
--- a/crates/wasi-http/wit/deps/sockets/tcp-create-socket.wit
+++ b/crates/wasi-http/wit/deps/sockets/tcp-create-socket.wit
@@ -6,9 +6,10 @@ interface tcp-create-socket {
     /// Create a new TCP socket.
     ///
     /// Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` in POSIX.
+    /// On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise.
     ///
     /// 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`
+    /// 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.
     ///
     /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations.
diff --git a/crates/wasi-http/wit/deps/sockets/tcp.wit b/crates/wasi-http/wit/deps/sockets/tcp.wit
index 976b272c0..e045a436a 100644
--- a/crates/wasi-http/wit/deps/sockets/tcp.wit
+++ b/crates/wasi-http/wit/deps/sockets/tcp.wit
@@ -25,15 +25,12 @@ interface tcp {
         /// 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.
-        ///
         /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts.
         ///
         /// # Typical `start` errors
         /// - `invalid-argument`:          The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows)
         /// - `invalid-argument`:          `local-address` is not a unicast address. (EINVAL)
-        /// - `invalid-argument`:          `local-address` is an IPv4-mapped IPv6 address, but the socket has `ipv6-only` enabled. (EINVAL)
+        /// - `invalid-argument`:          `local-address` is an IPv4-mapped IPv6 address. (EINVAL)
         /// - `invalid-state`:             The socket is already bound. (EINVAL)
         ///
         /// # Typical `finish` errors
@@ -45,8 +42,9 @@ interface tcp {
         /// 
         /// # Implementors note
         /// When binding to a non-zero port, this bind operation shouldn't be affected by the TIME_WAIT
-        /// state of a recently closed socket on the same local address (i.e. the SO_REUSEADDR socket
-        /// option should be set implicitly on platforms that require it).
+        /// state of a recently closed socket on the same local address. In practice this means that the SO_REUSEADDR 
+        /// socket option should be set implicitly on all platforms, except on Windows where this is the default behavior
+        /// and SO_REUSEADDR performs something different entirely.
         ///
         /// # References
         /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/bind.html>
@@ -62,20 +60,13 @@ interface tcp {
         /// - the socket is transitioned into the Connection state
         /// - a pair of streams is returned that can be used to read & write to the connection
         ///
-        /// POSIX mentions:
-        /// > If connect() fails, the state of the socket is unspecified. Conforming applications should
-        /// > close the file descriptor and create a new socket before attempting to reconnect.
-        ///
-        /// WASI prescribes the following behavior:
-        /// - If `connect` fails because an input/state validation error, the socket should remain usable.
-        /// - If a connection was actually attempted but failed, the socket should become unusable for further network communication.
-        ///   Besides `drop`, any method after such a failure may return an error.
+        /// After a failed connection attempt, the only valid action left is to
+        /// `drop` the socket. A single socket can not be used to connect more than once.
         ///
         /// # Typical `start` errors
         /// - `invalid-argument`:          The `remote-address` has the wrong address family. (EAFNOSUPPORT)
         /// - `invalid-argument`:          `remote-address` is not a unicast address. (EINVAL, ENETUNREACH on Linux, EAFNOSUPPORT on MacOS)
-        /// - `invalid-argument`:          `remote-address` is an IPv4-mapped IPv6 address, but the socket has `ipv6-only` enabled. (EINVAL, EADDRNOTAVAIL on Illumos)
-        /// - `invalid-argument`:          `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa)
+        /// - `invalid-argument`:          `remote-address` is an IPv4-mapped IPv6 address. (EINVAL, EADDRNOTAVAIL on Illumos)
         /// - `invalid-argument`:          The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EADDRNOTAVAIL on Windows)
         /// - `invalid-argument`:          The port in `remote-address` is set to 0. (EADDRNOTAVAIL on Windows)
         /// - `invalid-argument`:          The socket is already attached to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`.
@@ -130,7 +121,6 @@ interface tcp {
         ///
         /// The returned socket is bound and in the Connection state. The following properties are inherited from the listener socket:
         /// - `address-family`
-        /// - `ipv6-only`
         /// - `keep-alive-enabled`
         /// - `keep-alive-idle-time`
         /// - `keep-alive-interval`
@@ -195,17 +185,6 @@ interface tcp {
         /// Equivalent to the SO_DOMAIN socket option.
         address-family: func() -> ip-address-family;
 
-        /// Whether IPv4 compatibility (dual-stack) mode is disabled or not.
-        ///
-        /// Equivalent to the IPV6_V6ONLY socket option.
-        ///
-        /// # Typical errors
-        /// - `invalid-state`:        (set) The socket is already bound.
-        /// - `not-supported`:        (get/set) `this` socket is an IPv4 socket.
-        /// - `not-supported`:        (set) Host does not support dual-stack sockets. (Implementations are not required to.)
-        ipv6-only: func() -> result<bool, error-code>;
-        set-ipv6-only: func(value: bool) -> result<_, error-code>;
-
         /// Hints the desired listen queue size. Implementations are free to ignore this.
         ///
         /// If the provided value is 0, an `invalid-argument` error is returned.
@@ -304,12 +283,16 @@ interface tcp {
 
         /// Initiate a graceful shutdown.
         ///
-        /// - 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.
+        /// - `receive`: The socket is not expecting to receive any data from
+        ///   the peer. The `input-stream` associated with this socket will be
+        ///   closed. Any data still in the receive queue at time of calling
+        ///   this method will be discarded.
+        /// - `send`: The socket has no more data to send to the peer. The `output-stream`
+        ///   associated with this socket will be closed and a FIN packet will be sent.
+        /// - `both`: Same effect as `receive` & `send` combined.
+        ///
+        /// This function is idempotent. Shutting a down a direction more than once
+        /// has no effect and returns `ok`.
         ///
         /// The shutdown function does not close (drop) the socket.
         ///
diff --git a/crates/wasi-http/wit/deps/sockets/udp-create-socket.wit b/crates/wasi-http/wit/deps/sockets/udp-create-socket.wit
index cc58234d8..0482d1fe7 100644
--- a/crates/wasi-http/wit/deps/sockets/udp-create-socket.wit
+++ b/crates/wasi-http/wit/deps/sockets/udp-create-socket.wit
@@ -6,6 +6,7 @@ interface udp-create-socket {
     /// Create a new UDP socket.
     ///
     /// Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` in POSIX.
+    /// On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise.
     ///
     /// 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` is called,
diff --git a/crates/wasi-http/wit/deps/sockets/udp.wit b/crates/wasi-http/wit/deps/sockets/udp.wit
index c8dafadfc..ae82b97fc 100644
--- a/crates/wasi-http/wit/deps/sockets/udp.wit
+++ b/crates/wasi-http/wit/deps/sockets/udp.wit
@@ -92,7 +92,6 @@ interface udp {
         /// 
         /// # Typical errors
         /// - `invalid-argument`:          The `remote-address` has the wrong address family. (EAFNOSUPPORT)
-        /// - `invalid-argument`:          `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa)
         /// - `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-state`:             The socket is not bound.
@@ -142,17 +141,6 @@ interface udp {
         /// Equivalent to the SO_DOMAIN socket option.
         address-family: func() -> ip-address-family;
 
-        /// Whether IPv4 compatibility (dual-stack) mode is disabled or not.
-        ///
-        /// Equivalent to the IPV6_V6ONLY socket option.
-        ///
-        /// # Typical errors
-        /// - `not-supported`:        (get/set) `this` socket is an IPv4 socket.
-        /// - `invalid-state`:        (set) The socket is already bound.
-        /// - `not-supported`:        (set) Host does not support dual-stack sockets. (Implementations are not required to.)
-        ipv6-only: func() -> result<bool, error-code>;
-        set-ipv6-only: func(value: bool) -> result<_, error-code>;
-
         /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options.
         ///
         /// If the provided value is 0, an `invalid-argument` error is returned.
@@ -248,7 +236,6 @@ interface udp {
         ///
         /// # Typical errors
         /// - `invalid-argument`:        The `remote-address` has the wrong address family. (EAFNOSUPPORT)
-        /// - `invalid-argument`:        `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa)
         /// - `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 `stream`. (EISCONN)
diff --git a/crates/wasi-http/wit/deps/sockets/world.wit b/crates/wasi-http/wit/deps/sockets/world.wit
index 49ad8d3d9..8588cc6c1 100644
--- a/crates/wasi-http/wit/deps/sockets/world.wit
+++ b/crates/wasi-http/wit/deps/sockets/world.wit
@@ -1,4 +1,4 @@
-package wasi:sockets@0.2.0-rc-2023-11-10;
+package wasi:sockets@0.2.0-rc-2024-01-16;
 
 world imports {
     import instance-network;
diff --git a/crates/wasi-preview1-component-adapter/src/lib.rs b/crates/wasi-preview1-component-adapter/src/lib.rs
index 8ef496d56..bd1d94e40 100644
--- a/crates/wasi-preview1-component-adapter/src/lib.rs
+++ b/crates/wasi-preview1-component-adapter/src/lib.rs
@@ -87,9 +87,9 @@ pub mod bindings {
                 import wasi:clocks/wall-clock@0.2.0-rc-2023-11-10;
                 import wasi:clocks/monotonic-clock@0.2.0-rc-2023-11-10;
                 import wasi:random/random@0.2.0-rc-2023-11-10;
-                import wasi:cli/stdout@0.2.0-rc-2023-12-05;
-                import wasi:cli/stderr@0.2.0-rc-2023-12-05;
-                import wasi:cli/stdin@0.2.0-rc-2023-12-05;
+                import wasi:cli/stdout@0.2.0-rc-2024-01-16;
+                import wasi:cli/stderr@0.2.0-rc-2024-01-16;
+                import wasi:cli/stdin@0.2.0-rc-2024-01-16;
             }
         "#,
         std_feature,
@@ -98,7 +98,7 @@ pub mod bindings {
     });
 }
 
-#[export_name = "wasi:cli/run@0.2.0-rc-2023-12-05#run"]
+#[export_name = "wasi:cli/run@0.2.0-rc-2024-01-16#run"]
 #[cfg(feature = "command")]
 pub unsafe extern "C" fn run() -> u32 {
     #[link(wasm_import_module = "__main_module__")]
@@ -2665,7 +2665,7 @@ impl State {
     #[cfg(not(feature = "proxy"))]
     fn get_environment(&self) -> &[StrTuple] {
         if self.env_vars.get().is_none() {
-            #[link(wasm_import_module = "wasi:cli/environment@0.2.0-rc-2023-12-05")]
+            #[link(wasm_import_module = "wasi:cli/environment@0.2.0-rc-2024-01-16")]
             extern "C" {
                 #[link_name = "get-environment"]
                 fn get_environment_import(rval: *mut StrTupleList);
@@ -2690,7 +2690,7 @@ impl State {
     #[cfg(not(feature = "proxy"))]
     fn get_args(&self) -> &[WasmStr] {
         if self.args.get().is_none() {
-            #[link(wasm_import_module = "wasi:cli/environment@0.2.0-rc-2023-12-05")]
+            #[link(wasm_import_module = "wasi:cli/environment@0.2.0-rc-2024-01-16")]
             extern "C" {
                 #[link_name = "get-arguments"]
                 fn get_args_import(rval: *mut WasmStrList);
diff --git a/crates/wasi/src/preview2/host/network.rs b/crates/wasi/src/preview2/host/network.rs
index 63a09e293..31ef2cb61 100644
--- a/crates/wasi/src/preview2/host/network.rs
+++ b/crates/wasi/src/preview2/host/network.rs
@@ -261,14 +261,14 @@ pub(crate) mod util {
     ) -> SocketResult<()> {
         match (socket_family, addr.ip()) {
             (SocketAddressFamily::Ipv4, IpAddr::V4(_)) => Ok(()),
-            (SocketAddressFamily::Ipv6 { v6only }, IpAddr::V6(ipv6)) => {
+            (SocketAddressFamily::Ipv6, IpAddr::V6(ipv6)) => {
                 if is_deprecated_ipv4_compatible(&ipv6) {
                     // Reject IPv4-*compatible* IPv6 addresses. They have been deprecated
                     // since 2006, OS handling of them is inconsistent and our own
                     // validations don't take them into account either.
                     // Note that these are not the same as IPv4-*mapped* IPv6 addresses.
                     Err(ErrorCode::InvalidArgument.into())
-                } else if *v6only && ipv6.to_ipv4_mapped().is_some() {
+                } else if ipv6.to_ipv4_mapped().is_some() {
                     Err(ErrorCode::InvalidArgument.into())
                 } else {
                     Ok(())
diff --git a/crates/wasi/src/preview2/host/tcp.rs b/crates/wasi/src/preview2/host/tcp.rs
index e9587af70..67bd04429 100644
--- a/crates/wasi/src/preview2/host/tcp.rs
+++ b/crates/wasi/src/preview2/host/tcp.rs
@@ -270,8 +270,7 @@ impl<T: WasiView> crate::preview2::host::tcp::tcp::HostTcpSocket for T {
             }
 
             // For some reason, IP_TTL is inherited, but IPV6_UNICAST_HOPS isn't.
-            if let (SocketAddressFamily::Ipv6 { .. }, Some(ttl)) = (socket.family, socket.hop_limit)
-            {
+            if let (SocketAddressFamily::Ipv6, Some(ttl)) = (socket.family, socket.hop_limit) {
                 _ = util::set_ipv6_unicast_hops(&client_fd, ttl); // Ignore potential error.
             }
 
@@ -350,38 +349,7 @@ impl<T: WasiView> crate::preview2::host::tcp::tcp::HostTcpSocket for T {
 
         match socket.family {
             SocketAddressFamily::Ipv4 => Ok(IpAddressFamily::Ipv4),
-            SocketAddressFamily::Ipv6 { .. } => Ok(IpAddressFamily::Ipv6),
-        }
-    }
-
-    fn ipv6_only(&mut self, this: Resource<tcp::TcpSocket>) -> SocketResult<bool> {
-        let table = self.table();
-        let socket = table.get(&this)?;
-
-        // Instead of just calling the OS we return our own internal state, because
-        // MacOS doesn't propagate the V6ONLY state on to accepted client sockets.
-
-        match socket.family {
-            SocketAddressFamily::Ipv4 => Err(ErrorCode::NotSupported.into()),
-            SocketAddressFamily::Ipv6 { v6only } => Ok(v6only),
-        }
-    }
-
-    fn set_ipv6_only(&mut self, this: Resource<tcp::TcpSocket>, value: bool) -> SocketResult<()> {
-        let table = self.table_mut();
-        let socket = table.get_mut(&this)?;
-
-        match socket.family {
-            SocketAddressFamily::Ipv4 => Err(ErrorCode::NotSupported.into()),
-            SocketAddressFamily::Ipv6 { .. } => match socket.tcp_state {
-                TcpState::Default => {
-                    sockopt::set_ipv6_v6only(socket.tcp_socket(), value)?;
-                    socket.family = SocketAddressFamily::Ipv6 { v6only: value };
-                    Ok(())
-                }
-                TcpState::BindStarted => Err(ErrorCode::ConcurrencyConflict.into()),
-                _ => Err(ErrorCode::InvalidState.into()),
-            },
+            SocketAddressFamily::Ipv6 => Ok(IpAddressFamily::Ipv6),
         }
     }
 
@@ -514,7 +482,7 @@ impl<T: WasiView> crate::preview2::host::tcp::tcp::HostTcpSocket for T {
 
         let ttl = match socket.family {
             SocketAddressFamily::Ipv4 => util::get_ip_ttl(socket.tcp_socket())?,
-            SocketAddressFamily::Ipv6 { .. } => util::get_ipv6_unicast_hops(socket.tcp_socket())?,
+            SocketAddressFamily::Ipv6 => util::get_ipv6_unicast_hops(socket.tcp_socket())?,
         };
 
         Ok(ttl)
@@ -526,9 +494,7 @@ impl<T: WasiView> crate::preview2::host::tcp::tcp::HostTcpSocket for T {
 
         match socket.family {
             SocketAddressFamily::Ipv4 => util::set_ip_ttl(socket.tcp_socket(), value)?,
-            SocketAddressFamily::Ipv6 { .. } => {
-                util::set_ipv6_unicast_hops(socket.tcp_socket(), value)?
-            }
+            SocketAddressFamily::Ipv6 => util::set_ipv6_unicast_hops(socket.tcp_socket(), value)?,
         }
 
         #[cfg(target_os = "macos")]
diff --git a/crates/wasi/src/preview2/host/udp.rs b/crates/wasi/src/preview2/host/udp.rs
index b256fd861..8b96a686a 100644
--- a/crates/wasi/src/preview2/host/udp.rs
+++ b/crates/wasi/src/preview2/host/udp.rs
@@ -13,7 +13,6 @@ use anyhow::anyhow;
 use async_trait::async_trait;
 use io_lifetimes::AsSocketlike;
 use rustix::io::Errno;
-use rustix::net::sockopt;
 use std::net::SocketAddr;
 use tokio::io::Interest;
 use wasmtime::component::Resource;
@@ -209,35 +208,7 @@ impl<T: WasiView> udp::HostUdpSocket for T {
 
         match socket.family {
             SocketAddressFamily::Ipv4 => Ok(IpAddressFamily::Ipv4),
-            SocketAddressFamily::Ipv6 { .. } => Ok(IpAddressFamily::Ipv6),
-        }
-    }
-
-    fn ipv6_only(&mut self, this: Resource<udp::UdpSocket>) -> SocketResult<bool> {
-        let table = self.table();
-        let socket = table.get(&this)?;
-
-        match socket.family {
-            SocketAddressFamily::Ipv4 => Err(ErrorCode::NotSupported.into()),
-            SocketAddressFamily::Ipv6 { v6only } => Ok(v6only),
-        }
-    }
-
-    fn set_ipv6_only(&mut self, this: Resource<udp::UdpSocket>, value: bool) -> SocketResult<()> {
-        let table = self.table_mut();
-        let socket = table.get_mut(&this)?;
-
-        match socket.family {
-            SocketAddressFamily::Ipv4 => Err(ErrorCode::NotSupported.into()),
-            SocketAddressFamily::Ipv6 { .. } => match socket.udp_state {
-                UdpState::Default => {
-                    sockopt::set_ipv6_v6only(socket.udp_socket(), value)?;
-                    socket.family = SocketAddressFamily::Ipv6 { v6only: value };
-                    Ok(())
-                }
-                UdpState::BindStarted => Err(ErrorCode::ConcurrencyConflict.into()),
-                _ => Err(ErrorCode::InvalidState.into()),
-            },
+            SocketAddressFamily::Ipv6 => Ok(IpAddressFamily::Ipv6),
         }
     }
 
@@ -247,7 +218,7 @@ impl<T: WasiView> udp::HostUdpSocket for T {
 
         let ttl = match socket.family {
             SocketAddressFamily::Ipv4 => util::get_ip_ttl(socket.udp_socket())?,
-            SocketAddressFamily::Ipv6 { .. } => util::get_ipv6_unicast_hops(socket.udp_socket())?,
+            SocketAddressFamily::Ipv6 => util::get_ipv6_unicast_hops(socket.udp_socket())?,
         };
 
         Ok(ttl)
@@ -263,9 +234,7 @@ impl<T: WasiView> udp::HostUdpSocket for T {
 
         match socket.family {
             SocketAddressFamily::Ipv4 => util::set_ip_ttl(socket.udp_socket(), value)?,
-            SocketAddressFamily::Ipv6 { .. } => {
-                util::set_ipv6_unicast_hops(socket.udp_socket(), value)?
-            }
+            SocketAddressFamily::Ipv6 => util::set_ipv6_unicast_hops(socket.udp_socket(), value)?,
         }
 
         Ok(())
diff --git a/crates/wasi/src/preview2/network.rs b/crates/wasi/src/preview2/network.rs
index 0cd86cba8..82b0929bc 100644
--- a/crates/wasi/src/preview2/network.rs
+++ b/crates/wasi/src/preview2/network.rs
@@ -84,7 +84,7 @@ impl From<rustix::io::Errno> for SocketError {
 #[derive(Copy, Clone)]
 pub enum SocketAddressFamily {
     Ipv4,
-    Ipv6 { v6only: bool },
+    Ipv6,
 }
 
 pub(crate) fn to_ipv4_addr(addr: Ipv4Address) -> std::net::Ipv4Addr {
diff --git a/crates/wasi/src/preview2/tcp.rs b/crates/wasi/src/preview2/tcp.rs
index fe803306f..fc766b167 100644
--- a/crates/wasi/src/preview2/tcp.rs
+++ b/crates/wasi/src/preview2/tcp.rs
@@ -265,9 +265,10 @@ impl TcpSocket {
 
         let socket_address_family = match family {
             AddressFamily::Ipv4 => SocketAddressFamily::Ipv4,
-            AddressFamily::Ipv6 => SocketAddressFamily::Ipv6 {
-                v6only: sockopt::get_ipv6_v6only(&fd)?,
-            },
+            AddressFamily::Ipv6 => {
+                sockopt::set_ipv6_v6only(&fd, true)?;
+                SocketAddressFamily::Ipv6
+            }
         };
 
         Self::from_fd(fd, socket_address_family)
diff --git a/crates/wasi/src/preview2/udp.rs b/crates/wasi/src/preview2/udp.rs
index 34d464024..e6a5269a8 100644
--- a/crates/wasi/src/preview2/udp.rs
+++ b/crates/wasi/src/preview2/udp.rs
@@ -64,9 +64,10 @@ impl UdpSocket {
 
         let socket_address_family = match family {
             AddressFamily::Ipv4 => SocketAddressFamily::Ipv4,
-            AddressFamily::Ipv6 => SocketAddressFamily::Ipv6 {
-                v6only: rustix::net::sockopt::get_ipv6_v6only(&fd)?,
-            },
+            AddressFamily::Ipv6 => {
+                rustix::net::sockopt::set_ipv6_v6only(&fd, true)?;
+                SocketAddressFamily::Ipv6
+            }
         };
 
         let socket = Self::setup_tokio_udp_socket(fd)?;
diff --git a/crates/wasi/wit/command-extended.wit b/crates/wasi/wit/command-extended.wit
index 16f01bc23..2f1491ef9 100644
--- a/crates/wasi/wit/command-extended.wit
+++ b/crates/wasi/wit/command-extended.wit
@@ -1,6 +1,6 @@
 // All of the same imports and exports available in the wasi:cli/command world
 // with addition of HTTP proxy related imports:
 world command-extended {
-  include wasi:cli/command@0.2.0-rc-2023-12-05;
-  import wasi:http/outgoing-handler@0.2.0-rc-2023-12-05;
+  include wasi:cli/command@0.2.0-rc-2024-01-16;
+  import wasi:http/outgoing-handler@0.2.0-rc-2024-01-16;
 }
diff --git a/crates/wasi/wit/deps/cli/command.wit b/crates/wasi/wit/deps/cli/command.wit
index cc82ae5dc..a9889164f 100644
--- a/crates/wasi/wit/deps/cli/command.wit
+++ b/crates/wasi/wit/deps/cli/command.wit
@@ -1,4 +1,4 @@
-package wasi:cli@0.2.0-rc-2023-12-05;
+package wasi:cli@0.2.0-rc-2024-01-16;
 
 world command {
   include imports;
diff --git a/crates/wasi/wit/deps/cli/imports.wit b/crates/wasi/wit/deps/cli/imports.wit
index 9965ea35e..8ce1abec3 100644
--- a/crates/wasi/wit/deps/cli/imports.wit
+++ b/crates/wasi/wit/deps/cli/imports.wit
@@ -1,9 +1,9 @@
-package wasi:cli@0.2.0-rc-2023-12-05;
+package wasi:cli@0.2.0-rc-2024-01-16;
 
 world imports {
   include wasi:clocks/imports@0.2.0-rc-2023-11-10;
   include wasi:filesystem/imports@0.2.0-rc-2023-11-10;
-  include wasi:sockets/imports@0.2.0-rc-2023-11-10;
+  include wasi:sockets/imports@0.2.0-rc-2024-01-16;
   include wasi:random/imports@0.2.0-rc-2023-11-10;
   include wasi:io/imports@0.2.0-rc-2023-11-10;
 
diff --git a/crates/wasi/wit/deps/cli/terminal.wit b/crates/wasi/wit/deps/cli/terminal.wit
index 47495769b..38c724efc 100644
--- a/crates/wasi/wit/deps/cli/terminal.wit
+++ b/crates/wasi/wit/deps/cli/terminal.wit
@@ -1,19 +1,21 @@
+/// Terminal input.
+///
+/// In the future, this may include functions for disabling echoing,
+/// disabling input buffering so that keyboard events are sent through
+/// immediately, querying supported features, and so on.
 interface terminal-input {
     /// The input side of a terminal.
     resource terminal-input;
-
-    // In the future, this may include functions for disabling echoing,
-    // disabling input buffering so that keyboard events are sent through
-    // immediately, querying supported features, and so on.
 }
 
+/// Terminal output.
+///
+/// In the future, this may include functions for querying the terminal
+/// size, being notified of terminal size changes, querying supported
+/// features, and so on.
 interface terminal-output {
     /// The output side of a terminal.
     resource terminal-output;
-
-    // In the future, this may include functions for querying the terminal
-    // size, being notified of terminal size changes, querying supported
-    // features, and so on.
 }
 
 /// An interface providing an optional `terminal-input` for stdin as a
diff --git a/crates/wasi/wit/deps/http/proxy.wit b/crates/wasi/wit/deps/http/proxy.wit
index 0f466c93c..2b0ce4b8a 100644
--- a/crates/wasi/wit/deps/http/proxy.wit
+++ b/crates/wasi/wit/deps/http/proxy.wit
@@ -1,4 +1,4 @@
-package wasi:http@0.2.0-rc-2023-12-05;
+package wasi:http@0.2.0-rc-2024-01-16;
 
 /// The `wasi:http/proxy` world captures a widely-implementable intersection of
 /// hosts that includes HTTP forward and reverse proxies. Components targeting
@@ -11,14 +11,14 @@ world proxy {
 
   /// Proxies have standard output and error streams which are expected to
   /// terminate in a developer-facing console provided by the host.
-  import wasi:cli/stdout@0.2.0-rc-2023-12-05;
-  import wasi:cli/stderr@0.2.0-rc-2023-12-05;
+  import wasi:cli/stdout@0.2.0-rc-2024-01-16;
+  import wasi:cli/stderr@0.2.0-rc-2024-01-16;
 
   /// TODO: this is a temporary workaround until component tooling is able to
   /// gracefully handle the absence of stdin. Hosts must return an eof stream
   /// for this import, which is what wasi-libc + tooling will do automatically
   /// when this import is properly removed.
-  import wasi:cli/stdin@0.2.0-rc-2023-12-05;
+  import wasi:cli/stdin@0.2.0-rc-2024-01-16;
 
   /// This is the default handler to use when user code simply wants to make an
   /// HTTP request (e.g., via `fetch()`).
diff --git a/crates/wasi/wit/deps/sockets/network.wit b/crates/wasi/wit/deps/sockets/network.wit
index 6bb07cd6f..9cadf0650 100644
--- a/crates/wasi/wit/deps/sockets/network.wit
+++ b/crates/wasi/wit/deps/sockets/network.wit
@@ -18,8 +18,6 @@ interface network {
     ///
     /// See each individual API for what the POSIX equivalents are. They sometimes differ per API.
     enum error-code {
-        // ### GENERAL ERRORS ###
-
         /// Unknown error
         unknown,
 
@@ -64,9 +62,6 @@ interface network {
         would-block,
 
 
-
-        // ### TCP & UDP SOCKET ERRORS ###
-
         /// The operation is not valid in the socket's current state.
         invalid-state,
 
@@ -83,24 +78,21 @@ interface network {
         remote-unreachable,
 
 
-        // ### TCP SOCKET ERRORS ###
-
-        /// The connection was forcefully rejected
+        /// The TCP connection was forcefully rejected
         connection-refused,
 
-        /// The connection was reset.
+        /// The TCP connection was reset.
         connection-reset,
 
-        /// A connection was aborted.
+        /// A TCP connection was aborted.
         connection-aborted,
 
 
-        // ### UDP SOCKET ERRORS ###
+        /// The size of a datagram sent to a UDP socket exceeded the maximum
+        /// supported size.
         datagram-too-large,
 
 
-        // ### NAME LOOKUP ERRORS ###
-
         /// Name does not exist or has no suitable associated IP addresses.
         name-unresolvable,
 
@@ -128,15 +120,21 @@ interface network {
     }
 
     record ipv4-socket-address {
-        port: u16, // sin_port
-        address: ipv4-address, // sin_addr
+        /// sin_port
+        port: u16,
+        /// sin_addr
+        address: ipv4-address,
     }
 
     record ipv6-socket-address {
-        port: u16, // sin6_port
-        flow-info: u32, // sin6_flowinfo
-        address: ipv6-address, // sin6_addr
-        scope-id: u32, // sin6_scope_id
+        /// sin6_port
+        port: u16,
+        /// sin6_flowinfo
+        flow-info: u32,
+        /// sin6_addr
+        address: ipv6-address,
+        /// sin6_scope_id
+        scope-id: u32,
     }
 
     variant ip-socket-address {
diff --git a/crates/wasi/wit/deps/sockets/tcp-create-socket.wit b/crates/wasi/wit/deps/sockets/tcp-create-socket.wit
index 768a07c85..c7ddf1f22 100644
--- a/crates/wasi/wit/deps/sockets/tcp-create-socket.wit
+++ b/crates/wasi/wit/deps/sockets/tcp-create-socket.wit
@@ -6,9 +6,10 @@ interface tcp-create-socket {
     /// Create a new TCP socket.
     ///
     /// Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` in POSIX.
+    /// On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise.
     ///
     /// 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`
+    /// 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.
     ///
     /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations.
diff --git a/crates/wasi/wit/deps/sockets/tcp.wit b/crates/wasi/wit/deps/sockets/tcp.wit
index 976b272c0..e045a436a 100644
--- a/crates/wasi/wit/deps/sockets/tcp.wit
+++ b/crates/wasi/wit/deps/sockets/tcp.wit
@@ -25,15 +25,12 @@ interface tcp {
         /// 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.
-        ///
         /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts.
         ///
         /// # Typical `start` errors
         /// - `invalid-argument`:          The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows)
         /// - `invalid-argument`:          `local-address` is not a unicast address. (EINVAL)
-        /// - `invalid-argument`:          `local-address` is an IPv4-mapped IPv6 address, but the socket has `ipv6-only` enabled. (EINVAL)
+        /// - `invalid-argument`:          `local-address` is an IPv4-mapped IPv6 address. (EINVAL)
         /// - `invalid-state`:             The socket is already bound. (EINVAL)
         ///
         /// # Typical `finish` errors
@@ -45,8 +42,9 @@ interface tcp {
         /// 
         /// # Implementors note
         /// When binding to a non-zero port, this bind operation shouldn't be affected by the TIME_WAIT
-        /// state of a recently closed socket on the same local address (i.e. the SO_REUSEADDR socket
-        /// option should be set implicitly on platforms that require it).
+        /// state of a recently closed socket on the same local address. In practice this means that the SO_REUSEADDR 
+        /// socket option should be set implicitly on all platforms, except on Windows where this is the default behavior
+        /// and SO_REUSEADDR performs something different entirely.
         ///
         /// # References
         /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/bind.html>
@@ -62,20 +60,13 @@ interface tcp {
         /// - the socket is transitioned into the Connection state
         /// - a pair of streams is returned that can be used to read & write to the connection
         ///
-        /// POSIX mentions:
-        /// > If connect() fails, the state of the socket is unspecified. Conforming applications should
-        /// > close the file descriptor and create a new socket before attempting to reconnect.
-        ///
-        /// WASI prescribes the following behavior:
-        /// - If `connect` fails because an input/state validation error, the socket should remain usable.
-        /// - If a connection was actually attempted but failed, the socket should become unusable for further network communication.
-        ///   Besides `drop`, any method after such a failure may return an error.
+        /// After a failed connection attempt, the only valid action left is to
+        /// `drop` the socket. A single socket can not be used to connect more than once.
         ///
         /// # Typical `start` errors
         /// - `invalid-argument`:          The `remote-address` has the wrong address family. (EAFNOSUPPORT)
         /// - `invalid-argument`:          `remote-address` is not a unicast address. (EINVAL, ENETUNREACH on Linux, EAFNOSUPPORT on MacOS)
-        /// - `invalid-argument`:          `remote-address` is an IPv4-mapped IPv6 address, but the socket has `ipv6-only` enabled. (EINVAL, EADDRNOTAVAIL on Illumos)
-        /// - `invalid-argument`:          `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa)
+        /// - `invalid-argument`:          `remote-address` is an IPv4-mapped IPv6 address. (EINVAL, EADDRNOTAVAIL on Illumos)
         /// - `invalid-argument`:          The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EADDRNOTAVAIL on Windows)
         /// - `invalid-argument`:          The port in `remote-address` is set to 0. (EADDRNOTAVAIL on Windows)
         /// - `invalid-argument`:          The socket is already attached to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`.
@@ -130,7 +121,6 @@ interface tcp {
         ///
         /// The returned socket is bound and in the Connection state. The following properties are inherited from the listener socket:
         /// - `address-family`
-        /// - `ipv6-only`
         /// - `keep-alive-enabled`
         /// - `keep-alive-idle-time`
         /// - `keep-alive-interval`
@@ -195,17 +185,6 @@ interface tcp {
         /// Equivalent to the SO_DOMAIN socket option.
         address-family: func() -> ip-address-family;
 
-        /// Whether IPv4 compatibility (dual-stack) mode is disabled or not.
-        ///
-        /// Equivalent to the IPV6_V6ONLY socket option.
-        ///
-        /// # Typical errors
-        /// - `invalid-state`:        (set) The socket is already bound.
-        /// - `not-supported`:        (get/set) `this` socket is an IPv4 socket.
-        /// - `not-supported`:        (set) Host does not support dual-stack sockets. (Implementations are not required to.)
-        ipv6-only: func() -> result<bool, error-code>;
-        set-ipv6-only: func(value: bool) -> result<_, error-code>;
-
         /// Hints the desired listen queue size. Implementations are free to ignore this.
         ///
         /// If the provided value is 0, an `invalid-argument` error is returned.
@@ -304,12 +283,16 @@ interface tcp {
 
         /// Initiate a graceful shutdown.
         ///
-        /// - 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.
+        /// - `receive`: The socket is not expecting to receive any data from
+        ///   the peer. The `input-stream` associated with this socket will be
+        ///   closed. Any data still in the receive queue at time of calling
+        ///   this method will be discarded.
+        /// - `send`: The socket has no more data to send to the peer. The `output-stream`
+        ///   associated with this socket will be closed and a FIN packet will be sent.
+        /// - `both`: Same effect as `receive` & `send` combined.
+        ///
+        /// This function is idempotent. Shutting a down a direction more than once
+        /// has no effect and returns `ok`.
         ///
         /// The shutdown function does not close (drop) the socket.
         ///
diff --git a/crates/wasi/wit/deps/sockets/udp-create-socket.wit b/crates/wasi/wit/deps/sockets/udp-create-socket.wit
index cc58234d8..0482d1fe7 100644
--- a/crates/wasi/wit/deps/sockets/udp-create-socket.wit
+++ b/crates/wasi/wit/deps/sockets/udp-create-socket.wit
@@ -6,6 +6,7 @@ interface udp-create-socket {
     /// Create a new UDP socket.
     ///
     /// Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` in POSIX.
+    /// On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise.
     ///
     /// 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` is called,
diff --git a/crates/wasi/wit/deps/sockets/udp.wit b/crates/wasi/wit/deps/sockets/udp.wit
index c8dafadfc..ae82b97fc 100644
--- a/crates/wasi/wit/deps/sockets/udp.wit
+++ b/crates/wasi/wit/deps/sockets/udp.wit
@@ -92,7 +92,6 @@ interface udp {
         /// 
         /// # Typical errors
         /// - `invalid-argument`:          The `remote-address` has the wrong address family. (EAFNOSUPPORT)
-        /// - `invalid-argument`:          `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa)
         /// - `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-state`:             The socket is not bound.
@@ -142,17 +141,6 @@ interface udp {
         /// Equivalent to the SO_DOMAIN socket option.
         address-family: func() -> ip-address-family;
 
-        /// Whether IPv4 compatibility (dual-stack) mode is disabled or not.
-        ///
-        /// Equivalent to the IPV6_V6ONLY socket option.
-        ///
-        /// # Typical errors
-        /// - `not-supported`:        (get/set) `this` socket is an IPv4 socket.
-        /// - `invalid-state`:        (set) The socket is already bound.
-        /// - `not-supported`:        (set) Host does not support dual-stack sockets. (Implementations are not required to.)
-        ipv6-only: func() -> result<bool, error-code>;
-        set-ipv6-only: func(value: bool) -> result<_, error-code>;
-
         /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options.
         ///
         /// If the provided value is 0, an `invalid-argument` error is returned.
@@ -248,7 +236,6 @@ interface udp {
         ///
         /// # Typical errors
         /// - `invalid-argument`:        The `remote-address` has the wrong address family. (EAFNOSUPPORT)
-        /// - `invalid-argument`:        `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa)
         /// - `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 `stream`. (EISCONN)
diff --git a/crates/wasi/wit/deps/sockets/world.wit b/crates/wasi/wit/deps/sockets/world.wit
index 49ad8d3d9..8588cc6c1 100644
--- a/crates/wasi/wit/deps/sockets/world.wit
+++ b/crates/wasi/wit/deps/sockets/world.wit
@@ -1,4 +1,4 @@
-package wasi:sockets@0.2.0-rc-2023-11-10;
+package wasi:sockets@0.2.0-rc-2024-01-16;
 
 world imports {
     import instance-network;


Test patch:
diff --git a/crates/test-programs/src/bin/preview2_tcp_bind.rs b/crates/test-programs/src/bin/preview2_tcp_bind.rs
index 8523dbe47..5cc54022e 100644
--- a/crates/test-programs/src/bin/preview2_tcp_bind.rs
+++ b/crates/test-programs/src/bin/preview2_tcp_bind.rs
@@ -138,23 +138,11 @@ fn test_tcp_bind_dual_stack(net: &Network) {
     let sock = TcpSocket::new(IpAddressFamily::Ipv6).unwrap();
     let addr = IpSocketAddress::new(IpAddress::IPV4_MAPPED_LOOPBACK, 0);
 
-    // Even on platforms that don't support dualstack sockets,
-    // setting ipv6_only to true (disabling dualstack mode) should work.
-    sock.set_ipv6_only(true).unwrap();
-
     // Binding an IPv4-mapped-IPv6 address on a ipv6-only socket should fail:
     assert!(matches!(
         sock.blocking_bind(net, addr),
         Err(ErrorCode::InvalidArgument)
     ));
-
-    sock.set_ipv6_only(false).unwrap();
-
-    sock.blocking_bind(net, addr).unwrap();
-
-    let bound_addr = sock.local_address().unwrap();
-
-    assert_eq!(bound_addr.family(), IpAddressFamily::Ipv6);
 }
 
 fn main() {
diff --git a/crates/test-programs/src/bin/preview2_tcp_connect.rs b/crates/test-programs/src/bin/preview2_tcp_connect.rs
index f304e46a5..d840cc593 100644
--- a/crates/test-programs/src/bin/preview2_tcp_connect.rs
+++ b/crates/test-programs/src/bin/preview2_tcp_connect.rs
@@ -83,23 +83,16 @@ fn test_tcp_connect_dual_stack(net: &Network) {
 
     // Tests:
 
-    // Even on platforms that don't support dualstack sockets,
-    // setting ipv6_only to true (disabling dualstack mode) should work.
-    v6_client.set_ipv6_only(true).unwrap();
-
-    // Connecting to an IPv4-mapped-IPv6 address on an ipv6-only socket should fail:
+    // Connecting to an IPv4 address on an IPv6 socket should fail:
+    assert!(matches!(
+        v6_client.blocking_connect(net, v4_listener_addr),
+        Err(ErrorCode::InvalidArgument)
+    ));
+    // Connecting to an IPv4-mapped-IPv6 address on an IPv6 socket should fail:
     assert!(matches!(
         v6_client.blocking_connect(net, v6_listener_addr),
         Err(ErrorCode::InvalidArgument)
     ));
-
-    v6_client.set_ipv6_only(false).unwrap();
-
-    v6_client.blocking_connect(net, v6_listener_addr).unwrap();
-
-    let connected_addr = v6_client.local_address().unwrap();
-
-    assert_eq!(connected_addr.family(), IpAddressFamily::Ipv6);
 }
 
 fn main() {
diff --git a/crates/test-programs/src/bin/preview2_tcp_sockopts.rs b/crates/test-programs/src/bin/preview2_tcp_sockopts.rs
index 8bc1ef49b..d52f7c8f9 100644
--- a/crates/test-programs/src/bin/preview2_tcp_sockopts.rs
+++ b/crates/test-programs/src/bin/preview2_tcp_sockopts.rs
@@ -10,10 +10,6 @@ fn test_tcp_sockopt_defaults(family: IpAddressFamily) {
 
     assert_eq!(sock.address_family(), family);
 
-    if family == IpAddressFamily::Ipv6 {
-        sock.ipv6_only().unwrap(); // Only verify that it has a default value at all, but either value is valid.
-    }
-
     sock.keep_alive_enabled().unwrap(); // Only verify that it has a default value at all, but either value is valid.
     assert!(sock.keep_alive_idle_time().unwrap() > 0);
     assert!(sock.keep_alive_interval().unwrap() > 0);
@@ -26,11 +22,6 @@ fn test_tcp_sockopt_defaults(family: IpAddressFamily) {
 fn test_tcp_sockopt_input_ranges(family: IpAddressFamily) {
     let sock = TcpSocket::new(family).unwrap();
 
-    if family == IpAddressFamily::Ipv6 {
-        assert!(matches!(sock.set_ipv6_only(true), Ok(_)));
-        assert!(matches!(sock.set_ipv6_only(false), Ok(_)));
-    }
-
     assert!(matches!(
         sock.set_listen_backlog_size(0),
         Err(ErrorCode::InvalidArgument)
@@ -90,13 +81,6 @@ fn test_tcp_sockopt_input_ranges(family: IpAddressFamily) {
 fn test_tcp_sockopt_readback(family: IpAddressFamily) {
     let sock = TcpSocket::new(family).unwrap();
 
-    if family == IpAddressFamily::Ipv6 {
-        sock.set_ipv6_only(true).unwrap();
-        assert_eq!(sock.ipv6_only().unwrap(), true);
-        sock.set_ipv6_only(false).unwrap();
-        assert_eq!(sock.ipv6_only().unwrap(), false);
-    }
-
     sock.set_keep_alive_enabled(true).unwrap();
     assert_eq!(sock.keep_alive_enabled().unwrap(), true);
     sock.set_keep_alive_enabled(false).unwrap();
@@ -125,15 +109,10 @@ fn test_tcp_sockopt_inheritance(net: &Network, family: IpAddressFamily) {
     let bind_addr = IpSocketAddress::new(IpAddress::new_loopback(family), 0);
     let listener = TcpSocket::new(family).unwrap();
 
-    let default_ipv6_only = listener.ipv6_only().unwrap_or(false);
     let default_keep_alive = listener.keep_alive_enabled().unwrap();
 
     // Configure options on listener:
     {
-        if family == IpAddressFamily::Ipv6 {
-            listener.set_ipv6_only(!default_ipv6_only).unwrap();
-        }
-
         listener
             .set_keep_alive_enabled(!default_keep_alive)
             .unwrap();
@@ -154,10 +133,6 @@ fn test_tcp_sockopt_inheritance(net: &Network, family: IpAddressFamily) {
 
     // Verify options on accepted socket:
     {
-        if family == IpAddressFamily::Ipv6 {
-            assert_eq!(accepted_client.ipv6_only().unwrap(), !default_ipv6_only);
-        }
-
         assert_eq!(
             accepted_client.keep_alive_enabled().unwrap(),
             !default_keep_alive
diff --git a/crates/test-programs/src/bin/preview2_tcp_states.rs b/crates/test-programs/src/bin/preview2_tcp_states.rs
index 308c6cfd6..4d31e36cf 100644
--- a/crates/test-programs/src/bin/preview2_tcp_states.rs
+++ b/crates/test-programs/src/bin/preview2_tcp_states.rs
@@ -35,20 +35,6 @@ fn test_tcp_unbound_state_invariants(family: IpAddressFamily) {
     assert_eq!(sock.is_listening(), false);
     assert_eq!(sock.address_family(), family);
 
-    if family == IpAddressFamily::Ipv6 {
-        assert!(matches!(sock.ipv6_only(), Ok(_)));
-
-        // Even on platforms that don't support dualstack sockets,
-        // setting ipv6_only to true (disabling dualstack mode) should work.
-        assert!(matches!(sock.set_ipv6_only(true), Ok(_)));
-    } else {
-        assert!(matches!(sock.ipv6_only(), Err(ErrorCode::NotSupported)));
-        assert!(matches!(
-            sock.set_ipv6_only(true),
-            Err(ErrorCode::NotSupported)
-        ));
-    }
-
     assert!(matches!(sock.set_listen_backlog_size(32), Ok(_)));
     assert!(matches!(sock.keep_alive_enabled(), Ok(_)));
     assert!(matches!(sock.set_keep_alive_enabled(false), Ok(_)));
@@ -100,20 +86,6 @@ fn test_tcp_bound_state_invariants(net: &Network, family: IpAddressFamily) {
     assert_eq!(sock.is_listening(), false);
     assert_eq!(sock.address_family(), family);
 
-    if family == IpAddressFamily::Ipv6 {
-        assert!(matches!(sock.ipv6_only(), Ok(_)));
-        assert!(matches!(
-            sock.set_ipv6_only(true),
-            Err(ErrorCode::InvalidState)
-        ));
-    } else {
-        assert!(matches!(sock.ipv6_only(), Err(ErrorCode::NotSupported)));
-        assert!(matches!(
-            sock.set_ipv6_only(true),
-            Err(ErrorCode::NotSupported)
-        ));
-    }
-
     assert!(matches!(sock.set_listen_backlog_size(32), Ok(_)));
     assert!(matches!(sock.keep_alive_enabled(), Ok(_)));
     assert!(matches!(sock.set_keep_alive_enabled(false), Ok(_)));
@@ -169,20 +141,6 @@ fn test_tcp_listening_state_invariants(net: &Network, family: IpAddressFamily) {
     assert_eq!(sock.is_listening(), true);
     assert_eq!(sock.address_family(), family);
 
-    if family == IpAddressFamily::Ipv6 {
-        assert!(matches!(sock.ipv6_only(), Ok(_)));
-        assert!(matches!(
-            sock.set_ipv6_only(true),
-            Err(ErrorCode::InvalidState)
-        ));
-    } else {
-        assert!(matches!(sock.ipv6_only(), Err(ErrorCode::NotSupported)));
-        assert!(matches!(
-            sock.set_ipv6_only(true),
-            Err(ErrorCode::NotSupported)
-        ));
-    }
-
     assert!(matches!(
         sock.set_listen_backlog_size(32),
         Ok(_) | Err(ErrorCode::NotSupported)
@@ -238,20 +196,6 @@ fn test_tcp_connected_state_invariants(net: &Network, family: IpAddressFamily) {
     assert_eq!(sock.is_listening(), false);
     assert_eq!(sock.address_family(), family);
 
-    if family == IpAddressFamily::Ipv6 {
-        assert!(matches!(sock.ipv6_only(), Ok(_)));
-        assert!(matches!(
-            sock.set_ipv6_only(true),
-            Err(ErrorCode::InvalidState)
-        ));
-    } else {
-        assert!(matches!(sock.ipv6_only(), Err(ErrorCode::NotSupported)));
-        assert!(matches!(
-            sock.set_ipv6_only(true),
-            Err(ErrorCode::NotSupported)
-        ));
-    }
-
     assert!(matches!(sock.keep_alive_enabled(), Ok(_)));
     assert!(matches!(sock.set_keep_alive_enabled(false), Ok(_)));
     assert!(matches!(sock.keep_alive_idle_time(), Ok(_)));
diff --git a/crates/test-programs/src/bin/preview2_udp_bind.rs b/crates/test-programs/src/bin/preview2_udp_bind.rs
index f320a0430..8b366bcf9 100644
--- a/crates/test-programs/src/bin/preview2_udp_bind.rs
+++ b/crates/test-programs/src/bin/preview2_udp_bind.rs
@@ -75,23 +75,11 @@ fn test_udp_bind_dual_stack(net: &Network) {
     let sock = UdpSocket::new(IpAddressFamily::Ipv6).unwrap();
     let addr = IpSocketAddress::new(IpAddress::IPV4_MAPPED_LOOPBACK, 0);
 
-    // Even on platforms that don't support dualstack sockets,
-    // setting ipv6_only to true (disabling dualstack mode) should work.
-    sock.set_ipv6_only(true).unwrap();
-
     // Binding an IPv4-mapped-IPv6 address on a ipv6-only socket should fail:
     assert!(matches!(
         sock.blocking_bind(net, addr),
         Err(ErrorCode::InvalidArgument)
     ));
-
-    sock.set_ipv6_only(false).unwrap();
-
-    sock.blocking_bind(net, addr).unwrap();
-
-    let bound_addr = sock.local_address().unwrap();
-
-    assert_eq!(bound_addr.family(), IpAddressFamily::Ipv6);
 }
 
 fn main() {
diff --git a/crates/test-programs/src/bin/preview2_udp_connect.rs b/crates/test-programs/src/bin/preview2_udp_connect.rs
index 7e21c7e79..7ce1ad56f 100644
--- a/crates/test-programs/src/bin/preview2_udp_connect.rs
+++ b/crates/test-programs/src/bin/preview2_udp_connect.rs
@@ -88,38 +88,21 @@ fn test_udp_connect_dual_stack(net: &Network) {
         IpSocketAddress::new(IpAddress::IPV4_MAPPED_LOOPBACK, v4_server_addr.port());
 
     // Tests:
-    {
-        let v6_client = UdpSocket::new(IpAddressFamily::Ipv6).unwrap();
-
-        // Even on platforms that don't support dualstack sockets,
-        // setting ipv6_only to true (disabling dualstack mode) should work.
-        v6_client.set_ipv6_only(true).unwrap();
-
-        v6_client.blocking_bind_unspecified(&net).unwrap();
-
-        // Connecting to an IPv4-mapped-IPv6 address on an ipv6-only socket should fail:
-        assert!(matches!(
-            v6_client.stream(Some(v6_server_addr)),
-            Err(ErrorCode::InvalidArgument)
-        ));
-    }
-
-    {
-        let v6_client = UdpSocket::new(IpAddressFamily::Ipv6).unwrap();
-
-        v6_client.set_ipv6_only(false).unwrap();
-        v6_client.blocking_bind_unspecified(&net).unwrap();
-        v6_client.stream(Some(v6_server_addr)).unwrap();
-
-        assert_eq!(
-            v6_client.local_address().unwrap().family(),
-            IpAddressFamily::Ipv6
-        );
-        assert_eq!(
-            v6_client.remote_address().unwrap().family(),
-            IpAddressFamily::Ipv6
-        );
-    }
+    let v6_client = UdpSocket::new(IpAddressFamily::Ipv6).unwrap();
+
+    v6_client.blocking_bind_unspecified(&net).unwrap();
+
+    // Connecting to an IPv4 address on an IPv6 socket should fail:
+    assert!(matches!(
+        v6_client.stream(Some(v4_server_addr)),
+        Err(ErrorCode::InvalidArgument)
+    ));
+
+    // Connecting to an IPv4-mapped-IPv6 address on an IPv6 socket should fail:
+    assert!(matches!(
+        v6_client.stream(Some(v6_server_addr)),
+        Err(ErrorCode::InvalidArgument)
+    ));
 }
 
 fn main() {
diff --git a/crates/test-programs/src/bin/preview2_udp_sample_application.rs b/crates/test-programs/src/bin/preview2_udp_sample_application.rs
index 171258e5e..ca001769f 100644
--- a/crates/test-programs/src/bin/preview2_udp_sample_application.rs
+++ b/crates/test-programs/src/bin/preview2_udp_sample_application.rs
@@ -72,52 +72,6 @@ fn test_udp_sample_application(family: IpAddressFamily, bind_address: IpSocketAd
     }
 }
 
-fn test_udp_dual_stack_conversation() {
-    let net = Network::default();
-
-    let v4_server = UdpSocket::new(IpAddressFamily::Ipv4).unwrap();
-    v4_server
-        .blocking_bind(&net, IpSocketAddress::new(IpAddress::IPV4_LOOPBACK, 0))
-        .unwrap();
-    let (server_incoming, server_outgoing) = v4_server.stream(None).unwrap();
-
-    let v4_server_addr = v4_server.local_address().unwrap();
-    let v6_server_addr =
-        IpSocketAddress::new(IpAddress::IPV4_MAPPED_LOOPBACK, v4_server_addr.port());
-
-    let v6_client = UdpSocket::new(IpAddressFamily::Ipv6).unwrap();
-
-    v6_client.set_ipv6_only(false).unwrap();
-    v6_client.blocking_bind_unspecified(&net).unwrap();
-    let (client_incoming, client_outgoing) = v6_client.stream(None).unwrap();
-
-    // Send from v6 client to v4 server:
-    client_outgoing
-        .blocking_send(&[OutgoingDatagram {
-            data: "Hi!".into(),
-            remote_address: Some(v6_server_addr),
-        }])
-        .unwrap();
-
-    // Receive from v6 client on v4 server:
-    let results = server_incoming.blocking_receive(1..1).unwrap();
-    let msg = results.first().unwrap();
-    assert_eq!(msg.remote_address.family(), IpAddressFamily::Ipv4);
-
-    // Send from v4 server to v6 client:
-    server_outgoing
-        .blocking_send(&[OutgoingDatagram {
-            data: msg.data.clone(),
-            remote_address: Some(msg.remote_address),
-        }])
-        .unwrap();
-
-    // Receive from v4 server on v6 client:
-    let results = client_incoming.blocking_receive(1..1).unwrap();
-    let msg = results.first().unwrap();
-    assert_eq!(msg.remote_address.family(), IpAddressFamily::Ipv6);
-}
-
 fn main() {
     test_udp_sample_application(
         IpAddressFamily::Ipv4,
@@ -135,6 +89,4 @@ fn main() {
             scope_id: 0,
         }),
     );
-
-    test_udp_dual_stack_conversation();
 }
diff --git a/crates/test-programs/src/bin/preview2_udp_sockopts.rs b/crates/test-programs/src/bin/preview2_udp_sockopts.rs
index f705b476c..893789b50 100644
--- a/crates/test-programs/src/bin/preview2_udp_sockopts.rs
+++ b/crates/test-programs/src/bin/preview2_udp_sockopts.rs
@@ -6,10 +6,6 @@ fn test_udp_sockopt_defaults(family: IpAddressFamily) {
 
     assert_eq!(sock.address_family(), family);
 
-    if family == IpAddressFamily::Ipv6 {
-        sock.ipv6_only().unwrap(); // Only verify that it has a default value at all, but either value is valid.
-    }
-
     assert!(sock.unicast_hop_limit().unwrap() > 0);
     assert!(sock.receive_buffer_size().unwrap() > 0);
     assert!(sock.send_buffer_size().unwrap() > 0);
@@ -18,11 +14,6 @@ fn test_udp_sockopt_defaults(family: IpAddressFamily) {
 fn test_udp_sockopt_input_ranges(family: IpAddressFamily) {
     let sock = UdpSocket::new(family).unwrap();
 
-    if family == IpAddressFamily::Ipv6 {
-        assert!(matches!(sock.set_ipv6_only(true), Ok(_)));
-        assert!(matches!(sock.set_ipv6_only(false), Ok(_)));
-    }
-
     assert!(matches!(
         sock.set_unicast_hop_limit(0),
         Err(ErrorCode::InvalidArgument)
@@ -47,13 +38,6 @@ fn test_udp_sockopt_input_ranges(family: IpAddressFamily) {
 fn test_udp_sockopt_readback(family: IpAddressFamily) {
     let sock = UdpSocket::new(family).unwrap();
 
-    if family == IpAddressFamily::Ipv6 {
-        sock.set_ipv6_only(true).unwrap();
-        assert_eq!(sock.ipv6_only().unwrap(), true);
-        sock.set_ipv6_only(false).unwrap();
-        assert_eq!(sock.ipv6_only().unwrap(), false);
-    }
-
     sock.set_unicast_hop_limit(42).unwrap();
     assert_eq!(sock.unicast_hop_limit().unwrap(), 42);
 
diff --git a/crates/test-programs/src/bin/preview2_udp_states.rs b/crates/test-programs/src/bin/preview2_udp_states.rs
index 4081b971e..314f94a25 100644
--- a/crates/test-programs/src/bin/preview2_udp_states.rs
+++ b/crates/test-programs/src/bin/preview2_udp_states.rs
@@ -18,20 +18,6 @@ fn test_udp_unbound_state_invariants(family: IpAddressFamily) {
     ));
     assert_eq!(sock.address_family(), family);
 
-    if family == IpAddressFamily::Ipv6 {
-        assert!(matches!(sock.ipv6_only(), Ok(_)));
-
-        // Even on platforms that don't support dualstack sockets,
-        // setting ipv6_only to true (disabling dualstack mode) should work.
-        assert!(matches!(sock.set_ipv6_only(true), Ok(_)));
-    } else {
-        assert!(matches!(sock.ipv6_only(), Err(ErrorCode::NotSupported)));
-        assert!(matches!(
-            sock.set_ipv6_only(true),
-            Err(ErrorCode::NotSupported)
-        ));
-    }
-
     assert!(matches!(sock.unicast_hop_limit(), Ok(_)));
     assert!(matches!(sock.set_unicast_hop_limit(255), Ok(_)));
     assert!(matches!(sock.receive_buffer_size(), Ok(_)));
@@ -59,20 +45,6 @@ fn test_udp_bound_state_invariants(net: &Network, family: IpAddressFamily) {
     ));
     assert_eq!(sock.address_family(), family);
 
-    if family == IpAddressFamily::Ipv6 {
-        assert!(matches!(sock.ipv6_only(), Ok(_)));
-        assert!(matches!(
-            sock.set_ipv6_only(true),
-            Err(ErrorCode::InvalidState)
-        ));
-    } else {
-        assert!(matches!(sock.ipv6_only(), Err(ErrorCode::NotSupported)));
-        assert!(matches!(
-            sock.set_ipv6_only(true),
-            Err(ErrorCode::NotSupported)
-        ));
-    }
-
     assert!(matches!(sock.unicast_hop_limit(), Ok(_)));
     assert!(matches!(sock.set_unicast_hop_limit(255), Ok(_)));
     assert!(matches!(sock.receive_buffer_size(), Ok(_)));
@@ -99,20 +71,6 @@ fn test_udp_connected_state_invariants(net: &Network, family: IpAddressFamily) {
     assert!(matches!(sock.remote_address(), Ok(_)));
     assert_eq!(sock.address_family(), family);
 
-    if family == IpAddressFamily::Ipv6 {
-        assert!(matches!(sock.ipv6_only(), Ok(_)));
-        assert!(matches!(
-            sock.set_ipv6_only(true),
-            Err(ErrorCode::InvalidState)
-        ));
-    } else {
-        assert!(matches!(sock.ipv6_only(), Err(ErrorCode::NotSupported)));
-        assert!(matches!(
-            sock.set_ipv6_only(true),
-            Err(ErrorCode::NotSupported)
-        ));
-    }
-
     assert!(matches!(sock.unicast_hop_limit(), Ok(_)));
     assert!(matches!(sock.set_unicast_hop_limit(255), Ok(_)));
     assert!(matches!(sock.receive_buffer_size(), Ok(_)));
diff --git a/crates/wasi-http/wit/test.wit b/crates/wasi-http/wit/test.wit
index f3cc6151b..15294b4aa 100644
--- a/crates/wasi-http/wit/test.wit
+++ b/crates/wasi-http/wit/test.wit
@@ -2,7 +2,7 @@ package wasmtime:wasi;
 
 // only used as part of `test-programs`
 world test-reactor {
-  include wasi:cli/imports@0.2.0-rc-2023-12-05;
+  include wasi:cli/imports@0.2.0-rc-2024-01-16;
 
   export add-strings: func(s: list<string>) -> u32;
   export get-strings: func() -> list<string>;
@@ -16,7 +16,7 @@ world test-reactor {
 }
 
 world test-command {
-  include wasi:cli/imports@0.2.0-rc-2023-12-05;
-  import wasi:http/types@0.2.0-rc-2023-12-05;
-  import wasi:http/outgoing-handler@0.2.0-rc-2023-12-05;
+  include wasi:cli/imports@0.2.0-rc-2024-01-16;
+  import wasi:http/types@0.2.0-rc-2024-01-16;
+  import wasi:http/outgoing-handler@0.2.0-rc-2024-01-16;
 }
diff --git a/crates/wasi/wit/test.wit b/crates/wasi/wit/test.wit
index f3cc6151b..15294b4aa 100644
--- a/crates/wasi/wit/test.wit
+++ b/crates/wasi/wit/test.wit
@@ -2,7 +2,7 @@ package wasmtime:wasi;
 
 // only used as part of `test-programs`
 world test-reactor {
-  include wasi:cli/imports@0.2.0-rc-2023-12-05;
+  include wasi:cli/imports@0.2.0-rc-2024-01-16;
 
   export add-strings: func(s: list<string>) -> u32;
   export get-strings: func() -> list<string>;
@@ -16,7 +16,7 @@ world test-reactor {
 }
 
 world test-command {
-  include wasi:cli/imports@0.2.0-rc-2023-12-05;
-  import wasi:http/types@0.2.0-rc-2023-12-05;
-  import wasi:http/outgoing-handler@0.2.0-rc-2023-12-05;
+  include wasi:cli/imports@0.2.0-rc-2024-01-16;
+  import wasi:http/types@0.2.0-rc-2024-01-16;
+  import wasi:http/outgoing-handler@0.2.0-rc-2024-01-16;
 }
diff --git a/tests/all/cli_tests/component-basic.wat b/tests/all/cli_tests/component-basic.wat
index 6e009cab9..99600f1f0 100644
--- a/tests/all/cli_tests/component-basic.wat
+++ b/tests/all/cli_tests/component-basic.wat
@@ -7,6 +7,6 @@
   (func $run (result (result))
     (canon lift (core func $i "run")))
 
-  (instance (export (interface "wasi:cli/run@0.2.0-rc-2023-12-05"))
+  (instance (export (interface "wasi:cli/run@0.2.0-rc-2024-01-16"))
     (export "run" (func $run)))
 )