Repository: bytecodealliance/wasmtime
Pull request #7484 (2023-11-05 13:44:43)

wasi-sockets: Remove TCP no-delay
The semantics of TCP_NODELAY (and TCP_CORK for that matter) and its effects on the output-stream needs to investigated and specified.  I don't expect there to be anything insurmountable. Its just that I haven't had the time to do so yet and I can't promise to have it done before the stabilization Preview2.

So, in order to get wasi-sockets ready for Preview2, it was discussed to temporarily remove `no-delay` and reevaluate its inclusion before Preview3.

Related: https://github.com/bytecodealliance/wasmtime/issues/7013


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 65b555e89..1b8e40430 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -226,7 +226,7 @@ checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
 
 [[package]]
 name = "byte-array-literals"
-version = "15.0.0"
+version = "16.0.0"
 
 [[package]]
 name = "byteorder"
@@ -557,7 +557,7 @@ dependencies = [
 
 [[package]]
 name = "cranelift"
-version = "0.102.0"
+version = "0.103.0"
 dependencies = [
  "cranelift-codegen",
  "cranelift-frontend",
@@ -565,14 +565,14 @@ dependencies = [
 
 [[package]]
 name = "cranelift-bforest"
-version = "0.102.0"
+version = "0.103.0"
 dependencies = [
  "cranelift-entity",
 ]
 
 [[package]]
 name = "cranelift-codegen"
-version = "0.102.0"
+version = "0.103.0"
 dependencies = [
  "anyhow",
  "bincode",
@@ -600,25 +600,25 @@ dependencies = [
 
 [[package]]
 name = "cranelift-codegen-meta"
-version = "0.102.0"
+version = "0.103.0"
 dependencies = [
  "cranelift-codegen-shared",
 ]
 
 [[package]]
 name = "cranelift-codegen-shared"
-version = "0.102.0"
+version = "0.103.0"
 
 [[package]]
 name = "cranelift-control"
-version = "0.102.0"
+version = "0.103.0"
 dependencies = [
  "arbitrary",
 ]
 
 [[package]]
 name = "cranelift-entity"
-version = "0.102.0"
+version = "0.103.0"
 dependencies = [
  "serde",
  "serde_derive",
@@ -657,7 +657,7 @@ dependencies = [
 
 [[package]]
 name = "cranelift-frontend"
-version = "0.102.0"
+version = "0.103.0"
 dependencies = [
  "cranelift-codegen",
  "hashbrown 0.14.0",
@@ -681,7 +681,7 @@ dependencies = [
 
 [[package]]
 name = "cranelift-interpreter"
-version = "0.102.0"
+version = "0.103.0"
 dependencies = [
  "cranelift-codegen",
  "cranelift-entity",
@@ -695,7 +695,7 @@ dependencies = [
 
 [[package]]
 name = "cranelift-isle"
-version = "0.102.0"
+version = "0.103.0"
 dependencies = [
  "codespan-reporting",
  "log",
@@ -704,7 +704,7 @@ dependencies = [
 
 [[package]]
 name = "cranelift-jit"
-version = "0.102.0"
+version = "0.103.0"
 dependencies = [
  "anyhow",
  "cranelift",
@@ -725,7 +725,7 @@ dependencies = [
 
 [[package]]
 name = "cranelift-module"
-version = "0.102.0"
+version = "0.103.0"
 dependencies = [
  "anyhow",
  "cranelift-codegen",
@@ -737,7 +737,7 @@ dependencies = [
 
 [[package]]
 name = "cranelift-native"
-version = "0.102.0"
+version = "0.103.0"
 dependencies = [
  "cranelift-codegen",
  "libc",
@@ -746,7 +746,7 @@ dependencies = [
 
 [[package]]
 name = "cranelift-object"
-version = "0.102.0"
+version = "0.103.0"
 dependencies = [
  "anyhow",
  "cranelift-codegen",
@@ -761,7 +761,7 @@ dependencies = [
 
 [[package]]
 name = "cranelift-reader"
-version = "0.102.0"
+version = "0.103.0"
 dependencies = [
  "anyhow",
  "cranelift-codegen",
@@ -771,7 +771,7 @@ dependencies = [
 
 [[package]]
 name = "cranelift-serde"
-version = "0.102.0"
+version = "0.103.0"
 dependencies = [
  "clap",
  "cranelift-codegen",
@@ -818,7 +818,7 @@ dependencies = [
 
 [[package]]
 name = "cranelift-wasm"
-version = "0.102.0"
+version = "0.103.0"
 dependencies = [
  "cranelift-codegen",
  "cranelift-entity",
@@ -2923,7 +2923,7 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
 
 [[package]]
 name = "verify-component-adapter"
-version = "15.0.0"
+version = "16.0.0"
 dependencies = [
  "anyhow",
  "wasmparser",
@@ -2973,7 +2973,7 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 
 [[package]]
 name = "wasi-cap-std-sync"
-version = "15.0.0"
+version = "16.0.0"
 dependencies = [
  "anyhow",
  "async-trait",
@@ -2995,7 +2995,7 @@ dependencies = [
 
 [[package]]
 name = "wasi-common"
-version = "15.0.0"
+version = "16.0.0"
 dependencies = [
  "anyhow",
  "bitflags 2.4.1",
@@ -3019,7 +3019,7 @@ dependencies = [
 
 [[package]]
 name = "wasi-preview1-component-adapter"
-version = "15.0.0"
+version = "16.0.0"
 dependencies = [
  "byte-array-literals",
  "object",
@@ -3030,7 +3030,7 @@ dependencies = [
 
 [[package]]
 name = "wasi-tokio"
-version = "15.0.0"
+version = "16.0.0"
 dependencies = [
  "anyhow",
  "cap-std",
@@ -3221,7 +3221,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime"
-version = "15.0.0"
+version = "16.0.0"
 dependencies = [
  "anyhow",
  "async-trait",
@@ -3262,14 +3262,14 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-asm-macros"
-version = "15.0.0"
+version = "16.0.0"
 dependencies = [
  "cfg-if",
 ]
 
 [[package]]
 name = "wasmtime-bench-api"
-version = "15.0.0"
+version = "16.0.0"
 dependencies = [
  "anyhow",
  "cap-std",
@@ -3286,14 +3286,14 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-c-api"
-version = "15.0.0"
+version = "16.0.0"
 dependencies = [
  "wasmtime-c-api-impl",
 ]
 
 [[package]]
 name = "wasmtime-c-api-impl"
-version = "15.0.0"
+version = "16.0.0"
 dependencies = [
  "anyhow",
  "cap-std",
@@ -3320,7 +3320,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-cache"
-version = "15.0.0"
+version = "16.0.0"
 dependencies = [
  "anyhow",
  "base64",
@@ -3342,7 +3342,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-cli"
-version = "15.0.0"
+version = "16.0.0"
 dependencies = [
  "anyhow",
  "async-trait",
@@ -3395,7 +3395,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-cli-flags"
-version = "15.0.0"
+version = "16.0.0"
 dependencies = [
  "anyhow",
  "clap",
@@ -3408,7 +3408,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-component-macro"
-version = "15.0.0"
+version = "16.0.0"
 dependencies = [
  "anyhow",
  "component-macro-test-helpers",
@@ -3424,11 +3424,11 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-component-util"
-version = "15.0.0"
+version = "16.0.0"
 
 [[package]]
 name = "wasmtime-cranelift"
-version = "15.0.0"
+version = "16.0.0"
 dependencies = [
  "anyhow",
  "cfg-if",
@@ -3451,7 +3451,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-cranelift-shared"
-version = "15.0.0"
+version = "16.0.0"
 dependencies = [
  "anyhow",
  "cranelift-codegen",
@@ -3465,7 +3465,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-environ"
-version = "15.0.0"
+version = "16.0.0"
 dependencies = [
  "anyhow",
  "clap",
@@ -3503,7 +3503,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-explorer"
-version = "15.0.0"
+version = "16.0.0"
 dependencies = [
  "anyhow",
  "capstone",
@@ -3517,7 +3517,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-fiber"
-version = "15.0.0"
+version = "16.0.0"
 dependencies = [
  "anyhow",
  "backtrace",
@@ -3586,7 +3586,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-jit"
-version = "15.0.0"
+version = "16.0.0"
 dependencies = [
  "addr2line",
  "anyhow",
@@ -3611,7 +3611,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-jit-debug"
-version = "15.0.0"
+version = "16.0.0"
 dependencies = [
  "object",
  "once_cell",
@@ -3621,7 +3621,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-jit-icache-coherence"
-version = "15.0.0"
+version = "16.0.0"
 dependencies = [
  "cfg-if",
  "libc",
@@ -3630,7 +3630,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-runtime"
-version = "15.0.0"
+version = "16.0.0"
 dependencies = [
  "anyhow",
  "cc",
@@ -3660,7 +3660,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-types"
-version = "15.0.0"
+version = "16.0.0"
 dependencies = [
  "cranelift-entity",
  "serde",
@@ -3671,7 +3671,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-versioned-export-macros"
-version = "15.0.0"
+version = "16.0.0"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -3680,7 +3680,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-wasi"
-version = "15.0.0"
+version = "16.0.0"
 dependencies = [
  "anyhow",
  "async-trait",
@@ -3718,7 +3718,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-wasi-http"
-version = "15.0.0"
+version = "16.0.0"
 dependencies = [
  "anyhow",
  "async-trait",
@@ -3744,7 +3744,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-wasi-nn"
-version = "15.0.0"
+version = "16.0.0"
 dependencies = [
  "anyhow",
  "openvino",
@@ -3757,7 +3757,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-wasi-threads"
-version = "15.0.0"
+version = "16.0.0"
 dependencies = [
  "anyhow",
  "log",
@@ -3769,7 +3769,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-wast"
-version = "15.0.0"
+version = "16.0.0"
 dependencies = [
  "anyhow",
  "log",
@@ -3779,7 +3779,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-winch"
-version = "15.0.0"
+version = "16.0.0"
 dependencies = [
  "anyhow",
  "cranelift-codegen",
@@ -3794,7 +3794,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-wit-bindgen"
-version = "15.0.0"
+version = "16.0.0"
 dependencies = [
  "anyhow",
  "heck",
@@ -3804,7 +3804,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-wmemcheck"
-version = "15.0.0"
+version = "16.0.0"
 
 [[package]]
 name = "wast"
@@ -3865,7 +3865,7 @@ dependencies = [
 
 [[package]]
 name = "wiggle"
-version = "15.0.0"
+version = "16.0.0"
 dependencies = [
  "anyhow",
  "async-trait",
@@ -3882,7 +3882,7 @@ dependencies = [
 
 [[package]]
 name = "wiggle-generate"
-version = "15.0.0"
+version = "16.0.0"
 dependencies = [
  "anyhow",
  "heck",
@@ -3895,7 +3895,7 @@ dependencies = [
 
 [[package]]
 name = "wiggle-macro"
-version = "15.0.0"
+version = "16.0.0"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -3950,7 +3950,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 
 [[package]]
 name = "winch-codegen"
-version = "0.13.0"
+version = "0.14.0"
 dependencies = [
  "anyhow",
  "cranelift-codegen",
diff --git a/Cargo.toml b/Cargo.toml
index 513105811..f41e8d743 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -122,7 +122,7 @@ exclude = [
 ]
 
 [workspace.package]
-version = "15.0.0"
+version = "16.0.0"
 authors = ["The Wasmtime Project Developers"]
 edition = "2021"
 # Wasmtime's current policy is that this number can be no larger than the
@@ -131,59 +131,59 @@ rust-version = "1.71.0"
 
 [workspace.dependencies]
 arbitrary = { version = "1.3.1" }
-wasmtime-wmemcheck = { path = "crates/wmemcheck", version = "=15.0.0" }
-wasmtime = { path = "crates/wasmtime", version = "15.0.0", default-features = false }
-wasmtime-cache = { path = "crates/cache", version = "=15.0.0" }
-wasmtime-cli-flags = { path = "crates/cli-flags", version = "=15.0.0" }
-wasmtime-cranelift = { path = "crates/cranelift", version = "=15.0.0" }
-wasmtime-cranelift-shared = { path = "crates/cranelift-shared", version = "=15.0.0" }
-wasmtime-winch = { path = "crates/winch", version = "=15.0.0" }
-wasmtime-environ = { path = "crates/environ", version = "=15.0.0" }
-wasmtime-explorer = { path = "crates/explorer", version = "=15.0.0" }
-wasmtime-fiber = { path = "crates/fiber", version = "=15.0.0" }
-wasmtime-types = { path = "crates/types", version = "15.0.0" }
-wasmtime-jit = { path = "crates/jit", version = "=15.0.0" }
-wasmtime-jit-debug = { path = "crates/jit-debug", version = "=15.0.0" }
-wasmtime-runtime = { path = "crates/runtime", version = "=15.0.0" }
-wasmtime-wast = { path = "crates/wast", version = "=15.0.0" }
-wasmtime-wasi = { path = "crates/wasi", version = "15.0.0", default-features = false }
-wasmtime-wasi-http = { path = "crates/wasi-http", version = "=15.0.0", default-features = false }
-wasmtime-wasi-nn = { path = "crates/wasi-nn", version = "15.0.0" }
-wasmtime-wasi-threads = { path = "crates/wasi-threads", version = "15.0.0" }
-wasmtime-component-util = { path = "crates/component-util", version = "=15.0.0" }
-wasmtime-component-macro = { path = "crates/component-macro", version = "=15.0.0" }
-wasmtime-asm-macros = { path = "crates/asm-macros", version = "=15.0.0" }
-wasmtime-versioned-export-macros = { path = "crates/versioned-export-macros", version = "=15.0.0" }
+wasmtime-wmemcheck = { path = "crates/wmemcheck", version = "=16.0.0" }
+wasmtime = { path = "crates/wasmtime", version = "16.0.0", default-features = false }
+wasmtime-cache = { path = "crates/cache", version = "=16.0.0" }
+wasmtime-cli-flags = { path = "crates/cli-flags", version = "=16.0.0" }
+wasmtime-cranelift = { path = "crates/cranelift", version = "=16.0.0" }
+wasmtime-cranelift-shared = { path = "crates/cranelift-shared", version = "=16.0.0" }
+wasmtime-winch = { path = "crates/winch", version = "=16.0.0" }
+wasmtime-environ = { path = "crates/environ", version = "=16.0.0" }
+wasmtime-explorer = { path = "crates/explorer", version = "=16.0.0" }
+wasmtime-fiber = { path = "crates/fiber", version = "=16.0.0" }
+wasmtime-types = { path = "crates/types", version = "16.0.0" }
+wasmtime-jit = { path = "crates/jit", version = "=16.0.0" }
+wasmtime-jit-debug = { path = "crates/jit-debug", version = "=16.0.0" }
+wasmtime-runtime = { path = "crates/runtime", version = "=16.0.0" }
+wasmtime-wast = { path = "crates/wast", version = "=16.0.0" }
+wasmtime-wasi = { path = "crates/wasi", version = "16.0.0", default-features = false }
+wasmtime-wasi-http = { path = "crates/wasi-http", version = "=16.0.0", default-features = false }
+wasmtime-wasi-nn = { path = "crates/wasi-nn", version = "16.0.0" }
+wasmtime-wasi-threads = { path = "crates/wasi-threads", version = "16.0.0" }
+wasmtime-component-util = { path = "crates/component-util", version = "=16.0.0" }
+wasmtime-component-macro = { path = "crates/component-macro", version = "=16.0.0" }
+wasmtime-asm-macros = { path = "crates/asm-macros", version = "=16.0.0" }
+wasmtime-versioned-export-macros = { path = "crates/versioned-export-macros", version = "=16.0.0" }
 component-test-util = { path = "crates/misc/component-test-util" }
 component-fuzz-util = { path = "crates/misc/component-fuzz-util" }
-wiggle = { path = "crates/wiggle", version = "=15.0.0", default-features = false }
-wiggle-macro = { path = "crates/wiggle/macro", version = "=15.0.0" }
-wiggle-generate = { path = "crates/wiggle/generate", version = "=15.0.0" }
-wasi-common = { path = "crates/wasi-common", version = "=15.0.0" }
-wasi-tokio = { path = "crates/wasi-common/tokio", version = "=15.0.0" }
-wasi-cap-std-sync = { path = "crates/wasi-common/cap-std-sync", version = "=15.0.0" }
+wiggle = { path = "crates/wiggle", version = "=16.0.0", default-features = false }
+wiggle-macro = { path = "crates/wiggle/macro", version = "=16.0.0" }
+wiggle-generate = { path = "crates/wiggle/generate", version = "=16.0.0" }
+wasi-common = { path = "crates/wasi-common", version = "=16.0.0" }
+wasi-tokio = { path = "crates/wasi-common/tokio", version = "=16.0.0" }
+wasi-cap-std-sync = { path = "crates/wasi-common/cap-std-sync", version = "=16.0.0" }
 wasmtime-fuzzing = { path = "crates/fuzzing" }
-wasmtime-jit-icache-coherence = { path = "crates/jit-icache-coherence", version = "=15.0.0" }
-wasmtime-wit-bindgen = { path = "crates/wit-bindgen", version = "=15.0.0" }
+wasmtime-jit-icache-coherence = { path = "crates/jit-icache-coherence", version = "=16.0.0" }
+wasmtime-wit-bindgen = { path = "crates/wit-bindgen", version = "=16.0.0" }
 test-programs-artifacts = { path = 'crates/test-programs/artifacts' }
 
-cranelift-wasm = { path = "cranelift/wasm", version = "0.102.0" }
-cranelift-codegen = { path = "cranelift/codegen", version = "0.102.0", default-features = false, features = ["std", "unwind"] }
-cranelift-frontend = { path = "cranelift/frontend", version = "0.102.0" }
-cranelift-entity = { path = "cranelift/entity", version = "0.102.0" }
-cranelift-native = { path = "cranelift/native", version = "0.102.0" }
-cranelift-module = { path = "cranelift/module", version = "0.102.0" }
-cranelift-interpreter = { path = "cranelift/interpreter", version = "0.102.0" }
-cranelift-reader = { path = "cranelift/reader", version = "0.102.0" }
+cranelift-wasm = { path = "cranelift/wasm", version = "0.103.0" }
+cranelift-codegen = { path = "cranelift/codegen", version = "0.103.0", default-features = false, features = ["std", "unwind"] }
+cranelift-frontend = { path = "cranelift/frontend", version = "0.103.0" }
+cranelift-entity = { path = "cranelift/entity", version = "0.103.0" }
+cranelift-native = { path = "cranelift/native", version = "0.103.0" }
+cranelift-module = { path = "cranelift/module", version = "0.103.0" }
+cranelift-interpreter = { path = "cranelift/interpreter", version = "0.103.0" }
+cranelift-reader = { path = "cranelift/reader", version = "0.103.0" }
 cranelift-filetests = { path = "cranelift/filetests" }
-cranelift-object = { path = "cranelift/object", version = "0.102.0" }
-cranelift-jit = { path = "cranelift/jit", version = "0.102.0" }
+cranelift-object = { path = "cranelift/object", version = "0.103.0" }
+cranelift-jit = { path = "cranelift/jit", version = "0.103.0" }
 cranelift-fuzzgen = { path = "cranelift/fuzzgen" }
-cranelift-bforest = { path = "cranelift/bforest", version = "0.102.0" }
-cranelift-control = { path = "cranelift/control", version = "0.102.0" }
-cranelift = { path = "cranelift/umbrella", version = "0.102.0" }
+cranelift-bforest = { path = "cranelift/bforest", version = "0.103.0" }
+cranelift-control = { path = "cranelift/control", version = "0.103.0" }
+cranelift = { path = "cranelift/umbrella", version = "0.103.0" }
 
-winch-codegen = { path = "winch/codegen", version = "=0.13.0" }
+winch-codegen = { path = "winch/codegen", version = "=0.14.0" }
 winch-filetests = { path = "winch/filetests" }
 winch-test-macros = { path = "winch/test-macros" }
 
diff --git a/RELEASES.md b/RELEASES.md
index 1988ab0aa..246410421 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -1,5 +1,15 @@
 --------------------------------------------------------------------------------
 
+## 16.0.0
+
+Unreleased.
+
+### Added
+
+### Changed
+
+--------------------------------------------------------------------------------
+
 ## 15.0.0
 
 Unreleased.
diff --git a/cranelift/bforest/Cargo.toml b/cranelift/bforest/Cargo.toml
index f620eb757..00921a551 100644
--- a/cranelift/bforest/Cargo.toml
+++ b/cranelift/bforest/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 authors = ["The Cranelift Project Developers"]
 name = "cranelift-bforest"
-version = "0.102.0"
+version = "0.103.0"
 description = "A forest of B+-trees"
 license = "Apache-2.0 WITH LLVM-exception"
 documentation = "https://docs.rs/cranelift-bforest"
diff --git a/cranelift/codegen/Cargo.toml b/cranelift/codegen/Cargo.toml
index 3402550d0..fe7d606d1 100644
--- a/cranelift/codegen/Cargo.toml
+++ b/cranelift/codegen/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 authors = ["The Cranelift Project Developers"]
 name = "cranelift-codegen"
-version = "0.102.0"
+version = "0.103.0"
 description = "Low-level code generator library"
 license = "Apache-2.0 WITH LLVM-exception"
 documentation = "https://docs.rs/cranelift-codegen"
@@ -16,7 +16,7 @@ edition.workspace = true
 anyhow = { workspace = true, optional = true }
 bumpalo = "3"
 capstone = { workspace = true, optional = true }
-cranelift-codegen-shared = { path = "./shared", version = "0.102.0" }
+cranelift-codegen-shared = { path = "./shared", version = "0.103.0" }
 cranelift-entity = { workspace = true }
 cranelift-bforest = { workspace = true }
 cranelift-control = { workspace = true }
@@ -41,8 +41,8 @@ criterion = { version = "0.5.0", features = ["html_reports"] }
 similar = "2.1.0"
 
 [build-dependencies]
-cranelift-codegen-meta = { path = "meta", version = "0.102.0" }
-cranelift-isle = { path = "../isle/isle", version = "=0.102.0" }
+cranelift-codegen-meta = { path = "meta", version = "0.103.0" }
+cranelift-isle = { path = "../isle/isle", version = "=0.103.0" }
 
 [features]
 default = ["std", "unwind", "host-arch"]
diff --git a/cranelift/codegen/meta/Cargo.toml b/cranelift/codegen/meta/Cargo.toml
index f507cc873..ecc3d5015 100644
--- a/cranelift/codegen/meta/Cargo.toml
+++ b/cranelift/codegen/meta/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "cranelift-codegen-meta"
 authors = ["The Cranelift Project Developers"]
-version = "0.102.0"
+version = "0.103.0"
 description = "Metaprogram for cranelift-codegen code generator library"
 license = "Apache-2.0 WITH LLVM-exception"
 repository = "https://github.com/bytecodealliance/wasmtime"
@@ -12,4 +12,4 @@ edition.workspace = true
 rustdoc-args = [ "--document-private-items" ]
 
 [dependencies]
-cranelift-codegen-shared = { path = "../shared", version = "0.102.0" }
+cranelift-codegen-shared = { path = "../shared", version = "0.103.0" }
diff --git a/cranelift/codegen/shared/Cargo.toml b/cranelift/codegen/shared/Cargo.toml
index d68f66091..9187f8382 100644
--- a/cranelift/codegen/shared/Cargo.toml
+++ b/cranelift/codegen/shared/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 authors = ["The Cranelift Project Developers"]
 name = "cranelift-codegen-shared"
-version = "0.102.0"
+version = "0.103.0"
 description = "For code shared between cranelift-codegen-meta and cranelift-codegen"
 license = "Apache-2.0 WITH LLVM-exception"
 repository = "https://github.com/bytecodealliance/wasmtime"
diff --git a/cranelift/control/Cargo.toml b/cranelift/control/Cargo.toml
index 08b5d0ece..2d7856581 100644
--- a/cranelift/control/Cargo.toml
+++ b/cranelift/control/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 authors = ["The Cranelift Project Developers"]
 name = "cranelift-control"
-version = "0.102.0"
+version = "0.103.0"
 description = "White-box fuzz testing framework"
 license = "Apache-2.0 WITH LLVM-exception"
 repository = "https://github.com/bytecodealliance/wasmtime"
diff --git a/cranelift/entity/Cargo.toml b/cranelift/entity/Cargo.toml
index c0f08dde9..463bf4ab7 100644
--- a/cranelift/entity/Cargo.toml
+++ b/cranelift/entity/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 authors = ["The Cranelift Project Developers"]
 name = "cranelift-entity"
-version = "0.102.0"
+version = "0.103.0"
 description = "Data structures using entity references as mapping keys"
 license = "Apache-2.0 WITH LLVM-exception"
 documentation = "https://docs.rs/cranelift-entity"
diff --git a/cranelift/frontend/Cargo.toml b/cranelift/frontend/Cargo.toml
index 7c804f44c..01311566c 100644
--- a/cranelift/frontend/Cargo.toml
+++ b/cranelift/frontend/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 authors = ["The Cranelift Project Developers"]
 name = "cranelift-frontend"
-version = "0.102.0"
+version = "0.103.0"
 description = "Cranelift IR builder helper"
 license = "Apache-2.0 WITH LLVM-exception"
 documentation = "https://docs.rs/cranelift-frontend"
diff --git a/cranelift/interpreter/Cargo.toml b/cranelift/interpreter/Cargo.toml
index 728148e49..6c22f61a1 100644
--- a/cranelift/interpreter/Cargo.toml
+++ b/cranelift/interpreter/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "cranelift-interpreter"
-version = "0.102.0"
+version = "0.103.0"
 authors = ["The Cranelift Project Developers"]
 description = "Interpret Cranelift IR"
 repository = "https://github.com/bytecodealliance/wasmtime"
diff --git a/cranelift/isle/isle/Cargo.toml b/cranelift/isle/isle/Cargo.toml
index 44d555583..078787068 100644
--- a/cranelift/isle/isle/Cargo.toml
+++ b/cranelift/isle/isle/Cargo.toml
@@ -6,7 +6,7 @@ license = "Apache-2.0 WITH LLVM-exception"
 name = "cranelift-isle"
 readme = "../README.md"
 repository = "https://github.com/bytecodealliance/wasmtime/tree/main/cranelift/isle"
-version = "0.102.0"
+version = "0.103.0"
 
 [dependencies]
 codespan-reporting = { version = "0.11.1", optional = true }
diff --git a/cranelift/jit/Cargo.toml b/cranelift/jit/Cargo.toml
index eb137461b..05f311456 100644
--- a/cranelift/jit/Cargo.toml
+++ b/cranelift/jit/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "cranelift-jit"
-version = "0.102.0"
+version = "0.103.0"
 authors = ["The Cranelift Project Developers"]
 description = "A JIT library backed by Cranelift"
 repository = "https://github.com/bytecodealliance/wasmtime"
diff --git a/cranelift/module/Cargo.toml b/cranelift/module/Cargo.toml
index ad92ca3e5..e3598dba4 100644
--- a/cranelift/module/Cargo.toml
+++ b/cranelift/module/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "cranelift-module"
-version = "0.102.0"
+version = "0.103.0"
 authors = ["The Cranelift Project Developers"]
 description = "Support for linking functions and data with Cranelift"
 repository = "https://github.com/bytecodealliance/wasmtime"
diff --git a/cranelift/native/Cargo.toml b/cranelift/native/Cargo.toml
index e9410c36e..7cb7d2f56 100644
--- a/cranelift/native/Cargo.toml
+++ b/cranelift/native/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "cranelift-native"
-version = "0.102.0"
+version = "0.103.0"
 authors = ["The Cranelift Project Developers"]
 description = "Support for targeting the host with Cranelift"
 documentation = "https://docs.rs/cranelift-native"
diff --git a/cranelift/object/Cargo.toml b/cranelift/object/Cargo.toml
index 33acc617c..08952bec3 100644
--- a/cranelift/object/Cargo.toml
+++ b/cranelift/object/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "cranelift-object"
-version = "0.102.0"
+version = "0.103.0"
 authors = ["The Cranelift Project Developers"]
 description = "Emit Cranelift output to native object files with `object`"
 repository = "https://github.com/bytecodealliance/wasmtime"
diff --git a/cranelift/reader/Cargo.toml b/cranelift/reader/Cargo.toml
index b45218fa0..3140fa142 100644
--- a/cranelift/reader/Cargo.toml
+++ b/cranelift/reader/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 authors = ["The Cranelift Project Developers"]
 name = "cranelift-reader"
-version = "0.102.0"
+version = "0.103.0"
 description = "Cranelift textual IR reader"
 license = "Apache-2.0 WITH LLVM-exception"
 documentation = "https://docs.rs/cranelift-reader"
diff --git a/cranelift/serde/Cargo.toml b/cranelift/serde/Cargo.toml
index c2e10d7ce..d7cc7f7c3 100644
--- a/cranelift/serde/Cargo.toml
+++ b/cranelift/serde/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "cranelift-serde"
-version = "0.102.0"
+version = "0.103.0"
 authors = ["The Cranelift Project Developers"]
 description = "Serializer/Deserializer for Cranelift IR"
 repository = "https://github.com/bytecodealliance/wasmtime"
diff --git a/cranelift/umbrella/Cargo.toml b/cranelift/umbrella/Cargo.toml
index 4c8409971..eabeafd6f 100644
--- a/cranelift/umbrella/Cargo.toml
+++ b/cranelift/umbrella/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 authors = ["The Cranelift Project Developers"]
 name = "cranelift"
-version = "0.102.0"
+version = "0.103.0"
 description = "Umbrella for commonly-used cranelift crates"
 license = "Apache-2.0 WITH LLVM-exception"
 documentation = "https://docs.rs/cranelift"
diff --git a/cranelift/wasm/Cargo.toml b/cranelift/wasm/Cargo.toml
index b4b98d5a6..35fd46c76 100644
--- a/cranelift/wasm/Cargo.toml
+++ b/cranelift/wasm/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "cranelift-wasm"
-version = "0.102.0"
+version = "0.103.0"
 authors = ["The Cranelift Project Developers"]
 description = "Translator from WebAssembly to Cranelift IR"
 documentation = "https://docs.rs/cranelift-wasm"
diff --git a/crates/c-api/include/wasmtime.h b/crates/c-api/include/wasmtime.h
index ce7d46e0f..50d5ec8d1 100644
--- a/crates/c-api/include/wasmtime.h
+++ b/crates/c-api/include/wasmtime.h
@@ -202,11 +202,11 @@
 /**
  * \brief Wasmtime version string.
  */
-#define WASMTIME_VERSION "15.0.0"
+#define WASMTIME_VERSION "16.0.0"
 /**
  * \brief Wasmtime major version number.
  */
-#define WASMTIME_VERSION_MAJOR 15
+#define WASMTIME_VERSION_MAJOR 16
 /**
  * \brief Wasmtime minor version number.
  */
diff --git a/crates/wasi-http/wit/deps/sockets/tcp.wit b/crates/wasi-http/wit/deps/sockets/tcp.wit
index b9073b775..b9ef0625a 100644
--- a/crates/wasi-http/wit/deps/sockets/tcp.wit
+++ b/crates/wasi-http/wit/deps/sockets/tcp.wit
@@ -81,7 +81,7 @@ interface tcp {
         /// - `connection-refused`:        The connection was forcefully rejected. (ECONNREFUSED)
         /// - `connection-reset`:          The connection was reset. (ECONNRESET)
         /// - `connection-aborted`:        The connection was aborted. (ECONNABORTED)
-        /// - `remote-unreachable`:        The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN)
+        /// - `remote-unreachable`:        The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET)
         /// - `address-in-use`:            Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD)
         /// - `not-in-progress`:           A `connect` operation is not in progress.
         /// - `would-block`:               Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN)
@@ -126,7 +126,6 @@ interface tcp {
         /// - `address-family`
         /// - `ipv6-only`
         /// - `keep-alive`
-        /// - `no-delay`
         /// - `unicast-hop-limit`
         /// - `receive-buffer-size`
         /// - `send-buffer-size`
@@ -204,12 +203,6 @@ interface tcp {
         keep-alive: func() -> result<bool, error-code>;
         set-keep-alive: func(value: bool) -> result<_, error-code>;
 
-        /// Equivalent to the TCP_NODELAY socket option.
-        ///
-        /// The default value is `false`.
-        no-delay: func() -> result<bool, error-code>;
-        set-no-delay: func(value: bool) -> result<_, error-code>;
-
         /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options.
         ///
         /// # Typical errors
diff --git a/crates/wasi-http/wit/deps/sockets/udp.wit b/crates/wasi-http/wit/deps/sockets/udp.wit
index 232c2b41c..1ee1843a9 100644
--- a/crates/wasi-http/wit/deps/sockets/udp.wit
+++ b/crates/wasi-http/wit/deps/sockets/udp.wit
@@ -97,7 +97,8 @@ interface udp {
         /// - `invalid-argument`:          The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL)
         /// - `invalid-state`:             The socket is not bound.
         /// - `address-in-use`:            Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD)
-        /// - `remote-unreachable`:        The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN)
+        /// - `remote-unreachable`:        The remote address is not reachable. (ECONNRESET, ENETRESET, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET)
+        /// - `connection-refused`:        The connection was refused. (ECONNREFUSED)
         ///
         /// # References
         /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html>
@@ -190,7 +191,8 @@ interface udp {
         /// This function never returns `error(would-block)`.
         ///
         /// # Typical errors
-        /// - `remote-unreachable`: The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN)
+        /// - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET)
+        /// - `connection-refused`: The connection was refused. (ECONNREFUSED)
         ///
         /// # References
         /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvfrom.html>
@@ -246,7 +248,8 @@ interface udp {
         /// - `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)
         /// - `invalid-argument`:        The socket is not "connected" and no value for `remote-address` was provided. (EDESTADDRREQ)
-        /// - `remote-unreachable`:      The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN)
+        /// - `remote-unreachable`:      The remote address is not reachable. (ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET)
+        /// - `connection-refused`:      The connection was refused. (ECONNREFUSED)
         /// - `datagram-too-large`:      The datagram is too large. (EMSGSIZE)
         ///
         /// # References
diff --git a/crates/wasi/src/preview2/host/network.rs b/crates/wasi/src/preview2/host/network.rs
index aa01cb4f4..5233149f4 100644
--- a/crates/wasi/src/preview2/host/network.rs
+++ b/crates/wasi/src/preview2/host/network.rs
@@ -220,3 +220,251 @@ impl From<cap_net_ext::AddressFamily> for IpAddressFamily {
         }
     }
 }
+
+pub(crate) mod util {
+    use std::net::{IpAddr, Ipv6Addr, SocketAddr};
+
+    use crate::preview2::bindings::sockets::network::ErrorCode;
+    use crate::preview2::network::SocketAddressFamily;
+    use crate::preview2::SocketResult;
+    use cap_net_ext::{Blocking, TcpBinder, TcpConnecter, TcpListenerExt, UdpBinder};
+    use cap_std::net::{TcpListener, TcpStream, UdpSocket};
+    use rustix::fd::AsFd;
+    use rustix::io::Errno;
+    use rustix::net::sockopt;
+
+    pub fn validate_unicast(addr: &SocketAddr) -> SocketResult<()> {
+        match to_canonical(&addr.ip()) {
+            IpAddr::V4(ipv4) => {
+                if ipv4.is_multicast() || ipv4.is_broadcast() {
+                    Err(ErrorCode::InvalidArgument.into())
+                } else {
+                    Ok(())
+                }
+            }
+            IpAddr::V6(ipv6) => {
+                if ipv6.is_multicast() {
+                    Err(ErrorCode::InvalidArgument.into())
+                } else {
+                    Ok(())
+                }
+            }
+        }
+    }
+
+    pub fn validate_remote_address(addr: &SocketAddr) -> SocketResult<()> {
+        if to_canonical(&addr.ip()).is_unspecified() {
+            return Err(ErrorCode::InvalidArgument.into());
+        }
+
+        if addr.port() == 0 {
+            return Err(ErrorCode::InvalidArgument.into());
+        }
+
+        Ok(())
+    }
+
+    pub fn validate_address_family(
+        addr: &SocketAddr,
+        socket_family: &SocketAddressFamily,
+    ) -> SocketResult<()> {
+        match (socket_family, addr.ip()) {
+            (SocketAddressFamily::Ipv4, IpAddr::V4(_)) => Ok(()),
+            (SocketAddressFamily::Ipv6 { v6only }, 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() {
+                    Err(ErrorCode::InvalidArgument.into())
+                } else {
+                    Ok(())
+                }
+            }
+            _ => Err(ErrorCode::InvalidArgument.into()),
+        }
+    }
+
+    // Can be removed once `IpAddr::to_canonical` becomes stable.
+    fn to_canonical(addr: &IpAddr) -> IpAddr {
+        match addr {
+            IpAddr::V4(ipv4) => IpAddr::V4(*ipv4),
+            IpAddr::V6(ipv6) => {
+                if let Some(ipv4) = ipv6.to_ipv4_mapped() {
+                    IpAddr::V4(ipv4)
+                } else {
+                    IpAddr::V6(*ipv6)
+                }
+            }
+        }
+    }
+
+    fn is_deprecated_ipv4_compatible(addr: &Ipv6Addr) -> bool {
+        matches!(addr.segments(), [0, 0, 0, 0, 0, 0, _, _])
+            && *addr != Ipv6Addr::UNSPECIFIED
+            && *addr != Ipv6Addr::LOCALHOST
+    }
+
+    /*
+     * Syscalls wrappers with (opinionated) portability fixes.
+     */
+
+    pub fn tcp_bind(listener: &TcpListener, binder: &TcpBinder) -> std::io::Result<()> {
+        binder
+            .bind_existing_tcp_listener(listener)
+            .map_err(|error| match Errno::from_io_error(&error) {
+                #[cfg(windows)]
+                Some(Errno::NOBUFS) => Errno::ADDRINUSE.into(), // Windows returns WSAENOBUFS when the ephemeral ports have been exhausted.
+                _ => error,
+            })
+    }
+
+    pub fn udp_bind(socket: &UdpSocket, binder: &UdpBinder) -> std::io::Result<()> {
+        binder.bind_existing_udp_socket(socket).map_err(|error| {
+            match Errno::from_io_error(&error) {
+                #[cfg(windows)]
+                Some(Errno::NOBUFS) => Errno::ADDRINUSE.into(), // Windows returns WSAENOBUFS when the ephemeral ports have been exhausted.
+                _ => error,
+            }
+        })
+    }
+
+    pub fn tcp_connect(listener: &TcpListener, connecter: &TcpConnecter) -> std::io::Result<()> {
+        connecter.connect_existing_tcp_listener(listener).map_err(
+            |error| match Errno::from_io_error(&error) {
+                // On POSIX, non-blocking `connect` returns `EINPROGRESS`. Windows returns `WSAEWOULDBLOCK`.
+                #[cfg(windows)]
+                Some(Errno::WOULDBLOCK) => Errno::INPROGRESS.into(),
+                _ => error,
+            },
+        )
+    }
+
+    pub fn tcp_accept(
+        listener: &TcpListener,
+        blocking: Blocking,
+    ) -> std::io::Result<(TcpStream, SocketAddr)> {
+        listener
+            .accept_with(blocking)
+            .map_err(|error| match Errno::from_io_error(&error) {
+                #[cfg(windows)]
+                Some(Errno::INPROGRESS) => Errno::INTR.into(), // "A blocking Windows Sockets 1.1 call is in progress, or the service provider is still processing a callback function."
+
+                // Normalize Linux' non-standard behavior.
+                // "Linux accept() passes already-pending network errors on the new socket as an error code from accept(). This behavior differs from other BSD socket implementations."
+                #[cfg(target_os = "linux")]
+                Some(
+                    Errno::CONNRESET
+                    | Errno::NETRESET
+                    | Errno::HOSTUNREACH
+                    | Errno::HOSTDOWN
+                    | Errno::NETDOWN
+                    | Errno::NETUNREACH
+                    | Errno::PROTO
+                    | Errno::NOPROTOOPT
+                    | Errno::NONET
+                    | Errno::OPNOTSUPP,
+                ) => Errno::CONNABORTED.into(),
+
+                _ => error,
+            })
+    }
+
+    pub fn udp_disconnect<Fd: AsFd>(sockfd: Fd) -> rustix::io::Result<()> {
+        match rustix::net::connect_unspec(sockfd) {
+            // BSD platforms return an error even if the UDP socket was disconnected successfully.
+            #[cfg(target_os = "macos")]
+            Err(Errno::INVAL | Errno::AFNOSUPPORT) => Ok(()),
+            r => r,
+        }
+    }
+
+    pub fn get_ip_ttl<Fd: AsFd>(sockfd: Fd) -> rustix::io::Result<u8> {
+        sockopt::get_ip_ttl(sockfd)?
+            .try_into()
+            .map_err(|_| Errno::OPNOTSUPP)
+    }
+
+    pub fn get_ipv6_unicast_hops<Fd: AsFd>(sockfd: Fd) -> rustix::io::Result<u8> {
+        sockopt::get_ipv6_unicast_hops(sockfd)
+    }
+
+    pub fn set_ip_ttl<Fd: AsFd>(sockfd: Fd, value: u8) -> rustix::io::Result<()> {
+        match value {
+            // A well-behaved IP application should never send out new packets with TTL 0.
+            // We validate the value ourselves because OS'es are not consistent in this.
+            // On Linux the validation is even inconsistent between their IPv4 and IPv6 implementation.
+            0 => Err(Errno::INVAL),
+            _ => sockopt::set_ip_ttl(sockfd, value.into()),
+        }
+    }
+
+    pub fn set_ipv6_unicast_hops<Fd: AsFd>(sockfd: Fd, value: u8) -> rustix::io::Result<()> {
+        match value {
+            // A well-behaved IP application should never send out new packets with TTL 0.
+            // We validate the value ourselves because OS'es are not consistent in this.
+            // On Linux the validation is even inconsistent between their IPv4 and IPv6 implementation.
+            0 => Err(Errno::INVAL),
+            _ => sockopt::set_ipv6_unicast_hops(sockfd, Some(value)),
+        }
+    }
+
+    fn normalize_get_buffer_size(value: usize) -> usize {
+        if cfg!(target_os = "linux") {
+            // Linux doubles the value passed to setsockopt to allow space for bookkeeping overhead.
+            // getsockopt returns this internally doubled value.
+            // We'll half the value to at least get it back into the same ballpark that the application requested it in.
+            value / 2
+        } else {
+            value
+        }
+    }
+
+    fn normalize_set_buffer_size(value: usize) -> usize {
+        value.clamp(1, i32::MAX as usize)
+    }
+
+    pub fn get_socket_recv_buffer_size<Fd: AsFd>(sockfd: Fd) -> rustix::io::Result<usize> {
+        let value = sockopt::get_socket_recv_buffer_size(sockfd)?;
+        Ok(normalize_get_buffer_size(value))
+    }
+
+    pub fn get_socket_send_buffer_size<Fd: AsFd>(sockfd: Fd) -> rustix::io::Result<usize> {
+        let value = sockopt::get_socket_send_buffer_size(sockfd)?;
+        Ok(normalize_get_buffer_size(value))
+    }
+
+    pub fn set_socket_recv_buffer_size<Fd: AsFd>(
+        sockfd: Fd,
+        value: usize,
+    ) -> rustix::io::Result<()> {
+        let value = normalize_set_buffer_size(value);
+
+        match sockopt::set_socket_recv_buffer_size(sockfd, value) {
+            // Most platforms (Linux, Windows, Fuchsia, Solaris, Illumos, Haiku, ESP-IDF, ..and more?) treat the value
+            // passed to SO_SNDBUF/SO_RCVBUF as a performance tuning hint and silently clamp the input if it exceeds
+            // their capability.
+            // As far as I can see, only the *BSD family views this option as a hard requirement and fails when the
+            // value is out of range. We normalize this behavior in favor of the more commonly understood
+            // "performance hint" semantics. In other words; even ENOBUFS is "Ok".
+            // A future improvement could be to query the corresponding sysctl on *BSD platforms and clamp the input
+            // `size` ourselves, to completely close the gap with other platforms.
+            Err(Errno::NOBUFS) => Ok(()),
+            r => r,
+        }
+    }
+
+    pub fn set_socket_send_buffer_size<Fd: AsFd>(
+        sockfd: Fd,
+        value: usize,
+    ) -> rustix::io::Result<()> {
+        let value = normalize_set_buffer_size(value);
+
+        match sockopt::set_socket_send_buffer_size(sockfd, value) {
+            Err(Errno::NOBUFS) => Ok(()), // See set_socket_recv_buffer_size
+            r => r,
+        }
+    }
+}
diff --git a/crates/wasi/src/preview2/host/tcp.rs b/crates/wasi/src/preview2/host/tcp.rs
index f15fccd73..665b2a85a 100644
--- a/crates/wasi/src/preview2/host/tcp.rs
+++ b/crates/wasi/src/preview2/host/tcp.rs
@@ -1,3 +1,4 @@
+use crate::preview2::host::network::util;
 use crate::preview2::tcp::{TcpSocket, TcpState};
 use crate::preview2::{
     bindings::{
@@ -5,7 +6,7 @@ use crate::preview2::{
         sockets::network::{ErrorCode, IpAddressFamily, IpSocketAddress, Network},
         sockets::tcp::{self, ShutdownType},
     },
-    tcp::SocketAddressFamily,
+    network::SocketAddressFamily,
 };
 use crate::preview2::{Pollable, SocketResult, WasiView};
 use cap_net_ext::{Blocking, PoolExt, TcpListenerExt};
@@ -13,7 +14,7 @@ use cap_std::net::TcpListener;
 use io_lifetimes::AsSocketlike;
 use rustix::io::Errno;
 use rustix::net::sockopt;
-use std::net::{IpAddr, Ipv6Addr, SocketAddr};
+use std::net::SocketAddr;
 use tokio::io::Interest;
 use wasmtime::component::Resource;
 
@@ -37,20 +38,21 @@ impl<T: WasiView> crate::preview2::host::tcp::tcp::HostTcpSocket for T {
             _ => return Err(ErrorCode::InvalidState.into()),
         }
 
-        validate_unicast(&local_address)?;
-        validate_address_family(&socket, &local_address)?;
+        util::validate_unicast(&local_address)?;
+        util::validate_address_family(&local_address, &socket.family)?;
 
-        let binder = network.pool.tcp_binder(local_address)?;
-
-        // Perform the OS bind call.
-        binder
-            .bind_existing_tcp_listener(&*socket.tcp_socket().as_socketlike_view::<TcpListener>())
-            .map_err(|error| match Errno::from_io_error(&error) {
-                Some(Errno::AFNOSUPPORT) => ErrorCode::InvalidArgument, // Just in case our own validations weren't sufficient.
-                #[cfg(windows)]
-                Some(Errno::NOBUFS) => ErrorCode::AddressInUse, // Windows returns WSAENOBUFS when the ephemeral ports have been exhausted.
-                _ => ErrorCode::from(error),
+        {
+            let binder = network.pool.tcp_binder(local_address)?;
+            let listener = &*socket.tcp_socket().as_socketlike_view::<TcpListener>();
+
+            // Perform the OS bind call.
+            util::tcp_bind(listener, &binder).map_err(|error| {
+                match Errno::from_io_error(&error) {
+                    Some(Errno::AFNOSUPPORT) => ErrorCode::InvalidArgument, // Just in case our own validations weren't sufficient.
+                    _ => ErrorCode::from(error),
+                }
             })?;
+        }
 
         let socket = table.get_mut(&this)?;
         socket.tcp_state = TcpState::BindStarted;
@@ -96,18 +98,15 @@ impl<T: WasiView> crate::preview2::host::tcp::tcp::HostTcpSocket for T {
                 | TcpState::BindStarted => return Err(ErrorCode::ConcurrencyConflict.into()),
             }
 
-            validate_unicast(&remote_address)?;
-            validate_remote_address(&remote_address)?;
-            validate_address_family(&socket, &remote_address)?;
+            util::validate_unicast(&remote_address)?;
+            util::validate_remote_address(&remote_address)?;
+            util::validate_address_family(&remote_address, &socket.family)?;
 
             let connecter = network.pool.tcp_connecter(remote_address)?;
+            let listener = &*socket.tcp_socket().as_socketlike_view::<TcpListener>();
 
             // Do an OS `connect`. Our socket is non-blocking, so it'll either...
-            {
-                let view = &*socket.tcp_socket().as_socketlike_view::<TcpListener>();
-                let r = connecter.connect_existing_tcp_listener(view);
-                r
-            }
+            util::tcp_connect(listener, &connecter)
         };
 
         match r {
@@ -118,7 +117,7 @@ impl<T: WasiView> crate::preview2::host::tcp::tcp::HostTcpSocket for T {
                 return Ok(());
             }
             // continue in progress,
-            Err(err) if Errno::from_io_error(&err) == Some(INPROGRESS) => {}
+            Err(err) if Errno::from_io_error(&err) == Some(Errno::INPROGRESS) => {}
             // or fail immediately.
             Err(err) => {
                 return Err(match Errno::from_io_error(&err) {
@@ -241,34 +240,10 @@ impl<T: WasiView> crate::preview2::host::tcp::tcp::HostTcpSocket for T {
 
         // Do the OS accept call.
         let tcp_socket = socket.tcp_socket();
-        let (connection, _addr) = tcp_socket
-            .try_io(Interest::READABLE, || {
-                tcp_socket
-                    .as_socketlike_view::<TcpListener>()
-                    .accept_with(Blocking::No)
-            })
-            .map_err(|error| match Errno::from_io_error(&error) {
-                #[cfg(windows)]
-                Some(Errno::INPROGRESS) => ErrorCode::WouldBlock, // "A blocking Windows Sockets 1.1 call is in progress, or the service provider is still processing a callback function."
-
-                // Normalize Linux' non-standard behavior.
-                // "Linux accept() passes already-pending network errors on the new socket as an error code from accept(). This behavior differs from other BSD socket implementations."
-                #[cfg(target_os = "linux")]
-                Some(
-                    Errno::CONNRESET
-                    | Errno::NETRESET
-                    | Errno::HOSTUNREACH
-                    | Errno::HOSTDOWN
-                    | Errno::NETDOWN
-                    | Errno::NETUNREACH
-                    | Errno::PROTO
-                    | Errno::NOPROTOOPT
-                    | Errno::NONET
-                    | Errno::OPNOTSUPP,
-                ) => ErrorCode::ConnectionAborted,
-
-                _ => ErrorCode::from(error),
-            })?;
+        let (connection, _addr) = tcp_socket.try_io(Interest::READABLE, || {
+            let listener = &*tcp_socket.as_socketlike_view::<TcpListener>();
+            util::tcp_accept(listener, Blocking::No)
+        })?;
 
         #[cfg(target_os = "macos")]
         {
@@ -277,18 +252,17 @@ impl<T: WasiView> crate::preview2::host::tcp::tcp::HostTcpSocket for T {
             // and only if a specific value was explicitly set on the listener.
 
             if let Some(size) = socket.receive_buffer_size {
-                _ = sockopt::set_socket_recv_buffer_size(&connection, size); // Ignore potential error.
+                _ = util::set_socket_recv_buffer_size(&connection, size); // Ignore potential error.
             }
 
             if let Some(size) = socket.send_buffer_size {
-                _ = sockopt::set_socket_send_buffer_size(&connection, size); // Ignore potential error.
+                _ = util::set_socket_send_buffer_size(&connection, size); // Ignore potential error.
             }
 
             // For some reason, IP_TTL is inherited, but IPV6_UNICAST_HOPS isn't.
             if let (SocketAddressFamily::Ipv6 { .. }, Some(ttl)) = (socket.family, socket.hop_limit)
             {
-                _ = sockopt::set_ipv6_unicast_hops(&connection, Some(ttl));
-                // Ignore potential error.
+                _ = util::set_ipv6_unicast_hops(&connection, ttl); // Ignore potential error.
             }
         }
 
@@ -330,6 +304,9 @@ impl<T: WasiView> crate::preview2::host::tcp::tcp::HostTcpSocket for T {
 
         match socket.tcp_state {
             TcpState::Connected => {}
+            TcpState::Connecting | TcpState::ConnectReady => {
+                return Err(ErrorCode::ConcurrencyConflict.into())
+            }
             _ => return Err(ErrorCode::InvalidState.into()),
         }
 
@@ -438,29 +415,13 @@ impl<T: WasiView> crate::preview2::host::tcp::tcp::HostTcpSocket for T {
         Ok(sockopt::set_socket_keepalive(socket.tcp_socket(), value)?)
     }
 
-    fn no_delay(&mut self, this: Resource<tcp::TcpSocket>) -> SocketResult<bool> {
-        let table = self.table();
-        let socket = table.get(&this)?;
-        Ok(sockopt::get_tcp_nodelay(socket.tcp_socket())?)
-    }
-
-    fn set_no_delay(&mut self, this: Resource<tcp::TcpSocket>, value: bool) -> SocketResult<()> {
-        let table = self.table();
-        let socket = table.get(&this)?;
-        Ok(sockopt::set_tcp_nodelay(socket.tcp_socket(), value)?)
-    }
-
     fn unicast_hop_limit(&mut self, this: Resource<tcp::TcpSocket>) -> SocketResult<u8> {
         let table = self.table();
         let socket = table.get(&this)?;
 
         let ttl = match socket.family {
-            SocketAddressFamily::Ipv4 => sockopt::get_ip_ttl(socket.tcp_socket())?
-                .try_into()
-                .unwrap(),
-            SocketAddressFamily::Ipv6 { .. } => {
-                sockopt::get_ipv6_unicast_hops(socket.tcp_socket())?
-            }
+            SocketAddressFamily::Ipv4 => util::get_ip_ttl(socket.tcp_socket())?,
+            SocketAddressFamily::Ipv6 { .. } => util::get_ipv6_unicast_hops(socket.tcp_socket())?,
         };
 
         Ok(ttl)
@@ -474,17 +435,10 @@ impl<T: WasiView> crate::preview2::host::tcp::tcp::HostTcpSocket for T {
         let table = self.table_mut();
         let socket = table.get_mut(&this)?;
 
-        if value == 0 {
-            // A well-behaved IP application should never send out new packets with TTL 0.
-            // We validate the value ourselves because OS'es are not consistent in this.
-            // On Linux the validation is even inconsistent between their IPv4 and IPv6 implementation.
-            return Err(ErrorCode::InvalidArgument.into());
-        }
-
         match socket.family {
-            SocketAddressFamily::Ipv4 => sockopt::set_ip_ttl(socket.tcp_socket(), value.into())?,
+            SocketAddressFamily::Ipv4 => util::set_ip_ttl(socket.tcp_socket(), value)?,
             SocketAddressFamily::Ipv6 { .. } => {
-                sockopt::set_ipv6_unicast_hops(socket.tcp_socket(), Some(value))?
+                util::set_ipv6_unicast_hops(socket.tcp_socket(), value)?
             }
         }
 
@@ -500,8 +454,8 @@ impl<T: WasiView> crate::preview2::host::tcp::tcp::HostTcpSocket for T {
         let table = self.table();
         let socket = table.get(&this)?;
 
-        let value = sockopt::get_socket_recv_buffer_size(socket.tcp_socket())? as u64;
-        Ok(normalize_getsockopt_buffer_size(value))
+        let value = util::get_socket_recv_buffer_size(socket.tcp_socket())?;
+        Ok(value as u64)
     }
 
     fn set_receive_buffer_size(
@@ -511,20 +465,9 @@ impl<T: WasiView> crate::preview2::host::tcp::tcp::HostTcpSocket for T {
     ) -> SocketResult<()> {
         let table = self.table_mut();
         let socket = table.get_mut(&this)?;
-        let value = normalize_setsockopt_buffer_size(value);
-
-        match sockopt::set_socket_recv_buffer_size(socket.tcp_socket(), value) {
-            // Most platforms (Linux, Windows, Fuchsia, Solaris, Illumos, Haiku, ESP-IDF, ..and more?) treat the value
-            // passed to SO_SNDBUF/SO_RCVBUF as a performance tuning hint and silently clamp the input if it exceeds
-            // their capability.
-            // As far as I can see, only the *BSD family views this option as a hard requirement and fails when the
-            // value is out of range. We normalize this behavior in favor of the more commonly understood
-            // "performance hint" semantics. In other words; even ENOBUFS is "Ok".
-            // A future improvement could be to query the corresponding sysctl on *BSD platforms and clamp the input
-            // `size` ourselves, to completely close the gap with other platforms.
-            Err(Errno::NOBUFS) => Ok(()),
-            r => r,
-        }?;
+        let value = value.try_into().unwrap_or(usize::MAX);
+
+        util::set_socket_recv_buffer_size(socket.tcp_socket(), value)?;
 
         #[cfg(target_os = "macos")]
         {
@@ -538,8 +481,8 @@ impl<T: WasiView> crate::preview2::host::tcp::tcp::HostTcpSocket for T {
         let table = self.table();
         let socket = table.get(&this)?;
 
-        let value = sockopt::get_socket_send_buffer_size(socket.tcp_socket())? as u64;
-        Ok(normalize_getsockopt_buffer_size(value))
+        let value = util::get_socket_send_buffer_size(socket.tcp_socket())?;
+        Ok(value as u64)
     }
 
     fn set_send_buffer_size(
@@ -549,12 +492,9 @@ impl<T: WasiView> crate::preview2::host::tcp::tcp::HostTcpSocket for T {
     ) -> SocketResult<()> {
         let table = self.table_mut();
         let socket = table.get_mut(&this)?;
-        let value = normalize_setsockopt_buffer_size(value);
+        let value = value.try_into().unwrap_or(usize::MAX);
 
-        match sockopt::set_socket_send_buffer_size(socket.tcp_socket(), value) {
-            Err(Errno::NOBUFS) => Ok(()), // See `set_receive_buffer_size`
-            r => r,
-        }?;
+        util::set_socket_send_buffer_size(socket.tcp_socket(), value)?;
 
         #[cfg(target_os = "macos")]
         {
@@ -608,99 +548,3 @@ impl<T: WasiView> crate::preview2::host::tcp::tcp::HostTcpSocket for T {
         Ok(())
     }
 }
-
-// On POSIX, non-blocking TCP socket `connect` uses `EINPROGRESS`.
-// <https://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html>
-#[cfg(not(windows))]
-const INPROGRESS: Errno = Errno::INPROGRESS;
-
-// On Windows, non-blocking TCP socket `connect` uses `WSAEWOULDBLOCK`.
-// <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-connect>
-#[cfg(windows)]
-const INPROGRESS: Errno = Errno::WOULDBLOCK;
-
-fn validate_unicast(addr: &SocketAddr) -> SocketResult<()> {
-    match to_canonical(&addr.ip()) {
-        IpAddr::V4(ipv4) => {
-            if ipv4.is_multicast() || ipv4.is_broadcast() {
-                Err(ErrorCode::InvalidArgument.into())
-            } else {
-                Ok(())
-            }
-        }
-        IpAddr::V6(ipv6) => {
-            if ipv6.is_multicast() {
-                Err(ErrorCode::InvalidArgument.into())
-            } else {
-                Ok(())
-            }
-        }
-    }
-}
-
-fn validate_remote_address(addr: &SocketAddr) -> SocketResult<()> {
-    if to_canonical(&addr.ip()).is_unspecified() {
-        return Err(ErrorCode::InvalidArgument.into());
-    }
-
-    if addr.port() == 0 {
-        return Err(ErrorCode::InvalidArgument.into());
-    }
-
-    Ok(())
-}
-
-fn validate_address_family(socket: &TcpSocket, addr: &SocketAddr) -> SocketResult<()> {
-    match (socket.family, addr.ip()) {
-        (SocketAddressFamily::Ipv4, IpAddr::V4(_)) => Ok(()),
-        (SocketAddressFamily::Ipv6 { v6only }, 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() {
-                Err(ErrorCode::InvalidArgument.into())
-            } else {
-                Ok(())
-            }
-        }
-        _ => Err(ErrorCode::InvalidArgument.into()),
-    }
-}
-
-// Can be removed once `IpAddr::to_canonical` becomes stable.
-fn to_canonical(addr: &IpAddr) -> IpAddr {
-    match addr {
-        IpAddr::V4(ipv4) => IpAddr::V4(*ipv4),
-        IpAddr::V6(ipv6) => {
-            if let Some(ipv4) = ipv6.to_ipv4_mapped() {
-                IpAddr::V4(ipv4)
-            } else {
-                IpAddr::V6(*ipv6)
-            }
-        }
-    }
-}
-
-fn is_deprecated_ipv4_compatible(addr: &Ipv6Addr) -> bool {
-    matches!(addr.segments(), [0, 0, 0, 0, 0, 0, _, _])
-        && *addr != Ipv6Addr::UNSPECIFIED
-        && *addr != Ipv6Addr::LOCALHOST
-}
-
-fn normalize_setsockopt_buffer_size(value: u64) -> usize {
-    value.clamp(1, i32::MAX as u64).try_into().unwrap()
-}
-
-fn normalize_getsockopt_buffer_size(value: u64) -> u64 {
-    if cfg!(target_os = "linux") {
-        // Linux doubles the value passed to setsockopt to allow space for bookkeeping overhead.
-        // getsockopt returns this internally doubled value.
-        // We'll half the value to at least get it back into the same ballpark that the application requested it in.
-        value / 2
-    } else {
-        value
-    }
-}
diff --git a/crates/wasi/src/preview2/host/udp.rs b/crates/wasi/src/preview2/host/udp.rs
index e2d1e239f..52a796f33 100644
--- a/crates/wasi/src/preview2/host/udp.rs
+++ b/crates/wasi/src/preview2/host/udp.rs
@@ -1,5 +1,5 @@
-use std::net::SocketAddr;
-
+use crate::preview2::host::network::util;
+use crate::preview2::network::SocketAddressFamily;
 use crate::preview2::{
     bindings::{
         sockets::network::{ErrorCode, IpAddressFamily, IpSocketAddress, Network},
@@ -11,10 +11,11 @@ use crate::preview2::{
 use crate::preview2::{Pollable, SocketError, SocketResult, WasiView};
 use anyhow::anyhow;
 use async_trait::async_trait;
-use cap_net_ext::{AddressFamily, PoolExt};
+use cap_net_ext::PoolExt;
 use io_lifetimes::AsSocketlike;
 use rustix::io::Errno;
 use rustix::net::sockopt;
+use std::net::SocketAddr;
 use tokio::io::Interest;
 use wasmtime::component::Resource;
 
@@ -43,14 +44,22 @@ impl<T: WasiView> udp::HostUdpSocket for T {
             UdpState::Bound | UdpState::Connected => return Err(ErrorCode::InvalidState.into()),
         }
 
-        let binder = network.pool.udp_binder(local_address)?;
+        util::validate_address_family(&local_address, &socket.family)?;
 
-        // Perform the OS bind call.
-        binder.bind_existing_udp_socket(
-            &*socket
+        {
+            let binder = network.pool.udp_binder(local_address)?;
+            let udp_socket = &*socket
                 .udp_socket()
-                .as_socketlike_view::<cap_std::net::UdpSocket>(),
-        )?;
+                .as_socketlike_view::<cap_std::net::UdpSocket>();
+
+            // Perform the OS bind call.
+            util::udp_bind(udp_socket, &binder).map_err(|error| {
+                match Errno::from_io_error(&error) {
+                    Some(Errno::AFNOSUPPORT) => ErrorCode::InvalidArgument, // Just in case our own validations weren't sufficient.
+                    _ => ErrorCode::from(error),
+                }
+            })?;
+        }
 
         let socket = table.get_mut(&this)?;
         socket.udp_state = UdpState::BindStarted;
@@ -105,13 +114,25 @@ impl<T: WasiView> udp::HostUdpSocket for T {
 
         // Step #1: Disconnect
         if let UdpState::Connected = socket.udp_state {
-            disconnect(socket.udp_socket())?;
+            util::udp_disconnect(socket.udp_socket())?;
             socket.udp_state = UdpState::Bound;
         }
 
         // Step #2: (Re)connect
         if let Some(connect_addr) = remote_address {
-            rustix::net::connect(socket.udp_socket(), &connect_addr)?;
+            util::validate_remote_address(&connect_addr)?;
+            util::validate_address_family(&connect_addr, &socket.family)?;
+
+            rustix::net::connect(socket.udp_socket(), &connect_addr).map_err(
+                |error| match error {
+                    Errno::AFNOSUPPORT => ErrorCode::InvalidArgument, // Just in case our own validations weren't sufficient.
+                    Errno::INPROGRESS => {
+                        log::debug!("UDP connect returned EINPROGRESS, which should never happen");
+                        ErrorCode::Unknown
+                    }
+                    _ => ErrorCode::from(error),
+                },
+            )?;
             socket.udp_state = UdpState::Connected;
         }
 
@@ -122,6 +143,7 @@ impl<T: WasiView> udp::HostUdpSocket for T {
         let outgoing_stream = OutgoingDatagramStream {
             inner: socket.inner.clone(),
             remote_address,
+            family: socket.family,
             send_state: SendState::Idle,
         };
 
@@ -134,6 +156,13 @@ impl<T: WasiView> udp::HostUdpSocket for T {
     fn local_address(&mut self, this: Resource<udp::UdpSocket>) -> SocketResult<IpSocketAddress> {
         let table = self.table();
         let socket = table.get(&this)?;
+
+        match socket.udp_state {
+            UdpState::Default => return Err(ErrorCode::InvalidState.into()),
+            UdpState::BindStarted => return Err(ErrorCode::ConcurrencyConflict.into()),
+            _ => {}
+        }
+
         let addr = socket
             .udp_socket()
             .as_socketlike_view::<std::net::UdpSocket>()
@@ -144,6 +173,12 @@ impl<T: WasiView> udp::HostUdpSocket for T {
     fn remote_address(&mut self, this: Resource<udp::UdpSocket>) -> SocketResult<IpSocketAddress> {
         let table = self.table();
         let socket = table.get(&this)?;
+
+        match socket.udp_state {
+            UdpState::Connected => {}
+            _ => return Err(ErrorCode::InvalidState.into()),
+        }
+
         let addr = socket
             .udp_socket()
             .as_socketlike_view::<std::net::UdpSocket>()
@@ -157,39 +192,51 @@ impl<T: WasiView> udp::HostUdpSocket for T {
     ) -> Result<IpAddressFamily, anyhow::Error> {
         let table = self.table();
         let socket = table.get(&this)?;
+
         match socket.family {
-            AddressFamily::Ipv4 => Ok(IpAddressFamily::Ipv4),
-            AddressFamily::Ipv6 => Ok(IpAddressFamily::Ipv6),
+            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)?;
-        Ok(sockopt::get_ipv6_v6only(socket.udp_socket())?)
+
+        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();
-        let socket = table.get(&this)?;
-        Ok(sockopt::set_ipv6_v6only(socket.udp_socket(), value)?)
+        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()),
+            },
+        }
     }
 
     fn unicast_hop_limit(&mut self, this: Resource<udp::UdpSocket>) -> SocketResult<u8> {
         let table = self.table();
         let socket = table.get(&this)?;
 
-        // We don't track whether the socket is IPv4 or IPv6 so try one and
-        // fall back to the other.
-        match sockopt::get_ipv6_unicast_hops(socket.udp_socket()) {
-            Ok(value) => Ok(value),
-            Err(Errno::NOPROTOOPT) => {
-                let value = sockopt::get_ip_ttl(socket.udp_socket())?;
-                let value = value.try_into().unwrap();
-                Ok(value)
-            }
-            Err(err) => Err(err.into()),
-        }
+        let ttl = match socket.family {
+            SocketAddressFamily::Ipv4 => util::get_ip_ttl(socket.udp_socket())?,
+            SocketAddressFamily::Ipv6 { .. } => util::get_ipv6_unicast_hops(socket.udp_socket())?,
+        };
+
+        Ok(ttl)
     }
 
     fn set_unicast_hop_limit(
@@ -200,19 +247,22 @@ impl<T: WasiView> udp::HostUdpSocket for T {
         let table = self.table();
         let socket = table.get(&this)?;
 
-        // We don't track whether the socket is IPv4 or IPv6 so try one and
-        // fall back to the other.
-        match sockopt::set_ipv6_unicast_hops(socket.udp_socket(), Some(value)) {
-            Ok(()) => Ok(()),
-            Err(Errno::NOPROTOOPT) => Ok(sockopt::set_ip_ttl(socket.udp_socket(), value.into())?),
-            Err(err) => Err(err.into()),
+        match socket.family {
+            SocketAddressFamily::Ipv4 => util::set_ip_ttl(socket.udp_socket(), value)?,
+            SocketAddressFamily::Ipv6 { .. } => {
+                util::set_ipv6_unicast_hops(socket.udp_socket(), value)?
+            }
         }
+
+        Ok(())
     }
 
     fn receive_buffer_size(&mut self, this: Resource<udp::UdpSocket>) -> SocketResult<u64> {
         let table = self.table();
         let socket = table.get(&this)?;
-        Ok(sockopt::get_socket_recv_buffer_size(socket.udp_socket())? as u64)
+
+        let value = util::get_socket_recv_buffer_size(socket.udp_socket())?;
+        Ok(value as u64)
     }
 
     fn set_receive_buffer_size(
@@ -222,17 +272,18 @@ impl<T: WasiView> udp::HostUdpSocket for T {
     ) -> SocketResult<()> {
         let table = self.table();
         let socket = table.get(&this)?;
-        let value = value.try_into().map_err(|_| ErrorCode::OutOfMemory)?;
-        Ok(sockopt::set_socket_recv_buffer_size(
-            socket.udp_socket(),
-            value,
-        )?)
+        let value = value.try_into().unwrap_or(usize::MAX);
+
+        util::set_socket_recv_buffer_size(socket.udp_socket(), value)?;
+        Ok(())
     }
 
     fn send_buffer_size(&mut self, this: Resource<udp::UdpSocket>) -> SocketResult<u64> {
         let table = self.table();
         let socket = table.get(&this)?;
-        Ok(sockopt::get_socket_send_buffer_size(socket.udp_socket())? as u64)
+
+        let value = util::get_socket_send_buffer_size(socket.udp_socket())?;
+        Ok(value as u64)
     }
 
     fn set_send_buffer_size(
@@ -242,11 +293,10 @@ impl<T: WasiView> udp::HostUdpSocket for T {
     ) -> SocketResult<()> {
         let table = self.table();
         let socket = table.get(&this)?;
-        let value = value.try_into().map_err(|_| ErrorCode::OutOfMemory)?;
-        Ok(sockopt::set_socket_send_buffer_size(
-            socket.udp_socket(),
-            value,
-        )?)
+        let value = value.try_into().unwrap_or(usize::MAX);
+
+        util::set_socket_send_buffer_size(socket.udp_socket(), value)?;
+        Ok(())
     }
 
     fn subscribe(&mut self, this: Resource<udp::UdpSocket>) -> anyhow::Result<Resource<Pollable>> {
@@ -277,6 +327,7 @@ impl<T: WasiView> udp::HostIncomingDatagramStream for T {
         ) -> SocketResult<Option<udp::IncomingDatagram>> {
             let mut buf = [0; MAX_UDP_DATAGRAM_SIZE];
             let (size, received_addr) = stream.inner.try_recv_from(&mut buf)?;
+            debug_assert!(size <= buf.len());
 
             match stream.remote_address {
                 Some(connected_addr) if connected_addr != received_addr => {
@@ -397,6 +448,9 @@ impl<T: WasiView> udp::HostOutgoingDatagramStream for T {
                 _ => return Err(ErrorCode::InvalidArgument.into()),
             };
 
+            util::validate_remote_address(&addr)?;
+            util::validate_address_family(&addr, &stream.family)?;
+
             // FIXME: check permission to send to `addr`.
             if stream.remote_address == Some(addr) {
                 stream.inner.try_send(&datagram.data)?;
@@ -487,12 +541,3 @@ impl Subscribe for OutgoingDatagramStream {
         }
     }
 }
-
-fn disconnect<Fd: rustix::fd::AsFd>(sockfd: Fd) -> rustix::io::Result<()> {
-    match rustix::net::connect_unspec(sockfd) {
-        // BSD platforms return an error even if the socket was disconnected successfully.
-        #[cfg(target_os = "macos")]
-        Err(rustix::io::Errno::INVAL | rustix::io::Errno::AFNOSUPPORT) => Ok(()),
-        r => r,
-    }
-}
diff --git a/crates/wasi/src/preview2/network.rs b/crates/wasi/src/preview2/network.rs
index 1f49e06f3..b3b7537b9 100644
--- a/crates/wasi/src/preview2/network.rs
+++ b/crates/wasi/src/preview2/network.rs
@@ -28,3 +28,9 @@ impl From<rustix::io::Errno> for SocketError {
         ErrorCode::from(error).into()
     }
 }
+
+#[derive(Copy, Clone)]
+pub enum SocketAddressFamily {
+    Ipv4,
+    Ipv6 { v6only: bool },
+}
diff --git a/crates/wasi/src/preview2/tcp.rs b/crates/wasi/src/preview2/tcp.rs
index dd782c4c6..b6ebffe7e 100644
--- a/crates/wasi/src/preview2/tcp.rs
+++ b/crates/wasi/src/preview2/tcp.rs
@@ -1,3 +1,4 @@
+use super::network::SocketAddressFamily;
 use super::{HostInputStream, HostOutputStream, StreamError};
 use crate::preview2::{
     with_ambient_tokio_runtime, AbortOnDropJoinHandle, InputStream, OutputStream, Subscribe,
@@ -74,12 +75,6 @@ pub struct TcpSocket {
     pub(crate) hop_limit: Option<u8>,
 }
 
-#[derive(Copy, Clone)]
-pub(crate) enum SocketAddressFamily {
-    Ipv4,
-    Ipv6 { v6only: bool },
-}
-
 pub(crate) struct TcpReadStream {
     stream: Arc<tokio::net::TcpStream>,
     closed: bool,
diff --git a/crates/wasi/src/preview2/udp.rs b/crates/wasi/src/preview2/udp.rs
index 504dba0b1..16335d601 100644
--- a/crates/wasi/src/preview2/udp.rs
+++ b/crates/wasi/src/preview2/udp.rs
@@ -7,6 +7,8 @@ use std::io;
 use std::net::SocketAddr;
 use std::sync::Arc;
 
+use super::network::SocketAddressFamily;
+
 /// The state of a UDP socket.
 ///
 /// This represents the various states a socket can be in during the
@@ -39,7 +41,7 @@ pub struct UdpSocket {
     pub(crate) udp_state: UdpState,
 
     /// Socket address family.
-    pub(crate) family: AddressFamily,
+    pub(crate) family: SocketAddressFamily,
 }
 
 #[async_trait]
@@ -52,10 +54,19 @@ impl Subscribe for UdpSocket {
 impl UdpSocket {
     /// Create a new socket in the given family.
     pub fn new(family: AddressFamily) -> io::Result<Self> {
+        let socket = Self::new_tokio_socket(family)?;
+
+        let socket_address_family = match family {
+            AddressFamily::Ipv4 => SocketAddressFamily::Ipv4,
+            AddressFamily::Ipv6 => SocketAddressFamily::Ipv6 {
+                v6only: rustix::net::sockopt::get_ipv6_v6only(&socket)?,
+            },
+        };
+
         Ok(UdpSocket {
-            inner: Arc::new(Self::new_tokio_socket(family)?),
+            inner: Arc::new(socket),
             udp_state: UdpState::Default,
-            family,
+            family: socket_address_family,
         })
     }
 
@@ -89,6 +100,9 @@ pub struct OutgoingDatagramStream {
     /// If this has a value, the stream is "connected".
     pub(crate) remote_address: Option<SocketAddr>,
 
+    /// Socket address family.
+    pub(crate) family: SocketAddressFamily,
+
     pub(crate) send_state: SendState,
 }
 
diff --git a/crates/wasi/wit/deps/sockets/tcp.wit b/crates/wasi/wit/deps/sockets/tcp.wit
index b9073b775..b9ef0625a 100644
--- a/crates/wasi/wit/deps/sockets/tcp.wit
+++ b/crates/wasi/wit/deps/sockets/tcp.wit
@@ -81,7 +81,7 @@ interface tcp {
         /// - `connection-refused`:        The connection was forcefully rejected. (ECONNREFUSED)
         /// - `connection-reset`:          The connection was reset. (ECONNRESET)
         /// - `connection-aborted`:        The connection was aborted. (ECONNABORTED)
-        /// - `remote-unreachable`:        The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN)
+        /// - `remote-unreachable`:        The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET)
         /// - `address-in-use`:            Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD)
         /// - `not-in-progress`:           A `connect` operation is not in progress.
         /// - `would-block`:               Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN)
@@ -126,7 +126,6 @@ interface tcp {
         /// - `address-family`
         /// - `ipv6-only`
         /// - `keep-alive`
-        /// - `no-delay`
         /// - `unicast-hop-limit`
         /// - `receive-buffer-size`
         /// - `send-buffer-size`
@@ -204,12 +203,6 @@ interface tcp {
         keep-alive: func() -> result<bool, error-code>;
         set-keep-alive: func(value: bool) -> result<_, error-code>;
 
-        /// Equivalent to the TCP_NODELAY socket option.
-        ///
-        /// The default value is `false`.
-        no-delay: func() -> result<bool, error-code>;
-        set-no-delay: func(value: bool) -> result<_, error-code>;
-
         /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options.
         ///
         /// # Typical errors
diff --git a/crates/wasi/wit/deps/sockets/udp.wit b/crates/wasi/wit/deps/sockets/udp.wit
index 232c2b41c..1ee1843a9 100644
--- a/crates/wasi/wit/deps/sockets/udp.wit
+++ b/crates/wasi/wit/deps/sockets/udp.wit
@@ -97,7 +97,8 @@ interface udp {
         /// - `invalid-argument`:          The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL)
         /// - `invalid-state`:             The socket is not bound.
         /// - `address-in-use`:            Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD)
-        /// - `remote-unreachable`:        The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN)
+        /// - `remote-unreachable`:        The remote address is not reachable. (ECONNRESET, ENETRESET, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET)
+        /// - `connection-refused`:        The connection was refused. (ECONNREFUSED)
         ///
         /// # References
         /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html>
@@ -190,7 +191,8 @@ interface udp {
         /// This function never returns `error(would-block)`.
         ///
         /// # Typical errors
-        /// - `remote-unreachable`: The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN)
+        /// - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET)
+        /// - `connection-refused`: The connection was refused. (ECONNREFUSED)
         ///
         /// # References
         /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvfrom.html>
@@ -246,7 +248,8 @@ interface udp {
         /// - `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)
         /// - `invalid-argument`:        The socket is not "connected" and no value for `remote-address` was provided. (EDESTADDRREQ)
-        /// - `remote-unreachable`:      The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN)
+        /// - `remote-unreachable`:      The remote address is not reachable. (ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET)
+        /// - `connection-refused`:      The connection was refused. (ECONNREFUSED)
         /// - `datagram-too-large`:      The datagram is too large. (EMSGSIZE)
         ///
         /// # References
diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock
index eec621f33..0a856149d 100644
--- a/supply-chain/imports.lock
+++ b/supply-chain/imports.lock
@@ -5,194 +5,386 @@
 version = "0.102.0"
 audited_as = "0.101.2"
 
+[[unpublished.cranelift]]
+version = "0.103.0"
+audited_as = "0.101.4"
+
 [[unpublished.cranelift-bforest]]
 version = "0.102.0"
 audited_as = "0.101.2"
 
+[[unpublished.cranelift-bforest]]
+version = "0.103.0"
+audited_as = "0.101.4"
+
 [[unpublished.cranelift-codegen]]
 version = "0.102.0"
 audited_as = "0.101.2"
 
+[[unpublished.cranelift-codegen]]
+version = "0.103.0"
+audited_as = "0.101.4"
+
 [[unpublished.cranelift-codegen-meta]]
 version = "0.102.0"
 audited_as = "0.101.2"
 
+[[unpublished.cranelift-codegen-meta]]
+version = "0.103.0"
+audited_as = "0.101.4"
+
 [[unpublished.cranelift-codegen-shared]]
 version = "0.102.0"
 audited_as = "0.101.2"
 
+[[unpublished.cranelift-codegen-shared]]
+version = "0.103.0"
+audited_as = "0.101.4"
+
 [[unpublished.cranelift-control]]
 version = "0.102.0"
 audited_as = "0.101.2"
 
+[[unpublished.cranelift-control]]
+version = "0.103.0"
+audited_as = "0.101.4"
+
 [[unpublished.cranelift-entity]]
 version = "0.102.0"
 audited_as = "0.101.2"
 
+[[unpublished.cranelift-entity]]
+version = "0.103.0"
+audited_as = "0.101.4"
+
 [[unpublished.cranelift-frontend]]
 version = "0.102.0"
 audited_as = "0.101.2"
 
+[[unpublished.cranelift-frontend]]
+version = "0.103.0"
+audited_as = "0.101.4"
+
 [[unpublished.cranelift-interpreter]]
 version = "0.102.0"
 audited_as = "0.101.2"
 
+[[unpublished.cranelift-interpreter]]
+version = "0.103.0"
+audited_as = "0.101.4"
+
 [[unpublished.cranelift-isle]]
 version = "0.102.0"
 audited_as = "0.101.2"
 
+[[unpublished.cranelift-isle]]
+version = "0.103.0"
+audited_as = "0.101.4"
+
 [[unpublished.cranelift-jit]]
 version = "0.102.0"
 audited_as = "0.101.2"
 
+[[unpublished.cranelift-jit]]
+version = "0.103.0"
+audited_as = "0.101.4"
+
 [[unpublished.cranelift-module]]
 version = "0.102.0"
 audited_as = "0.101.2"
 
+[[unpublished.cranelift-module]]
+version = "0.103.0"
+audited_as = "0.101.4"
+
 [[unpublished.cranelift-native]]
 version = "0.102.0"
 audited_as = "0.101.2"
 
+[[unpublished.cranelift-native]]
+version = "0.103.0"
+audited_as = "0.101.4"
+
 [[unpublished.cranelift-object]]
 version = "0.102.0"
 audited_as = "0.101.2"
 
+[[unpublished.cranelift-object]]
+version = "0.103.0"
+audited_as = "0.101.4"
+
 [[unpublished.cranelift-reader]]
 version = "0.102.0"
 audited_as = "0.101.2"
 
+[[unpublished.cranelift-reader]]
+version = "0.103.0"
+audited_as = "0.101.4"
+
 [[unpublished.cranelift-serde]]
 version = "0.102.0"
 audited_as = "0.101.2"
 
+[[unpublished.cranelift-serde]]
+version = "0.103.0"
+audited_as = "0.101.4"
+
 [[unpublished.cranelift-wasm]]
 version = "0.102.0"
 audited_as = "0.101.2"
 
+[[unpublished.cranelift-wasm]]
+version = "0.103.0"
+audited_as = "0.101.4"
+
 [[unpublished.wasi-cap-std-sync]]
 version = "15.0.0"
 audited_as = "14.0.2"
 
+[[unpublished.wasi-cap-std-sync]]
+version = "16.0.0"
+audited_as = "14.0.4"
+
 [[unpublished.wasi-common]]
 version = "15.0.0"
 audited_as = "14.0.2"
 
+[[unpublished.wasi-common]]
+version = "16.0.0"
+audited_as = "14.0.4"
+
 [[unpublished.wasi-tokio]]
 version = "15.0.0"
 audited_as = "14.0.2"
 
+[[unpublished.wasi-tokio]]
+version = "16.0.0"
+audited_as = "14.0.4"
+
 [[unpublished.wasmtime]]
 version = "15.0.0"
 audited_as = "14.0.2"
 
+[[unpublished.wasmtime]]
+version = "16.0.0"
+audited_as = "14.0.4"
+
 [[unpublished.wasmtime-asm-macros]]
 version = "15.0.0"
 audited_as = "14.0.2"
 
+[[unpublished.wasmtime-asm-macros]]
+version = "16.0.0"
+audited_as = "14.0.4"
+
 [[unpublished.wasmtime-cache]]
 version = "15.0.0"
 audited_as = "14.0.2"
 
+[[unpublished.wasmtime-cache]]
+version = "16.0.0"
+audited_as = "14.0.4"
+
 [[unpublished.wasmtime-cli]]
 version = "15.0.0"
 audited_as = "14.0.2"
 
+[[unpublished.wasmtime-cli]]
+version = "16.0.0"
+audited_as = "14.0.4"
+
 [[unpublished.wasmtime-cli-flags]]
 version = "15.0.0"
 audited_as = "14.0.2"
 
+[[unpublished.wasmtime-cli-flags]]
+version = "16.0.0"
+audited_as = "14.0.4"
+
 [[unpublished.wasmtime-component-macro]]
 version = "15.0.0"
 audited_as = "14.0.2"
 
+[[unpublished.wasmtime-component-macro]]
+version = "16.0.0"
+audited_as = "14.0.4"
+
 [[unpublished.wasmtime-component-util]]
 version = "15.0.0"
 audited_as = "14.0.2"
 
+[[unpublished.wasmtime-component-util]]
+version = "16.0.0"
+audited_as = "14.0.4"
+
 [[unpublished.wasmtime-cranelift]]
 version = "15.0.0"
 audited_as = "14.0.2"
 
+[[unpublished.wasmtime-cranelift]]
+version = "16.0.0"
+audited_as = "14.0.4"
+
 [[unpublished.wasmtime-cranelift-shared]]
 version = "15.0.0"
 audited_as = "14.0.2"
 
+[[unpublished.wasmtime-cranelift-shared]]
+version = "16.0.0"
+audited_as = "14.0.4"
+
 [[unpublished.wasmtime-environ]]
 version = "15.0.0"
 audited_as = "14.0.2"
 
+[[unpublished.wasmtime-environ]]
+version = "16.0.0"
+audited_as = "14.0.4"
+
 [[unpublished.wasmtime-explorer]]
 version = "15.0.0"
 audited_as = "14.0.2"
 
+[[unpublished.wasmtime-explorer]]
+version = "16.0.0"
+audited_as = "14.0.4"
+
 [[unpublished.wasmtime-fiber]]
 version = "15.0.0"
 audited_as = "14.0.2"
 
+[[unpublished.wasmtime-fiber]]
+version = "16.0.0"
+audited_as = "14.0.4"
+
 [[unpublished.wasmtime-jit]]
 version = "15.0.0"
 audited_as = "14.0.2"
 
+[[unpublished.wasmtime-jit]]
+version = "16.0.0"
+audited_as = "14.0.4"
+
 [[unpublished.wasmtime-jit-debug]]
 version = "15.0.0"
 audited_as = "14.0.2"
 
+[[unpublished.wasmtime-jit-debug]]
+version = "16.0.0"
+audited_as = "14.0.4"
+
 [[unpublished.wasmtime-jit-icache-coherence]]
 version = "15.0.0"
 audited_as = "14.0.2"
 
+[[unpublished.wasmtime-jit-icache-coherence]]
+version = "16.0.0"
+audited_as = "14.0.4"
+
 [[unpublished.wasmtime-runtime]]
 version = "15.0.0"
 audited_as = "14.0.2"
 
+[[unpublished.wasmtime-runtime]]
+version = "16.0.0"
+audited_as = "14.0.4"
+
 [[unpublished.wasmtime-types]]
 version = "15.0.0"
 audited_as = "14.0.2"
 
+[[unpublished.wasmtime-types]]
+version = "16.0.0"
+audited_as = "14.0.4"
+
 [[unpublished.wasmtime-wasi]]
 version = "15.0.0"
 audited_as = "14.0.2"
 
+[[unpublished.wasmtime-wasi]]
+version = "16.0.0"
+audited_as = "14.0.4"
+
 [[unpublished.wasmtime-wasi-http]]
 version = "15.0.0"
 audited_as = "14.0.2"
 
+[[unpublished.wasmtime-wasi-http]]
+version = "16.0.0"
+audited_as = "14.0.4"
+
 [[unpublished.wasmtime-wasi-nn]]
 version = "15.0.0"
 audited_as = "14.0.2"
 
+[[unpublished.wasmtime-wasi-nn]]
+version = "16.0.0"
+audited_as = "14.0.4"
+
 [[unpublished.wasmtime-wasi-threads]]
 version = "15.0.0"
 audited_as = "14.0.2"
 
+[[unpublished.wasmtime-wasi-threads]]
+version = "16.0.0"
+audited_as = "14.0.4"
+
 [[unpublished.wasmtime-wast]]
 version = "15.0.0"
 audited_as = "14.0.2"
 
+[[unpublished.wasmtime-wast]]
+version = "16.0.0"
+audited_as = "14.0.4"
+
 [[unpublished.wasmtime-winch]]
 version = "15.0.0"
 audited_as = "14.0.2"
 
+[[unpublished.wasmtime-winch]]
+version = "16.0.0"
+audited_as = "14.0.4"
+
 [[unpublished.wasmtime-wit-bindgen]]
 version = "15.0.0"
 audited_as = "14.0.2"
 
+[[unpublished.wasmtime-wit-bindgen]]
+version = "16.0.0"
+audited_as = "14.0.4"
+
 [[unpublished.wasmtime-wmemcheck]]
 version = "15.0.0"
 audited_as = "14.0.2"
 
+[[unpublished.wasmtime-wmemcheck]]
+version = "16.0.0"
+audited_as = "14.0.4"
+
 [[unpublished.wiggle]]
 version = "15.0.0"
 audited_as = "14.0.2"
 
+[[unpublished.wiggle]]
+version = "16.0.0"
+audited_as = "14.0.4"
+
 [[unpublished.wiggle-generate]]
 version = "15.0.0"
 audited_as = "14.0.2"
 
+[[unpublished.wiggle-generate]]
+version = "16.0.0"
+audited_as = "14.0.4"
+
 [[unpublished.wiggle-macro]]
 version = "15.0.0"
 audited_as = "14.0.2"
 
+[[unpublished.wiggle-macro]]
+version = "16.0.0"
+audited_as = "14.0.4"
+
 [[unpublished.wiggle-test]]
 version = "0.0.0"
 audited_as = "0.1.0"
@@ -201,6 +393,10 @@ audited_as = "0.1.0"
 version = "0.13.0"
 audited_as = "0.12.2"
 
+[[unpublished.winch-codegen]]
+version = "0.14.0"
+audited_as = "0.12.4"
+
 [[publisher.aho-corasick]]
 version = "1.0.2"
 when = "2023-06-04"
@@ -375,102 +571,204 @@ when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.cranelift]]
+version = "0.101.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.cranelift-bforest]]
 version = "0.101.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.cranelift-bforest]]
+version = "0.101.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.cranelift-codegen]]
 version = "0.101.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.cranelift-codegen]]
+version = "0.101.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.cranelift-codegen-meta]]
 version = "0.101.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.cranelift-codegen-meta]]
+version = "0.101.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.cranelift-codegen-shared]]
 version = "0.101.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.cranelift-codegen-shared]]
+version = "0.101.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.cranelift-control]]
 version = "0.101.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.cranelift-control]]
+version = "0.101.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.cranelift-entity]]
 version = "0.101.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.cranelift-entity]]
+version = "0.101.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.cranelift-frontend]]
 version = "0.101.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.cranelift-frontend]]
+version = "0.101.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.cranelift-interpreter]]
 version = "0.101.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.cranelift-interpreter]]
+version = "0.101.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.cranelift-isle]]
 version = "0.101.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.cranelift-isle]]
+version = "0.101.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.cranelift-jit]]
 version = "0.101.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.cranelift-jit]]
+version = "0.101.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.cranelift-module]]
 version = "0.101.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.cranelift-module]]
+version = "0.101.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.cranelift-native]]
 version = "0.101.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.cranelift-native]]
+version = "0.101.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.cranelift-object]]
 version = "0.101.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.cranelift-object]]
+version = "0.101.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.cranelift-reader]]
 version = "0.101.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.cranelift-reader]]
+version = "0.101.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.cranelift-serde]]
 version = "0.101.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.cranelift-serde]]
+version = "0.101.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.cranelift-wasm]]
 version = "0.101.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.cranelift-wasm]]
+version = "0.101.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.derive_arbitrary]]
 version = "1.3.0"
 when = "2023-03-13"
@@ -791,18 +1089,36 @@ when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasi-cap-std-sync]]
+version = "14.0.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasi-common]]
 version = "14.0.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasi-common]]
+version = "14.0.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasi-tokio]]
 version = "14.0.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasi-tokio]]
+version = "14.0.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasm-bindgen]]
 version = "0.2.87"
 when = "2023-06-12"
@@ -928,150 +1244,300 @@ when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime]]
+version = "14.0.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-asm-macros]]
 version = "14.0.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-asm-macros]]
+version = "14.0.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-cache]]
 version = "14.0.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-cache]]
+version = "14.0.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-cli]]
 version = "14.0.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-cli]]
+version = "14.0.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-cli-flags]]
 version = "14.0.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-cli-flags]]
+version = "14.0.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-component-macro]]
 version = "14.0.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-component-macro]]
+version = "14.0.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-component-util]]
 version = "14.0.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-component-util]]
+version = "14.0.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-cranelift]]
 version = "14.0.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-cranelift]]
+version = "14.0.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-cranelift-shared]]
 version = "14.0.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-cranelift-shared]]
+version = "14.0.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-environ]]
 version = "14.0.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-environ]]
+version = "14.0.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-explorer]]
 version = "14.0.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-explorer]]
+version = "14.0.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-fiber]]
 version = "14.0.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-fiber]]
+version = "14.0.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-jit]]
 version = "14.0.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-jit]]
+version = "14.0.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-jit-debug]]
 version = "14.0.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-jit-debug]]
+version = "14.0.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-jit-icache-coherence]]
 version = "14.0.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-jit-icache-coherence]]
+version = "14.0.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-runtime]]
 version = "14.0.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-runtime]]
+version = "14.0.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-types]]
 version = "14.0.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-types]]
+version = "14.0.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-wasi]]
 version = "14.0.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-wasi]]
+version = "14.0.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-wasi-http]]
 version = "14.0.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-wasi-http]]
+version = "14.0.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-wasi-nn]]
 version = "14.0.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-wasi-nn]]
+version = "14.0.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-wasi-threads]]
 version = "14.0.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-wasi-threads]]
+version = "14.0.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-wast]]
 version = "14.0.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-wast]]
+version = "14.0.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-winch]]
 version = "14.0.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-winch]]
+version = "14.0.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-wit-bindgen]]
 version = "14.0.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-wit-bindgen]]
+version = "14.0.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-wmemcheck]]
 version = "14.0.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-wmemcheck]]
+version = "14.0.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wast]]
 version = "66.0.2"
 when = "2023-10-14"
@@ -1106,18 +1572,36 @@ when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wiggle]]
+version = "14.0.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wiggle-generate]]
 version = "14.0.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wiggle-generate]]
+version = "14.0.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wiggle-macro]]
 version = "14.0.2"
 when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wiggle-macro]]
+version = "14.0.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wiggle-test]]
 version = "0.1.0"
 when = "2020-03-12"
@@ -1138,6 +1622,12 @@ when = "2023-10-26"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.winch-codegen]]
+version = "0.12.4"
+when = "2023-11-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.windows-sys]]
 version = "0.48.0"
 when = "2023-03-31"
diff --git a/winch/codegen/Cargo.toml b/winch/codegen/Cargo.toml
index db0365ebb..42d183855 100644
--- a/winch/codegen/Cargo.toml
+++ b/winch/codegen/Cargo.toml
@@ -4,7 +4,7 @@ name = "winch-codegen"
 description = "Winch code generation library"
 license = "Apache-2.0 WITH LLVM-exception"
 repository = "https://github.com/bytecodealliance/wasmtime"
-version = "0.13.0"
+version = "0.14.0"
 edition.workspace = true
 
 [dependencies]


Test patch:
diff --git a/crates/test-programs/src/bin/preview2_tcp_connect.rs b/crates/test-programs/src/bin/preview2_tcp_connect.rs
index 58d5a84c4..f304e46a5 100644
--- a/crates/test-programs/src/bin/preview2_tcp_connect.rs
+++ b/crates/test-programs/src/bin/preview2_tcp_connect.rs
@@ -27,7 +27,7 @@ fn test_tcp_connect_port_0(net: &Network, family: IpAddressFamily) {
     ));
 }
 
-/// Bind should validate the address family.
+/// Connect should validate the address family.
 fn test_tcp_connect_wrong_family(net: &Network, family: IpAddressFamily) {
     let wrong_ip = match family {
         IpAddressFamily::Ipv4 => IpAddress::IPV6_LOOPBACK,
diff --git a/crates/test-programs/src/bin/preview2_tcp_sample_application.rs b/crates/test-programs/src/bin/preview2_tcp_sample_application.rs
index a2b648106..6aaa313c4 100644
--- a/crates/test-programs/src/bin/preview2_tcp_sample_application.rs
+++ b/crates/test-programs/src/bin/preview2_tcp_sample_application.rs
@@ -3,7 +3,7 @@ use test_programs::wasi::sockets::network::{
 };
 use test_programs::wasi::sockets::tcp::TcpSocket;
 
-fn test_sample_application(family: IpAddressFamily, bind_address: IpSocketAddress) {
+fn test_tcp_sample_application(family: IpAddressFamily, bind_address: IpSocketAddress) {
     let first_message = b"Hello, world!";
     let second_message = b"Greetings, planet!";
 
@@ -54,14 +54,14 @@ fn test_sample_application(family: IpAddressFamily, bind_address: IpSocketAddres
 }
 
 fn main() {
-    test_sample_application(
+    test_tcp_sample_application(
         IpAddressFamily::Ipv4,
         IpSocketAddress::Ipv4(Ipv4SocketAddress {
             port: 0,                 // use any free port
             address: (127, 0, 0, 1), // localhost
         }),
     );
-    test_sample_application(
+    test_tcp_sample_application(
         IpAddressFamily::Ipv6,
         IpSocketAddress::Ipv6(Ipv6SocketAddress {
             port: 0,                           // use any free port
diff --git a/crates/test-programs/src/bin/preview2_tcp_sockopts.rs b/crates/test-programs/src/bin/preview2_tcp_sockopts.rs
index 2170ed420..c54fbccfe 100644
--- a/crates/test-programs/src/bin/preview2_tcp_sockopts.rs
+++ b/crates/test-programs/src/bin/preview2_tcp_sockopts.rs
@@ -13,7 +13,6 @@ fn test_tcp_sockopt_defaults(family: IpAddressFamily) {
     }
 
     sock.keep_alive().unwrap(); // Only verify that it has a default value at all, but either value is valid.
-    assert_eq!(sock.no_delay().unwrap(), false);
     assert!(sock.unicast_hop_limit().unwrap() > 0);
     assert!(sock.receive_buffer_size().unwrap() > 0);
     assert!(sock.send_buffer_size().unwrap() > 0);
@@ -33,9 +32,6 @@ fn test_tcp_sockopt_input_ranges(family: IpAddressFamily) {
     assert!(matches!(sock.set_keep_alive(true), Ok(_)));
     assert!(matches!(sock.set_keep_alive(false), Ok(_)));
 
-    assert!(matches!(sock.set_no_delay(true), Ok(_)));
-    assert!(matches!(sock.set_no_delay(false), Ok(_)));
-
     assert!(matches!(
         sock.set_unicast_hop_limit(0),
         Err(ErrorCode::InvalidArgument)
@@ -64,11 +60,6 @@ fn test_tcp_sockopt_readback(family: IpAddressFamily) {
     sock.set_keep_alive(false).unwrap();
     assert_eq!(sock.keep_alive().unwrap(), false);
 
-    sock.set_no_delay(true).unwrap();
-    assert_eq!(sock.no_delay().unwrap(), true);
-    sock.set_no_delay(false).unwrap();
-    assert_eq!(sock.no_delay().unwrap(), false);
-
     sock.set_unicast_hop_limit(42).unwrap();
     assert_eq!(sock.unicast_hop_limit().unwrap(), 42);
 
@@ -93,7 +84,6 @@ fn test_tcp_sockopt_inheritance(net: &Network, family: IpAddressFamily) {
         }
 
         listener.set_keep_alive(!default_keep_alive).unwrap();
-        listener.set_no_delay(true).unwrap();
         listener.set_unicast_hop_limit(42).unwrap();
         listener.set_receive_buffer_size(0x10000).unwrap();
         listener.set_send_buffer_size(0x10000).unwrap();
@@ -113,7 +103,6 @@ fn test_tcp_sockopt_inheritance(net: &Network, family: IpAddressFamily) {
         }
 
         assert_eq!(accepted_client.keep_alive().unwrap(), !default_keep_alive);
-        assert_eq!(accepted_client.no_delay().unwrap(), true);
         assert_eq!(accepted_client.unicast_hop_limit().unwrap(), 42);
         assert_eq!(accepted_client.receive_buffer_size().unwrap(), 0x10000);
         assert_eq!(accepted_client.send_buffer_size().unwrap(), 0x10000);
@@ -122,7 +111,6 @@ fn test_tcp_sockopt_inheritance(net: &Network, family: IpAddressFamily) {
     // Update options on listener to something else:
     {
         listener.set_keep_alive(default_keep_alive).unwrap();
-        listener.set_no_delay(false).unwrap();
         listener.set_unicast_hop_limit(43).unwrap();
         listener.set_receive_buffer_size(0x20000).unwrap();
         listener.set_send_buffer_size(0x20000).unwrap();
@@ -131,7 +119,6 @@ fn test_tcp_sockopt_inheritance(net: &Network, family: IpAddressFamily) {
     // Verify that the already accepted socket was not affected:
     {
         assert_eq!(accepted_client.keep_alive().unwrap(), !default_keep_alive);
-        assert_eq!(accepted_client.no_delay().unwrap(), true);
         assert_eq!(accepted_client.unicast_hop_limit().unwrap(), 42);
         assert_eq!(accepted_client.receive_buffer_size().unwrap(), 0x10000);
         assert_eq!(accepted_client.send_buffer_size().unwrap(), 0x10000);
@@ -150,7 +137,6 @@ fn test_tcp_sockopt_after_listen(net: &Network, family: IpAddressFamily) {
     // Update options while the socket is already listening:
     {
         listener.set_keep_alive(!default_keep_alive).unwrap();
-        listener.set_no_delay(true).unwrap();
         listener.set_unicast_hop_limit(42).unwrap();
         listener.set_receive_buffer_size(0x10000).unwrap();
         listener.set_send_buffer_size(0x10000).unwrap();
@@ -163,7 +149,6 @@ fn test_tcp_sockopt_after_listen(net: &Network, family: IpAddressFamily) {
     // Verify options on accepted socket:
     {
         assert_eq!(accepted_client.keep_alive().unwrap(), !default_keep_alive);
-        assert_eq!(accepted_client.no_delay().unwrap(), true);
         assert_eq!(accepted_client.unicast_hop_limit().unwrap(), 42);
         assert_eq!(accepted_client.receive_buffer_size().unwrap(), 0x10000);
         assert_eq!(accepted_client.send_buffer_size().unwrap(), 0x10000);
diff --git a/crates/test-programs/src/bin/preview2_tcp_states.rs b/crates/test-programs/src/bin/preview2_tcp_states.rs
index 6bf6cbdab..e4439af81 100644
--- a/crates/test-programs/src/bin/preview2_tcp_states.rs
+++ b/crates/test-programs/src/bin/preview2_tcp_states.rs
@@ -51,8 +51,6 @@ fn test_tcp_unbound_state_invariants(family: IpAddressFamily) {
     assert!(matches!(sock.set_listen_backlog_size(32), Ok(_)));
     assert!(matches!(sock.keep_alive(), Ok(_)));
     assert!(matches!(sock.set_keep_alive(false), Ok(_)));
-    assert!(matches!(sock.no_delay(), Ok(_)));
-    assert!(matches!(sock.set_no_delay(false), Ok(_)));
     assert!(matches!(sock.unicast_hop_limit(), Ok(_)));
     assert!(matches!(sock.set_unicast_hop_limit(255), Ok(_)));
     assert!(matches!(sock.receive_buffer_size(), Ok(_)));
@@ -111,8 +109,6 @@ fn test_tcp_bound_state_invariants(net: &Network, family: IpAddressFamily) {
     assert!(matches!(sock.set_listen_backlog_size(32), Ok(_)));
     assert!(matches!(sock.keep_alive(), Ok(_)));
     assert!(matches!(sock.set_keep_alive(false), Ok(_)));
-    assert!(matches!(sock.no_delay(), Ok(_)));
-    assert!(matches!(sock.set_no_delay(false), Ok(_)));
     assert!(matches!(sock.unicast_hop_limit(), Ok(_)));
     assert!(matches!(sock.set_unicast_hop_limit(255), Ok(_)));
     assert!(matches!(sock.receive_buffer_size(), Ok(_)));
@@ -178,8 +174,6 @@ fn test_tcp_listening_state_invariants(net: &Network, family: IpAddressFamily) {
     ));
     assert!(matches!(sock.keep_alive(), Ok(_)));
     assert!(matches!(sock.set_keep_alive(false), Ok(_)));
-    assert!(matches!(sock.no_delay(), Ok(_)));
-    assert!(matches!(sock.set_no_delay(false), Ok(_)));
     assert!(matches!(sock.unicast_hop_limit(), Ok(_)));
     assert!(matches!(sock.set_unicast_hop_limit(255), Ok(_)));
     assert!(matches!(sock.receive_buffer_size(), Ok(_)));
@@ -238,8 +232,6 @@ fn test_tcp_connected_state_invariants(net: &Network, family: IpAddressFamily) {
 
     assert!(matches!(sock.keep_alive(), Ok(_)));
     assert!(matches!(sock.set_keep_alive(false), Ok(_)));
-    assert!(matches!(sock.no_delay(), Ok(_)));
-    assert!(matches!(sock.set_no_delay(false), Ok(_)));
     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/test-programs/src/bin/preview2_udp_bind.rs b/crates/test-programs/src/bin/preview2_udp_bind.rs
new file mode 100644
index 000000000..5fb5e5421
--- /dev/null
+++ b/crates/test-programs/src/bin/preview2_udp_bind.rs
@@ -0,0 +1,133 @@
+use test_programs::wasi::sockets::network::{
+    ErrorCode, IpAddress, IpAddressFamily, IpSocketAddress, Network,
+};
+use test_programs::wasi::sockets::udp::UdpSocket;
+
+/// Bind a socket and let the system determine a port.
+fn test_udp_bind_ephemeral_port(net: &Network, ip: IpAddress) {
+    let bind_addr = IpSocketAddress::new(ip, 0);
+
+    let sock = UdpSocket::new(ip.family()).unwrap();
+    sock.blocking_bind(net, bind_addr).unwrap();
+
+    let bound_addr = sock.local_address().unwrap();
+
+    assert_eq!(bind_addr.ip(), bound_addr.ip());
+    assert_ne!(bind_addr.port(), bound_addr.port());
+}
+
+/// Bind a socket on a specified port.
+fn test_udp_bind_specific_port(net: &Network, ip: IpAddress) {
+    const PORT: u16 = 54321;
+
+    let bind_addr = IpSocketAddress::new(ip, PORT);
+
+    let sock = UdpSocket::new(ip.family()).unwrap();
+    match sock.blocking_bind(net, bind_addr) {
+        Ok(()) => {}
+
+        // Concurrent invocations of this test can yield `AddressInUse` and that
+        // same error can show up on Windows as `AccessDenied`.
+        Err(ErrorCode::AddressInUse | ErrorCode::AccessDenied) => {}
+        r => r.unwrap(),
+    }
+
+    let bound_addr = sock.local_address().unwrap();
+
+    assert_eq!(bind_addr.ip(), bound_addr.ip());
+    assert_eq!(bind_addr.port(), bound_addr.port());
+}
+
+/// Two sockets may not be actively bound to the same address at the same time.
+fn test_udp_bind_addrinuse(net: &Network, ip: IpAddress) {
+    let bind_addr = IpSocketAddress::new(ip, 0);
+
+    let sock1 = UdpSocket::new(ip.family()).unwrap();
+    sock1.blocking_bind(net, bind_addr).unwrap();
+
+    let bound_addr = sock1.local_address().unwrap();
+
+    let sock2 = UdpSocket::new(ip.family()).unwrap();
+    assert!(matches!(
+        sock2.blocking_bind(net, bound_addr),
+        Err(ErrorCode::AddressInUse)
+    ));
+}
+
+// Try binding to an address that is not configured on the system.
+fn test_udp_bind_addrnotavail(net: &Network, ip: IpAddress) {
+    let bind_addr = IpSocketAddress::new(ip, 0);
+
+    let sock = UdpSocket::new(ip.family()).unwrap();
+
+    assert!(matches!(
+        sock.blocking_bind(net, bind_addr),
+        Err(ErrorCode::AddressNotBindable)
+    ));
+}
+
+/// Bind should validate the address family.
+fn test_udp_bind_wrong_family(net: &Network, family: IpAddressFamily) {
+    let wrong_ip = match family {
+        IpAddressFamily::Ipv4 => IpAddress::IPV6_LOOPBACK,
+        IpAddressFamily::Ipv6 => IpAddress::IPV4_LOOPBACK,
+    };
+
+    let sock = UdpSocket::new(family).unwrap();
+    let result = sock.blocking_bind(net, IpSocketAddress::new(wrong_ip, 0));
+
+    assert!(matches!(result, Err(ErrorCode::InvalidArgument)));
+}
+
+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() {
+    const RESERVED_IPV4_ADDRESS: IpAddress = IpAddress::Ipv4((192, 0, 2, 0)); // Reserved for documentation and examples.
+    const RESERVED_IPV6_ADDRESS: IpAddress = IpAddress::Ipv6((0x2001, 0x0db8, 0, 0, 0, 0, 0, 0)); // Reserved for documentation and examples.
+
+    let net = Network::default();
+
+    test_udp_bind_ephemeral_port(&net, IpAddress::IPV4_LOOPBACK);
+    test_udp_bind_ephemeral_port(&net, IpAddress::IPV6_LOOPBACK);
+    test_udp_bind_ephemeral_port(&net, IpAddress::IPV4_UNSPECIFIED);
+    test_udp_bind_ephemeral_port(&net, IpAddress::IPV6_UNSPECIFIED);
+
+    test_udp_bind_specific_port(&net, IpAddress::IPV4_LOOPBACK);
+    test_udp_bind_specific_port(&net, IpAddress::IPV6_LOOPBACK);
+    test_udp_bind_specific_port(&net, IpAddress::IPV4_UNSPECIFIED);
+    test_udp_bind_specific_port(&net, IpAddress::IPV6_UNSPECIFIED);
+
+    test_udp_bind_addrinuse(&net, IpAddress::IPV4_LOOPBACK);
+    test_udp_bind_addrinuse(&net, IpAddress::IPV6_LOOPBACK);
+    test_udp_bind_addrinuse(&net, IpAddress::IPV4_UNSPECIFIED);
+    test_udp_bind_addrinuse(&net, IpAddress::IPV6_UNSPECIFIED);
+
+    test_udp_bind_addrnotavail(&net, RESERVED_IPV4_ADDRESS);
+    test_udp_bind_addrnotavail(&net, RESERVED_IPV6_ADDRESS);
+
+    test_udp_bind_wrong_family(&net, IpAddressFamily::Ipv4);
+    test_udp_bind_wrong_family(&net, IpAddressFamily::Ipv6);
+
+    test_udp_bind_dual_stack(&net);
+}
diff --git a/crates/test-programs/src/bin/preview2_udp_connect.rs b/crates/test-programs/src/bin/preview2_udp_connect.rs
index 5100525e8..7e21c7e79 100644
--- a/crates/test-programs/src/bin/preview2_udp_connect.rs
+++ b/crates/test-programs/src/bin/preview2_udp_connect.rs
@@ -3,6 +3,8 @@ use test_programs::wasi::sockets::network::{
 };
 use test_programs::wasi::sockets::udp::UdpSocket;
 
+const SOME_PORT: u16 = 47; // If the tests pass, this will never actually be connected to.
+
 fn test_udp_connect_disconnect_reconnect(net: &Network, family: IpAddressFamily) {
     let unspecified_addr = IpSocketAddress::new(IpAddress::new_unspecified(family), 0);
     let remote1 = IpSocketAddress::new(IpAddress::new_loopback(family), 4321);
@@ -33,9 +35,107 @@ fn test_udp_connect_disconnect_reconnect(net: &Network, family: IpAddressFamily)
     assert_eq!(client.remote_address(), Ok(remote1));
 }
 
+/// `0.0.0.0` / `::` is not a valid remote address in WASI.
+fn test_udp_connect_unspec(net: &Network, family: IpAddressFamily) {
+    let addr = IpSocketAddress::new(IpAddress::new_unspecified(family), SOME_PORT);
+    let sock = UdpSocket::new(family).unwrap();
+    sock.blocking_bind_unspecified(&net).unwrap();
+
+    assert!(matches!(
+        sock.stream(Some(addr)),
+        Err(ErrorCode::InvalidArgument)
+    ));
+}
+
+/// 0 is not a valid remote port.
+fn test_udp_connect_port_0(net: &Network, family: IpAddressFamily) {
+    let addr = IpSocketAddress::new(IpAddress::new_loopback(family), 0);
+    let sock = UdpSocket::new(family).unwrap();
+    sock.blocking_bind_unspecified(&net).unwrap();
+
+    assert!(matches!(
+        sock.stream(Some(addr)),
+        Err(ErrorCode::InvalidArgument)
+    ));
+}
+
+/// Connect should validate the address family.
+fn test_udp_connect_wrong_family(net: &Network, family: IpAddressFamily) {
+    let wrong_ip = match family {
+        IpAddressFamily::Ipv4 => IpAddress::IPV6_LOOPBACK,
+        IpAddressFamily::Ipv6 => IpAddress::IPV4_LOOPBACK,
+    };
+    let remote_addr = IpSocketAddress::new(wrong_ip, SOME_PORT);
+
+    let sock = UdpSocket::new(family).unwrap();
+    sock.blocking_bind_unspecified(&net).unwrap();
+
+    assert!(matches!(
+        sock.stream(Some(remote_addr)),
+        Err(ErrorCode::InvalidArgument)
+    ));
+}
+
+fn test_udp_connect_dual_stack(net: &Network) {
+    // Set-up:
+    let v4_server = UdpSocket::new(IpAddressFamily::Ipv4).unwrap();
+    v4_server
+        .blocking_bind(&net, IpSocketAddress::new(IpAddress::IPV4_LOOPBACK, 0))
+        .unwrap();
+
+    let v4_server_addr = v4_server.local_address().unwrap();
+    let v6_server_addr =
+        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
+        );
+    }
+}
+
 fn main() {
     let net = Network::default();
 
     test_udp_connect_disconnect_reconnect(&net, IpAddressFamily::Ipv4);
     test_udp_connect_disconnect_reconnect(&net, IpAddressFamily::Ipv6);
+
+    test_udp_connect_unspec(&net, IpAddressFamily::Ipv4);
+    test_udp_connect_unspec(&net, IpAddressFamily::Ipv6);
+
+    test_udp_connect_port_0(&net, IpAddressFamily::Ipv4);
+    test_udp_connect_port_0(&net, IpAddressFamily::Ipv6);
+
+    test_udp_connect_wrong_family(&net, IpAddressFamily::Ipv4);
+    test_udp_connect_wrong_family(&net, IpAddressFamily::Ipv6);
+
+    test_udp_connect_dual_stack(&net);
 }
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 41da701e6..171258e5e 100644
--- a/crates/test-programs/src/bin/preview2_udp_sample_application.rs
+++ b/crates/test-programs/src/bin/preview2_udp_sample_application.rs
@@ -3,7 +3,7 @@ use test_programs::wasi::sockets::network::{
 };
 use test_programs::wasi::sockets::udp::{OutgoingDatagram, UdpSocket};
 
-fn test_sample_application(family: IpAddressFamily, bind_address: IpSocketAddress) {
+fn test_udp_sample_application(family: IpAddressFamily, bind_address: IpSocketAddress) {
     let unspecified_addr = IpSocketAddress::new(IpAddress::new_unspecified(family), 0);
 
     let first_message = &[];
@@ -72,15 +72,61 @@ fn test_sample_application(family: IpAddressFamily, bind_address: IpSocketAddres
     }
 }
 
+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_sample_application(
+    test_udp_sample_application(
         IpAddressFamily::Ipv4,
         IpSocketAddress::Ipv4(Ipv4SocketAddress {
             port: 0,                 // use any free port
             address: (127, 0, 0, 1), // localhost
         }),
     );
-    test_sample_application(
+    test_udp_sample_application(
         IpAddressFamily::Ipv6,
         IpSocketAddress::Ipv6(Ipv6SocketAddress {
             port: 0,                           // use any free port
@@ -89,4 +135,6 @@ 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
new file mode 100644
index 000000000..b9cebbcb3
--- /dev/null
+++ b/crates/test-programs/src/bin/preview2_udp_sockopts.rs
@@ -0,0 +1,68 @@
+use test_programs::wasi::sockets::network::{ErrorCode, IpAddressFamily};
+use test_programs::wasi::sockets::udp::UdpSocket;
+
+fn test_udp_sockopt_defaults(family: IpAddressFamily) {
+    let sock = UdpSocket::new(family).unwrap();
+
+    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);
+}
+
+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)
+    ));
+    assert!(matches!(sock.set_unicast_hop_limit(1), Ok(_)));
+    assert!(matches!(sock.set_unicast_hop_limit(u8::MAX), Ok(_)));
+
+    assert!(matches!(sock.set_receive_buffer_size(0), Ok(_))); // Unsupported sizes should be silently capped.
+    assert!(matches!(sock.set_receive_buffer_size(u64::MAX), Ok(_))); // Unsupported sizes should be silently capped.
+    assert!(matches!(sock.set_send_buffer_size(0), Ok(_))); // Unsupported sizes should be silently capped.
+    assert!(matches!(sock.set_send_buffer_size(u64::MAX), Ok(_))); // Unsupported sizes should be silently capped.
+}
+
+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);
+
+    sock.set_receive_buffer_size(0x10000).unwrap();
+    assert_eq!(sock.receive_buffer_size().unwrap(), 0x10000);
+
+    sock.set_send_buffer_size(0x10000).unwrap();
+    assert_eq!(sock.send_buffer_size().unwrap(), 0x10000);
+}
+
+fn main() {
+    test_udp_sockopt_defaults(IpAddressFamily::Ipv4);
+    test_udp_sockopt_defaults(IpAddressFamily::Ipv6);
+
+    test_udp_sockopt_input_ranges(IpAddressFamily::Ipv4);
+    test_udp_sockopt_input_ranges(IpAddressFamily::Ipv6);
+
+    test_udp_sockopt_readback(IpAddressFamily::Ipv4);
+    test_udp_sockopt_readback(IpAddressFamily::Ipv6);
+}
diff --git a/crates/test-programs/src/bin/preview2_udp_states.rs b/crates/test-programs/src/bin/preview2_udp_states.rs
new file mode 100644
index 000000000..4081b971e
--- /dev/null
+++ b/crates/test-programs/src/bin/preview2_udp_states.rs
@@ -0,0 +1,135 @@
+use test_programs::wasi::sockets::network::{
+    ErrorCode, IpAddress, IpAddressFamily, IpSocketAddress, Network,
+};
+use test_programs::wasi::sockets::udp::UdpSocket;
+
+fn test_udp_unbound_state_invariants(family: IpAddressFamily) {
+    let sock = UdpSocket::new(family).unwrap();
+
+    // Skipping: udp::start_bind
+    assert!(matches!(sock.finish_bind(), Err(ErrorCode::NotInProgress)));
+
+    assert!(matches!(sock.stream(None), Err(ErrorCode::InvalidState)));
+
+    assert!(matches!(sock.local_address(), Err(ErrorCode::InvalidState)));
+    assert!(matches!(
+        sock.remote_address(),
+        Err(ErrorCode::InvalidState)
+    ));
+    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(_)));
+    assert!(matches!(sock.set_receive_buffer_size(16000), Ok(_)));
+    assert!(matches!(sock.send_buffer_size(), Ok(_)));
+    assert!(matches!(sock.set_send_buffer_size(16000), Ok(_)));
+}
+
+fn test_udp_bound_state_invariants(net: &Network, family: IpAddressFamily) {
+    let bind_address = IpSocketAddress::new(IpAddress::new_loopback(family), 0);
+    let sock = UdpSocket::new(family).unwrap();
+    sock.blocking_bind(net, bind_address).unwrap();
+
+    assert!(matches!(
+        sock.start_bind(net, bind_address),
+        Err(ErrorCode::InvalidState)
+    ));
+    assert!(matches!(sock.finish_bind(), Err(ErrorCode::NotInProgress)));
+    // Skipping: udp::stream
+
+    assert!(matches!(sock.local_address(), Ok(_)));
+    assert!(matches!(
+        sock.remote_address(),
+        Err(ErrorCode::InvalidState)
+    ));
+    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(_)));
+    assert!(matches!(sock.set_receive_buffer_size(16000), Ok(_)));
+    assert!(matches!(sock.send_buffer_size(), Ok(_)));
+    assert!(matches!(sock.set_send_buffer_size(16000), Ok(_)));
+}
+
+fn test_udp_connected_state_invariants(net: &Network, family: IpAddressFamily) {
+    let bind_address = IpSocketAddress::new(IpAddress::new_loopback(family), 0);
+    let connect_address = IpSocketAddress::new(IpAddress::new_loopback(family), 54321);
+    let sock = UdpSocket::new(family).unwrap();
+    sock.blocking_bind(net, bind_address).unwrap();
+    sock.stream(Some(connect_address)).unwrap();
+
+    assert!(matches!(
+        sock.start_bind(net, bind_address),
+        Err(ErrorCode::InvalidState)
+    ));
+    assert!(matches!(sock.finish_bind(), Err(ErrorCode::NotInProgress)));
+    // Skipping: udp::stream
+
+    assert!(matches!(sock.local_address(), Ok(_)));
+    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(_)));
+    assert!(matches!(sock.set_receive_buffer_size(16000), Ok(_)));
+    assert!(matches!(sock.send_buffer_size(), Ok(_)));
+    assert!(matches!(sock.set_send_buffer_size(16000), Ok(_)));
+}
+
+fn main() {
+    let net = Network::default();
+
+    test_udp_unbound_state_invariants(IpAddressFamily::Ipv4);
+    test_udp_unbound_state_invariants(IpAddressFamily::Ipv6);
+
+    test_udp_bound_state_invariants(&net, IpAddressFamily::Ipv4);
+    test_udp_bound_state_invariants(&net, IpAddressFamily::Ipv6);
+
+    test_udp_connected_state_invariants(&net, IpAddressFamily::Ipv4);
+    test_udp_connected_state_invariants(&net, IpAddressFamily::Ipv6);
+}
diff --git a/crates/test-programs/src/sockets.rs b/crates/test-programs/src/sockets.rs
index 2d1a0c99d..55426cdf7 100644
--- a/crates/test-programs/src/sockets.rs
+++ b/crates/test-programs/src/sockets.rs
@@ -140,6 +140,13 @@ impl UdpSocket {
             }
         }
     }
+
+    pub fn blocking_bind_unspecified(&self, network: &Network) -> Result<(), ErrorCode> {
+        let ip = IpAddress::new_unspecified(self.address_family());
+        let port = 0;
+
+        self.blocking_bind(network, IpSocketAddress::new(ip, port))
+    }
 }
 
 impl OutgoingDatagramStream {
diff --git a/crates/wasi/tests/all/async_.rs b/crates/wasi/tests/all/async_.rs
index 8e44d8210..ba6a76f3d 100644
--- a/crates/wasi/tests/all/async_.rs
+++ b/crates/wasi/tests/all/async_.rs
@@ -323,8 +323,12 @@ async fn preview2_tcp_bind() {
     run(PREVIEW2_TCP_BIND_COMPONENT, false).await.unwrap()
 }
 #[test_log::test(tokio::test(flavor = "multi_thread"))]
-async fn preview2_udp_connect() {
-    run(PREVIEW2_UDP_CONNECT_COMPONENT, false).await.unwrap()
+async fn preview2_tcp_connect() {
+    run(PREVIEW2_TCP_CONNECT_COMPONENT, false).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn preview2_udp_sockopts() {
+    run(PREVIEW2_UDP_SOCKOPTS_COMPONENT, false).await.unwrap()
 }
 #[test_log::test(tokio::test(flavor = "multi_thread"))]
 async fn preview2_udp_sample_application() {
@@ -333,8 +337,16 @@ async fn preview2_udp_sample_application() {
         .unwrap()
 }
 #[test_log::test(tokio::test(flavor = "multi_thread"))]
-async fn preview2_tcp_connect() {
-    run(PREVIEW2_TCP_CONNECT_COMPONENT, false).await.unwrap()
+async fn preview2_udp_states() {
+    run(PREVIEW2_UDP_STATES_COMPONENT, false).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn preview2_udp_bind() {
+    run(PREVIEW2_UDP_BIND_COMPONENT, false).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn preview2_udp_connect() {
+    run(PREVIEW2_UDP_CONNECT_COMPONENT, false).await.unwrap()
 }
 #[test_log::test(tokio::test(flavor = "multi_thread"))]
 async fn preview2_stream_pollable_correct() {
diff --git a/crates/wasi/tests/all/sync.rs b/crates/wasi/tests/all/sync.rs
index 25e7b5ba3..09094d166 100644
--- a/crates/wasi/tests/all/sync.rs
+++ b/crates/wasi/tests/all/sync.rs
@@ -266,16 +266,28 @@ fn preview2_tcp_bind() {
     run(PREVIEW2_TCP_BIND_COMPONENT, false).unwrap()
 }
 #[test_log::test]
-fn preview2_udp_connect() {
-    run(PREVIEW2_UDP_CONNECT_COMPONENT, false).unwrap()
+fn preview2_tcp_connect() {
+    run(PREVIEW2_TCP_CONNECT_COMPONENT, false).unwrap()
+}
+#[test_log::test]
+fn preview2_udp_sockopts() {
+    run(PREVIEW2_UDP_SOCKOPTS_COMPONENT, false).unwrap()
 }
 #[test_log::test]
 fn preview2_udp_sample_application() {
     run(PREVIEW2_UDP_SAMPLE_APPLICATION_COMPONENT, false).unwrap()
 }
 #[test_log::test]
-fn preview2_tcp_connect() {
-    run(PREVIEW2_TCP_CONNECT_COMPONENT, false).unwrap()
+fn preview2_udp_states() {
+    run(PREVIEW2_UDP_STATES_COMPONENT, false).unwrap()
+}
+#[test_log::test]
+fn preview2_udp_bind() {
+    run(PREVIEW2_UDP_BIND_COMPONENT, false).unwrap()
+}
+#[test_log::test]
+fn preview2_udp_connect() {
+    run(PREVIEW2_UDP_CONNECT_COMPONENT, false).unwrap()
 }
 #[test_log::test]
 fn preview2_stream_pollable_correct() {