From 60f0a0369b1829f02205a1a77246412cf9366645 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Sun, 20 Sep 2026 17:36:19 +0800 Subject: [PATCH] Fix half-close not propagating through connection wrappers --- common/badtls/read_wait.go | 4 ++++ common/tls/reality_client.go | 2 +- common/tls/reality_server.go | 2 +- go.mod | 2 +- go.sum | 4 ++-- route/conn.go | 16 ++++++++++------ 6 files changed, 19 insertions(+), 11 deletions(-) diff --git a/common/badtls/read_wait.go b/common/badtls/read_wait.go index 8448b1a2..982f0f17 100644 --- a/common/badtls/read_wait.go +++ b/common/badtls/read_wait.go @@ -80,3 +80,7 @@ func (c *ReadWaitConn) Upstream() any { func (c *ReadWaitConn) ReaderReplaceable() bool { return true } + +func (c *ReadWaitConn) WriterReplaceable() bool { + return true +} diff --git a/common/tls/reality_client.go b/common/tls/reality_client.go index af64ef6f..13f88e38 100644 --- a/common/tls/reality_client.go +++ b/common/tls/reality_client.go @@ -343,5 +343,5 @@ func (c *realityClientConnWrapper) ReaderReplaceable() bool { } func (c *realityClientConnWrapper) WriterReplaceable() bool { - return true + return false } diff --git a/common/tls/reality_server.go b/common/tls/reality_server.go index 10d60618..8ad59e14 100644 --- a/common/tls/reality_server.go +++ b/common/tls/reality_server.go @@ -254,5 +254,5 @@ func (c *realityConnWrapper) ReaderReplaceable() bool { } func (c *realityConnWrapper) WriterReplaceable() bool { - return true + return false } diff --git a/go.mod b/go.mod index 4cd26fa1..af91c67f 100644 --- a/go.mod +++ b/go.mod @@ -45,7 +45,7 @@ require ( github.com/sagernet/netlink v0.0.0-20260814022025-64455d367bbf github.com/sagernet/nftables v0.3.0-mod.4 github.com/sagernet/quic-go v0.61.0-sing-box-mod.7 - github.com/sagernet/sing v0.9.6-0.20260920042621-643b9d01347d + github.com/sagernet/sing v0.9.6-0.20260920093512-38496bf4f1df github.com/sagernet/sing-cloudflared v0.1.3-0.20260706062323-d9787e794aa3 github.com/sagernet/sing-mux v0.3.8 github.com/sagernet/sing-openconnect v0.0.0-20260810065514-53aa8058f8df diff --git a/go.sum b/go.sum index 1d604331..56b41eb1 100644 --- a/go.sum +++ b/go.sum @@ -318,8 +318,8 @@ github.com/sagernet/nftables v0.3.0-mod.4 h1:vnOtcDYeSXv2e5RoRuGH0lrpttQFJ8iC4IC github.com/sagernet/nftables v0.3.0-mod.4/go.mod h1:8kslHG4VvYNihcco+i6uxIX7qbT8A56T0y5q7U44ZaQ= github.com/sagernet/quic-go v0.61.0-sing-box-mod.7 h1:sW2O+DoNF+my1PGlSZ3zG2N+fdxLR3DC0l4sXCM0lBE= github.com/sagernet/quic-go v0.61.0-sing-box-mod.7/go.mod h1:hmLC8GJPp+BrpBgrgJnacvI4fZGIhasuy1ROJJ8GR4E= -github.com/sagernet/sing v0.9.6-0.20260920042621-643b9d01347d h1:4HL9hWcaBCAyIm4bPWxTtWa4Oz4YcLgSvLcjVIQdpdY= -github.com/sagernet/sing v0.9.6-0.20260920042621-643b9d01347d/go.mod h1:K3Owt3xPhHugvlnlPPxZJ/exXdaJfEPOTNorGk4AXjo= +github.com/sagernet/sing v0.9.6-0.20260920093512-38496bf4f1df h1:KZSoa3WMDGrr7rCJOUfg0mgxvTkSWcsPk4dzRdt2+YQ= +github.com/sagernet/sing v0.9.6-0.20260920093512-38496bf4f1df/go.mod h1:K3Owt3xPhHugvlnlPPxZJ/exXdaJfEPOTNorGk4AXjo= github.com/sagernet/sing-cloudflared v0.1.3-0.20260706062323-d9787e794aa3 h1:3y6++yIa8XlDhxPkpR4p+7RUHVY2KTP9CPIGnWmOlO8= github.com/sagernet/sing-cloudflared v0.1.3-0.20260706062323-d9787e794aa3/go.mod h1:XEqEDYRCAYLaoPjZ1ifVWJg5iWAJHL2gOAXe/PM28Cg= github.com/sagernet/sing-mux v0.3.8 h1:mM0TLUkr36aEhgUHi9Qh60qcAbD2fZcmnJ30937xxrY= diff --git a/route/conn.go b/route/conn.go index c2ab90d5..fe718b50 100644 --- a/route/conn.go +++ b/route/conn.go @@ -274,13 +274,17 @@ func (m *ConnectionManager) connectionCopy(ctx context.Context, source net.Conn, _, err := bufio.CopyWithIncreateBuffer(destination, source, bufio.DefaultIncreaseBufferAfter, bufio.DefaultBatchSize) if err != nil { common.Close(source, destination) - } else if duplexDst, isDuplex := destination.(N.WriteCloser); isDuplex { - err = duplexDst.CloseWrite() - if err != nil { - common.Close(source, destination) - } } else { - destination.Close() + destinationWriter, _ := N.UnwrapCountWriter(destination, nil) + duplexDst, isDuplex := N.UnwrapWriter(destinationWriter).(N.WriteCloser) + if isDuplex { + err = duplexDst.CloseWrite() + if err != nil { + common.Close(source, destination) + } + } else { + destination.Close() + } } if done.Swap(true) { if onClose != nil {