Przeglądaj źródła

Fix leaks and add test

世界 3 lat temu
rodzic
commit
407509c985

+ 15 - 7
common/mux/service.go

@@ -14,6 +14,7 @@ import (
 	M "github.com/sagernet/sing/common/metadata"
 	N "github.com/sagernet/sing/common/network"
 	"github.com/sagernet/sing/common/rw"
+	"github.com/sagernet/sing/common/task"
 )
 
 func NewConnection(ctx context.Context, router adapter.Router, errorHandler E.Handler, logger log.ContextLogger, conn net.Conn, metadata adapter.InboundContext) error {
@@ -25,14 +26,21 @@ func NewConnection(ctx context.Context, router adapter.Router, errorHandler E.Ha
 	if err != nil {
 		return err
 	}
-	var stream net.Conn
-	for {
-		stream, err = session.Accept()
-		if err != nil {
-			return err
+	var group task.Group
+	group.Append0(func(ctx context.Context) error {
+		var stream net.Conn
+		for {
+			stream, err = session.Accept()
+			if err != nil {
+				return err
+			}
+			go newConnection(ctx, router, errorHandler, logger, stream, metadata)
 		}
-		go newConnection(ctx, router, errorHandler, logger, stream, metadata)
-	}
+	})
+	group.Cleanup(func() {
+		session.Close()
+	})
+	return group.Run(ctx)
 }
 
 func newConnection(ctx context.Context, router adapter.Router, errorHandler E.Handler, logger log.ContextLogger, stream net.Conn, metadata adapter.InboundContext) {

+ 1 - 1
go.mod

@@ -27,7 +27,7 @@ require (
 	github.com/sagernet/sing-dns v0.0.0-20220915084601-812e0864b45b
 	github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6
 	github.com/sagernet/sing-tun v0.0.0-20220922083325-80ee99472704
-	github.com/sagernet/sing-vmess v0.0.0-20220921140858-b6a1bdee672f
+	github.com/sagernet/sing-vmess v0.0.0-20220923035311-d70afe4ab2c9
 	github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195
 	github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e
 	github.com/spf13/cobra v1.5.0

+ 2 - 2
go.sum

@@ -153,8 +153,8 @@ github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 h1:JJfDe
 github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6/go.mod h1:EX3RbZvrwAkPI2nuGa78T2iQXmrkT+/VQtskjou42xM=
 github.com/sagernet/sing-tun v0.0.0-20220922083325-80ee99472704 h1:DOQQXQbB2gq4n2FuMHrL07HRs2naCCsuiu/9l1JFb9A=
 github.com/sagernet/sing-tun v0.0.0-20220922083325-80ee99472704/go.mod h1:5AhPUv9jWDQ3pv3Mj78SL/1TSjhoaj6WNASxRKLqXqM=
-github.com/sagernet/sing-vmess v0.0.0-20220921140858-b6a1bdee672f h1:xyJ3Wbibcug4DxLi/FCHX2Td667SfieyZv645b8+eEE=
-github.com/sagernet/sing-vmess v0.0.0-20220921140858-b6a1bdee672f/go.mod h1:bwhAdSNET1X+j9DOXGj9NIQR39xgcWIk1rOQ9lLD+gM=
+github.com/sagernet/sing-vmess v0.0.0-20220923035311-d70afe4ab2c9 h1:w7JlEa4xHXuuPTL3Opb+Z5f/l66SYi54rSfobzuxSF8=
+github.com/sagernet/sing-vmess v0.0.0-20220923035311-d70afe4ab2c9/go.mod h1:bwhAdSNET1X+j9DOXGj9NIQR39xgcWIk1rOQ9lLD+gM=
 github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195 h1:5VBIbVw9q7aKbrFdT83mjkyvQ+VaRsQ6yflTepfln38=
 github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195/go.mod h1:yedWtra8nyGJ+SyI+ziwuaGMzBatbB10P1IOOZbbSK8=
 github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e h1:7uw2njHFGE+VpWamge6o56j2RWk4omF6uLKKxMmcWvs=

+ 4 - 1
outbound/hysteria.go

@@ -38,6 +38,7 @@ type Hysteria struct {
 	recvBPS      uint64
 	connAccess   sync.Mutex
 	conn         quic.Connection
+	rawConn      net.Conn
 	udpAccess    sync.RWMutex
 	udpSessions  map[uint32]chan *hysteria.UDPMessage
 	udpDefragger hysteria.Defragger
@@ -149,7 +150,6 @@ func (h *Hysteria) offer(ctx context.Context) (quic.Connection, error) {
 	if err != nil {
 		return nil, err
 	}
-	h.conn = conn
 	if common.Contains(h.network, N.NetworkUDP) {
 		for _, session := range h.udpSessions {
 			close(session)
@@ -201,6 +201,8 @@ func (h *Hysteria) offerNew(ctx context.Context) (quic.Connection, error) {
 		return nil, E.New("remote error: ", serverHello.Message)
 	}
 	quicConn.SetCongestionControl(hysteria.NewBrutalSender(congestion.ByteCount(serverHello.RecvBPS)))
+	h.conn = quicConn
+	h.rawConn = udpConn
 	return quicConn, nil
 }
 
@@ -240,6 +242,7 @@ func (h *Hysteria) Close() error {
 	defer h.udpAccess.Unlock()
 	if h.conn != nil {
 		h.conn.CloseWithError(0, "")
+		h.rawConn.Close()
 	}
 	for _, session := range h.udpSessions {
 		close(session)

+ 4 - 0
outbound/trojan.go

@@ -103,6 +103,10 @@ func (h *Trojan) NewPacketConnection(ctx context.Context, conn N.PacketConn, met
 	return NewPacketConnection(ctx, h, conn, metadata)
 }
 
+func (h *Trojan) Close() error {
+	return common.Close(h.multiplexDialer, h.transport)
+}
+
 type trojanDialer Trojan
 
 func (h *trojanDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {

+ 4 - 4
outbound/vless.go

@@ -74,10 +74,6 @@ func NewVLESS(ctx context.Context, router adapter.Router, logger log.ContextLogg
 	return outbound, nil
 }
 
-func (h *VLESS) Close() error {
-	return common.Close(h.transport)
-}
-
 func (h *VLESS) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
 	ctx, metadata := adapter.AppendContext(ctx)
 	metadata.Outbound = h.tag
@@ -145,3 +141,7 @@ func (h *VLESS) NewConnection(ctx context.Context, conn net.Conn, metadata adapt
 func (h *VLESS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
 	return NewPacketConnection(ctx, h, conn, metadata)
 }
+
+func (h *VLESS) Close() error {
+	return common.Close(h.transport)
+}

+ 1 - 1
outbound/vmess.go

@@ -96,7 +96,7 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
 }
 
 func (h *VMess) Close() error {
-	return common.Close(h.transport)
+	return common.Close(h.multiplexDialer, h.transport)
 }
 
 func (h *VMess) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {

+ 4 - 5
outbound/wireguard.go

@@ -162,9 +162,8 @@ func (w *WireGuard) Start() error {
 }
 
 func (w *WireGuard) Close() error {
-	return common.Close(
-		w.tunDevice,
-		common.PtrOrNil(w.device),
-		common.PtrOrNil(w.bind),
-	)
+	if w.device != nil {
+		w.device.Close()
+	}
+	return common.Close(w.tunDevice)
 }

+ 11 - 2
test/box_test.go

@@ -15,9 +15,14 @@ import (
 	"github.com/sagernet/sing/protocol/socks"
 
 	"github.com/stretchr/testify/require"
+	"go.uber.org/goleak"
 )
 
-func startInstance(t *testing.T, options option.Options) {
+func TestMain(m *testing.M) {
+	goleak.VerifyTestMain(m)
+}
+
+func startInstance(t *testing.T, options option.Options) *box.Box {
 	if debug.Enabled {
 		options.Log = &option.LogOptions{
 			Level: "trace",
@@ -27,10 +32,12 @@ func startInstance(t *testing.T, options option.Options) {
 			Level: "warning",
 		}
 	}
+	// ctx := context.Background()
+	ctx, cancel := context.WithCancel(context.Background())
 	var instance *box.Box
 	var err error
 	for retry := 0; retry < 3; retry++ {
-		instance, err = box.New(context.Background(), options)
+		instance, err = box.New(ctx, options)
 		require.NoError(t, err)
 		err = instance.Start()
 		if err != nil {
@@ -42,7 +49,9 @@ func startInstance(t *testing.T, options option.Options) {
 	require.NoError(t, err)
 	t.Cleanup(func() {
 		instance.Close()
+		cancel()
 	})
+	return instance
 }
 
 func testSuit(t *testing.T, clientPort uint16, testPort uint16) {

+ 0 - 7
test/clash_test.go

@@ -7,7 +7,6 @@ import (
 	"errors"
 	"io"
 	"net"
-	"net/http"
 	_ "net/http/pprof"
 	"net/netip"
 	"sync"
@@ -101,12 +100,6 @@ func init() {
 
 		io.Copy(io.Discard, imageStream)
 	}
-	go func() {
-		err = http.ListenAndServe("0.0.0.0:8965", nil)
-		if err != nil {
-			log.Debug(err)
-		}
-	}()
 }
 
 func newPingPongPair() (chan []byte, chan []byte, func(t *testing.T) error) {

+ 5 - 4
test/go.mod

@@ -10,10 +10,11 @@ require (
 	github.com/docker/docker v20.10.18+incompatible
 	github.com/docker/go-connections v0.4.0
 	github.com/gofrs/uuid v4.3.0+incompatible
-	github.com/sagernet/sing v0.0.0-20220916071326-834794b006ea
+	github.com/sagernet/sing v0.0.0-20220921101604-86d7d510231f
 	github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6
 	github.com/spyzhov/ajson v0.7.1
 	github.com/stretchr/testify v1.8.0
+	go.uber.org/goleak v1.2.0
 	golang.org/x/net v0.0.0-20220909164309-bea034e7d591
 )
 
@@ -67,8 +68,8 @@ require (
 	github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
 	github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb // indirect
 	github.com/sagernet/sing-dns v0.0.0-20220915084601-812e0864b45b // indirect
-	github.com/sagernet/sing-tun v0.0.0-20220916073459-0032242c9617 // indirect
-	github.com/sagernet/sing-vmess v0.0.0-20220917033734-9b634758039d // indirect
+	github.com/sagernet/sing-tun v0.0.0-20220922083325-80ee99472704 // indirect
+	github.com/sagernet/sing-vmess v0.0.0-20220923035311-d70afe4ab2c9 // indirect
 	github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195 // indirect
 	github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e // indirect
 	github.com/sirupsen/logrus v1.9.0 // indirect
@@ -78,7 +79,7 @@ require (
 	go.uber.org/multierr v1.6.0 // indirect
 	go.uber.org/zap v1.22.0 // indirect
 	go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d // indirect
-	golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 // indirect
+	golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0 // indirect
 	golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
 	golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
 	golang.org/x/sys v0.0.0-20220913120320-3275c407cedc // indirect

+ 11 - 9
test/go.sum

@@ -165,16 +165,16 @@ github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb h1:wc0yQ+SBn4TaTY
 github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb/go.mod h1:MIccjRKnPTjWwAOpl+AUGWOkzyTd9tERytudxu+1ra4=
 github.com/sagernet/sing v0.0.0-20220812082120-05f9836bff8f/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
 github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
-github.com/sagernet/sing v0.0.0-20220916071326-834794b006ea h1:ZAWvZdeByPBBz3Vs+w3Erbh+DDo7D4biokoPhXl0nNU=
-github.com/sagernet/sing v0.0.0-20220916071326-834794b006ea/go.mod h1:x3NHUeJBQwV75L51zwmLKQdLtRvR+M4PmXkfQtU1vIY=
+github.com/sagernet/sing v0.0.0-20220921101604-86d7d510231f h1:GX416thAwyc0vHBOal/qplvdhFgYO2dHD5GqADCJ0Ig=
+github.com/sagernet/sing v0.0.0-20220921101604-86d7d510231f/go.mod h1:x3NHUeJBQwV75L51zwmLKQdLtRvR+M4PmXkfQtU1vIY=
 github.com/sagernet/sing-dns v0.0.0-20220915084601-812e0864b45b h1:cXCMNJ9heZ+c6l+qUcku60x9KyXo4SOAaJfg/6spOmU=
 github.com/sagernet/sing-dns v0.0.0-20220915084601-812e0864b45b/go.mod h1:SrvWLfOSlnFmH32CWXicfilAGgIXR0VjrH6yRbuXYww=
 github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 h1:JJfDeYYhWunvtxsU/mOVNTmFQmnzGx9dY034qG6G3g4=
 github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6/go.mod h1:EX3RbZvrwAkPI2nuGa78T2iQXmrkT+/VQtskjou42xM=
-github.com/sagernet/sing-tun v0.0.0-20220916073459-0032242c9617 h1:fNTNmylhB/UjoBLusmrFu2B1fat4OCkDkQXTgrE7ZsE=
-github.com/sagernet/sing-tun v0.0.0-20220916073459-0032242c9617/go.mod h1:5AhPUv9jWDQ3pv3Mj78SL/1TSjhoaj6WNASxRKLqXqM=
-github.com/sagernet/sing-vmess v0.0.0-20220917033734-9b634758039d h1:/GNWxSrQj4chFYk2jahIXNPdvUa9DW8IdgyqYFbq9oQ=
-github.com/sagernet/sing-vmess v0.0.0-20220917033734-9b634758039d/go.mod h1:u66Vv7NHXJWfeAmhh7JuJp/cwxmuQlM56QoZ7B7Mmd0=
+github.com/sagernet/sing-tun v0.0.0-20220922083325-80ee99472704 h1:DOQQXQbB2gq4n2FuMHrL07HRs2naCCsuiu/9l1JFb9A=
+github.com/sagernet/sing-tun v0.0.0-20220922083325-80ee99472704/go.mod h1:5AhPUv9jWDQ3pv3Mj78SL/1TSjhoaj6WNASxRKLqXqM=
+github.com/sagernet/sing-vmess v0.0.0-20220923035311-d70afe4ab2c9 h1:w7JlEa4xHXuuPTL3Opb+Z5f/l66SYi54rSfobzuxSF8=
+github.com/sagernet/sing-vmess v0.0.0-20220923035311-d70afe4ab2c9/go.mod h1:bwhAdSNET1X+j9DOXGj9NIQR39xgcWIk1rOQ9lLD+gM=
 github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195 h1:5VBIbVw9q7aKbrFdT83mjkyvQ+VaRsQ6yflTepfln38=
 github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195/go.mod h1:yedWtra8nyGJ+SyI+ziwuaGMzBatbB10P1IOOZbbSK8=
 github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e h1:7uw2njHFGE+VpWamge6o56j2RWk4omF6uLKKxMmcWvs=
@@ -205,8 +205,9 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe
 go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
 go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
 go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
-go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
 go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
+go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
+go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo=
 go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
 go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
 go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
@@ -220,8 +221,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
 golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM=
-golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0 h1:a5Yg6ylndHHYJqIPrdq0AhvR6KTvDTAvgBtaidhEevY=
+golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA=
 golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
@@ -229,6 +230,7 @@ golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTk
 golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
 golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
 golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=

+ 61 - 0
test/mux_cool_test.go

@@ -107,3 +107,64 @@ func TestMuxCoolClient(t *testing.T) {
 	})
 	testSuit(t, clientPort, testPort)
 }
+
+func TestMuxCoolSelf(t *testing.T) {
+	user := newUUID()
+	startInstance(t, option.Options{
+		Inbounds: []option.Inbound{
+			{
+				Type: C.TypeMixed,
+				Tag:  "mixed-in",
+				MixedOptions: option.HTTPMixedInboundOptions{
+					ListenOptions: option.ListenOptions{
+						Listen:     option.ListenAddress(netip.IPv4Unspecified()),
+						ListenPort: clientPort,
+					},
+				},
+			},
+			{
+				Type: C.TypeVMess,
+				VMessOptions: option.VMessInboundOptions{
+					ListenOptions: option.ListenOptions{
+						Listen:     option.ListenAddress(netip.IPv4Unspecified()),
+						ListenPort: serverPort,
+					},
+					Users: []option.VMessUser{
+						{
+							Name: "sekai",
+							UUID: user.String(),
+						},
+					},
+				},
+			},
+		},
+		Outbounds: []option.Outbound{
+			{
+				Type: C.TypeDirect,
+			},
+			{
+				Type: C.TypeVMess,
+				Tag:  "vmess-out",
+				VMessOptions: option.VMessOutboundOptions{
+					ServerOptions: option.ServerOptions{
+						Server:     "127.0.0.1",
+						ServerPort: serverPort,
+					},
+					UUID:           user.String(),
+					PacketEncoding: "xudp",
+				},
+			},
+		},
+		Route: &option.RouteOptions{
+			Rules: []option.Rule{
+				{
+					DefaultOptions: option.DefaultRule{
+						Inbound:  []string{"mixed-in"},
+						Outbound: "vmess-out",
+					},
+				},
+			},
+		},
+	})
+	testSuit(t, clientPort, testPort)
+}

+ 4 - 8
test/mux_test.go

@@ -17,18 +17,14 @@ var muxProtocols = []mux.Protocol{
 	mux.ProtocolSMux,
 }
 
-func TestShadowsocksMux(t *testing.T) {
-	for _, protocol := range muxProtocols {
-		t.Run(protocol.String(), func(t *testing.T) {
-			testShadowsocksMux(t, protocol.String())
-		})
-	}
+func TestVMessSMux(t *testing.T) {
+	testVMessMux(t, mux.ProtocolSMux.String())
 }
 
-func TestVMessMux(t *testing.T) {
+func TestShadowsocksMux(t *testing.T) {
 	for _, protocol := range muxProtocols {
 		t.Run(protocol.String(), func(t *testing.T) {
-			testVMessMux(t, protocol.String())
+			testShadowsocksMux(t, protocol.String())
 		})
 	}
 }

+ 1 - 1
test/vmess_test.go

@@ -18,7 +18,7 @@ func newUUID() uuid.UUID {
 	return user
 }
 
-func _TestVMessAuto(t *testing.T) {
+func TestVMessAuto(t *testing.T) {
 	security := "auto"
 	t.Run("self", func(t *testing.T) {
 		testVMessSelf(t, security, 0, false, false, false)

+ 8 - 2
transport/v2rayquic/client.go

@@ -25,8 +25,9 @@ type Client struct {
 	serverAddr M.Socksaddr
 	tlsConfig  *tls.STDConfig
 	quicConfig *quic.Config
-	conn       quic.Connection
 	connAccess sync.Mutex
+	conn       quic.Connection
+	rawConn    net.Conn
 }
 
 func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayQUICOptions, tlsConfig tls.Config) (adapter.V2RayClientTransport, error) {
@@ -64,7 +65,6 @@ func (c *Client) offer() (quic.Connection, error) {
 	if err != nil {
 		return nil, err
 	}
-	c.conn = conn
 	return conn, nil
 }
 
@@ -80,6 +80,8 @@ func (c *Client) offerNew() (quic.Connection, error) {
 		packetConn.Close()
 		return nil, err
 	}
+	c.conn = quicConn
+	c.rawConn = udpConn
 	return quicConn, nil
 }
 
@@ -94,3 +96,7 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
 	}
 	return &hysteria.StreamWrapper{Conn: conn, Stream: stream}, nil
 }
+
+func (c *Client) Close() error {
+	return common.Close(c.conn, c.rawConn)
+}

+ 23 - 1
transport/wireguard/client_bind.go

@@ -20,6 +20,7 @@ type ClientBind struct {
 	peerAddr   M.Socksaddr
 	connAccess sync.Mutex
 	conn       *wireConn
+	done       chan struct{}
 }
 
 func NewClientBind(ctx context.Context, dialer N.Dialer, peerAddr M.Socksaddr) *ClientBind {
@@ -63,6 +64,12 @@ func (c *ClientBind) connect() (*wireConn, error) {
 }
 
 func (c *ClientBind) Open(port uint16) (fns []conn.ReceiveFunc, actualPort uint16, err error) {
+	select {
+	case <-c.done:
+		err = net.ErrClosed
+		return
+	default:
+	}
 	return []conn.ReceiveFunc{c.receive}, 0, nil
 }
 
@@ -75,7 +82,12 @@ func (c *ClientBind) receive(b []byte) (n int, ep conn.Endpoint, err error) {
 	n, err = udpConn.Read(b)
 	if err != nil {
 		udpConn.Close()
-		err = &wireError{err}
+		select {
+		case <-c.done:
+		default:
+			err = &wireError{err}
+		}
+		return
 	}
 	ep = Endpoint(c.peerAddr)
 	return
@@ -85,6 +97,16 @@ func (c *ClientBind) Close() error {
 	c.connAccess.Lock()
 	defer c.connAccess.Unlock()
 	common.Close(common.PtrOrNil(c.conn))
+	if c.done == nil {
+		c.done = make(chan struct{})
+		return nil
+	}
+	select {
+	case <-c.done:
+		return net.ErrClosed
+	default:
+		close(c.done)
+	}
 	return nil
 }
 

+ 2 - 4
transport/wireguard/device_stack.go

@@ -35,7 +35,6 @@ type StackDevice struct {
 	events     chan tun.Event
 	outbound   chan *stack.PacketBuffer
 	dispatcher stack.NetworkDispatcher
-	done       chan struct{}
 	addr4      tcpip.Address
 	addr6      tcpip.Address
 }
@@ -51,7 +50,6 @@ func NewStackDevice(localAddresses []netip.Prefix, mtu uint32) (*StackDevice, er
 		mtu:      mtu,
 		events:   make(chan tun.Event, 1),
 		outbound: make(chan *stack.PacketBuffer, 256),
-		done:     make(chan struct{}),
 	}
 	err := ipStack.CreateNIC(defaultNIC, (*wireEndpoint)(tunDevice))
 	if err != nil {
@@ -193,11 +191,11 @@ func (w *StackDevice) Events() chan tun.Event {
 
 func (w *StackDevice) Close() error {
 	select {
-	case <-w.done:
+	case <-w.events:
 		return os.ErrClosed
 	default:
+		close(w.events)
 	}
-	close(w.done)
 	w.stack.Close()
 	for _, endpoint := range w.stack.CleanupEndpoints() {
 		endpoint.Abort()

+ 6 - 0
transport/wireguard/device_system.go

@@ -106,5 +106,11 @@ func (w *SystemDevice) Events() chan wgTun.Event {
 }
 
 func (w *SystemDevice) Close() error {
+	select {
+	case <-w.events:
+		return os.ErrClosed
+	default:
+		close(w.events)
+	}
 	return w.device.Close()
 }