diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 86352a15..bbf8e721 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -126,7 +126,7 @@ jobs:
shell: bash
- name: Setup Go
- uses: actions/setup-go@v3
+ uses: actions/setup-go@v4
with:
go-version: "1.20"
check-latest: true
@@ -223,7 +223,7 @@ jobs:
working-directory: bin
- name: Delete current release assets
- uses: andreaswilli/delete-release-assets-action@v2.0.0
+ uses: 8Mi-Tech/delete-release-assets-action@main
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
tag: Prerelease-${{ github.ref_name }}
@@ -246,18 +246,14 @@ jobs:
Release created at ${{ env.BUILDTIME }}
Synchronize ${{ github.ref_name }} branch code updates, keeping only the latest version
- ### release version
- `default(not specified in file name)`: compiled with GOAMD64=v3
- `cgo`: support lwip tun stack, compiled with GOAMD64=v1
- `compatible`: compiled with GOAMD64=v1
- Check details between different architectural levels [here](https://github.com/golang/go/wiki/MinimumRequirements#amd64).
+ [我应该下载哪个文件? / Which file should I download?](https://github.com/MetaCubeX/Clash.Meta/wiki/FAQ)
+ [查看文档 / Docs](https://metacubex.github.io/Meta-Docs/)
EOF
- name: Upload Prerelease
uses: softprops/action-gh-release@v1
if: ${{ success() }}
with:
- tag: ${{ github.ref_name }}
tag_name: Prerelease-${{ github.ref_name }}
files: |
bin/*
@@ -284,7 +280,6 @@ jobs:
uses: softprops/action-gh-release@v1
if: ${{ success() }}
with:
- tag: ${{ github.ref_name }}
tag_name: ${{ github.ref_name }}
files: bin/*
generate_release_notes: true
@@ -309,10 +304,10 @@ jobs:
working-directory: bin
- name: Set up QEMU
- uses: docker/setup-qemu-action@v1
+ uses: docker/setup-qemu-action@v2
- name: Setup Docker buildx
- uses: docker/setup-buildx-action@v1
+ uses: docker/setup-buildx-action@v2
with:
version: latest
@@ -320,7 +315,7 @@ jobs:
# https://github.com/docker/metadata-action
- name: Extract Docker metadata
id: meta
- uses: docker/metadata-action@v3
+ uses: docker/metadata-action@v4
with:
images: ${{ env.REGISTRY }}/${{ secrets.DOCKERHUB_ACCOUNT }}/${{secrets.DOCKERHUB_REPO}}
- name: Show files
@@ -329,7 +324,7 @@ jobs:
ls bin/
- name: Log into registry
if: github.event_name != 'pull_request'
- uses: docker/login-action@v1
+ uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ secrets.DOCKER_HUB_USER }}
@@ -339,7 +334,7 @@ jobs:
# https://github.com/docker/build-push-action
- name: Build and push Docker image
id: build-and-push
- uses: docker/build-push-action@v2
+ uses: docker/build-push-action@v4
with:
context: .
file: ./Dockerfile
diff --git a/adapter/inbound/addition.go b/adapter/inbound/addition.go
index 5966e784..47307ed7 100644
--- a/adapter/inbound/addition.go
+++ b/adapter/inbound/addition.go
@@ -16,6 +16,12 @@ func WithInName(name string) Addition {
}
}
+func WithInUser(user string) Addition {
+ return func(metadata *C.Metadata) {
+ metadata.InUser = user
+ }
+}
+
func WithSpecialRules(specialRules string) Addition {
return func(metadata *C.Metadata) {
metadata.SpecialRules = specialRules
diff --git a/adapter/inbound/socket.go b/adapter/inbound/socket.go
index a6b1288c..e41ee925 100644
--- a/adapter/inbound/socket.go
+++ b/adapter/inbound/socket.go
@@ -30,21 +30,18 @@ func NewSocket(target socks5.Addr, conn net.Conn, source C.Type, additions ...Ad
return context.NewConnContext(conn, metadata)
}
-func NewInner(conn net.Conn, dst string, host string) *context.ConnContext {
+func NewInner(conn net.Conn, address string) *context.ConnContext {
metadata := &C.Metadata{}
metadata.NetWork = C.TCP
metadata.Type = C.INNER
metadata.DNSMode = C.DNSNormal
- metadata.Host = host
metadata.Process = C.ClashName
- if h, port, err := net.SplitHostPort(dst); err == nil {
+ if h, port, err := net.SplitHostPort(address); err == nil {
metadata.DstPort = port
- if host == "" {
- if ip, err := netip.ParseAddr(h); err == nil {
- metadata.DstIP = ip
- } else {
- metadata.Host = h
- }
+ if ip, err := netip.ParseAddr(h); err == nil {
+ metadata.DstIP = ip
+ } else {
+ metadata.Host = h
}
}
diff --git a/adapter/outbound/base.go b/adapter/outbound/base.go
index 367638b8..f2ce56c9 100644
--- a/adapter/outbound/base.go
+++ b/adapter/outbound/base.go
@@ -45,8 +45,8 @@ func (b *Base) Type() C.AdapterType {
return b.tp
}
-// StreamConn implements C.ProxyAdapter
-func (b *Base) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
+// StreamConnContext implements C.ProxyAdapter
+func (b *Base) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.Metadata) (net.Conn, error) {
return c, C.ErrNotSupport
}
@@ -220,7 +220,7 @@ func NewConn(c net.Conn, a C.ProxyAdapter) C.Conn {
}
type packetConn struct {
- net.PacketConn
+ N.EnhancePacketConn
chain C.Chain
adapterName string
connID string
@@ -242,15 +242,28 @@ func (c *packetConn) AppendToChains(a C.ProxyAdapter) {
}
func (c *packetConn) LocalAddr() net.Addr {
- lAddr := c.PacketConn.LocalAddr()
+ lAddr := c.EnhancePacketConn.LocalAddr()
return N.NewCustomAddr(c.adapterName, c.connID, lAddr) // make quic-go's connMultiplexer happy
}
+func (c *packetConn) Upstream() any {
+ return c.EnhancePacketConn
+}
+
+func (c *packetConn) WriterReplaceable() bool {
+ return true
+}
+
+func (c *packetConn) ReaderReplaceable() bool {
+ return true
+}
+
func newPacketConn(pc net.PacketConn, a C.ProxyAdapter) C.PacketConn {
+ epc := N.NewEnhancePacketConn(pc)
if _, ok := pc.(syscall.Conn); !ok { // exclusion system conn like *net.UDPConn
- pc = N.NewDeadlinePacketConn(pc) // most conn from outbound can't handle readDeadline correctly
+ epc = N.NewDeadlineEnhancePacketConn(epc) // most conn from outbound can't handle readDeadline correctly
}
- return &packetConn{pc, []string{a.Name()}, a.Name(), utils.NewUUIDV4().String(), parseRemoteDestination(a.Addr())}
+ return &packetConn{epc, []string{a.Name()}, a.Name(), utils.NewUUIDV4().String(), parseRemoteDestination(a.Addr())}
}
func parseRemoteDestination(addr string) string {
diff --git a/adapter/outbound/direct.go b/adapter/outbound/direct.go
index eae37d7a..94b59cd0 100644
--- a/adapter/outbound/direct.go
+++ b/adapter/outbound/direct.go
@@ -3,8 +3,6 @@ package outbound
import (
"context"
"errors"
- "net"
-
"github.com/Dreamacro/clash/component/dialer"
"github.com/Dreamacro/clash/component/resolver"
C "github.com/Dreamacro/clash/constant"
@@ -39,11 +37,7 @@ func (d *Direct) ListenPacketContext(ctx context.Context, metadata *C.Metadata,
if err != nil {
return nil, err
}
- return newPacketConn(&directPacketConn{pc}, d), nil
-}
-
-type directPacketConn struct {
- net.PacketConn
+ return newPacketConn(pc, d), nil
}
func NewDirect() *Direct {
diff --git a/adapter/outbound/http.go b/adapter/outbound/http.go
index 2d5fd899..78735b2d 100644
--- a/adapter/outbound/http.go
+++ b/adapter/outbound/http.go
@@ -10,7 +10,6 @@ import (
"io"
"net"
"net/http"
- "net/url"
"strconv"
"github.com/Dreamacro/clash/component/dialer"
@@ -41,12 +40,10 @@ type HttpOption struct {
Headers map[string]string `proxy:"headers,omitempty"`
}
-// StreamConn implements C.ProxyAdapter
-func (h *Http) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
+// StreamConnContext implements C.ProxyAdapter
+func (h *Http) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.Metadata) (net.Conn, error) {
if h.tlsConfig != nil {
cc := tls.Client(c, h.tlsConfig)
- ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout)
- defer cancel()
err := cc.HandshakeContext(ctx)
c = cc
if err != nil {
@@ -83,7 +80,7 @@ func (h *Http) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metad
safeConnClose(c, err)
}(c)
- c, err = h.StreamConn(c, metadata)
+ c, err = h.StreamConnContext(ctx, c, metadata)
if err != nil {
return nil, err
}
@@ -98,34 +95,36 @@ func (h *Http) SupportWithDialer() C.NetWork {
func (h *Http) shakeHand(metadata *C.Metadata, rw io.ReadWriter) error {
addr := metadata.RemoteAddress()
- req := &http.Request{
- Method: http.MethodConnect,
- URL: &url.URL{
- Host: addr,
- },
- Host: addr,
- Header: http.Header{
- "Proxy-Connection": []string{"Keep-Alive"},
- },
+ HeaderString := "CONNECT " + addr + " HTTP/1.1\r\n"
+ tempHeaders := map[string]string{
+ "Host": addr,
+ "User-Agent": "Go-http-client/1.1",
+ "Proxy-Connection": "Keep-Alive",
}
- //增加headers
- if len(h.option.Headers) != 0 {
- for key, value := range h.option.Headers {
- req.Header.Add(key, value)
- }
+ for key, value := range h.option.Headers {
+ tempHeaders[key] = value
}
if h.user != "" && h.pass != "" {
auth := h.user + ":" + h.pass
- req.Header.Add("Proxy-Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(auth)))
+ tempHeaders["Proxy-Authorization"] = "Basic " + base64.StdEncoding.EncodeToString([]byte(auth))
}
- if err := req.Write(rw); err != nil {
+ for key, value := range tempHeaders {
+ HeaderString += key + ": " + value + "\r\n"
+ }
+
+ HeaderString += "\r\n"
+
+ _, err := rw.Write([]byte(HeaderString))
+
+ if err != nil {
return err
}
- resp, err := http.ReadResponse(bufio.NewReader(rw), req)
+ resp, err := http.ReadResponse(bufio.NewReader(rw), nil)
+
if err != nil {
return err
}
diff --git a/adapter/outbound/hysteria.go b/adapter/outbound/hysteria.go
index 6024ea10..7da4975d 100644
--- a/adapter/outbound/hysteria.go
+++ b/adapter/outbound/hysteria.go
@@ -318,6 +318,16 @@ func (c *hyPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
return
}
+func (c *hyPacketConn) WaitReadFrom() (data []byte, put func(), addr net.Addr, err error) {
+ b, addrStr, err := c.UDPConn.ReadFrom()
+ if err != nil {
+ return
+ }
+ data = b
+ addr = M.ParseSocksaddr(addrStr).UDPAddr()
+ return
+}
+
func (c *hyPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
err = c.UDPConn.WriteTo(p, M.SocksaddrFromNet(addr).String())
if err != nil {
diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go
index e2ed24c2..32558eac 100644
--- a/adapter/outbound/shadowsocks.go
+++ b/adapter/outbound/shadowsocks.go
@@ -6,7 +6,6 @@ import (
"fmt"
"net"
"strconv"
- "time"
N "github.com/Dreamacro/clash/common/net"
"github.com/Dreamacro/clash/common/structure"
@@ -17,13 +16,10 @@ import (
"github.com/Dreamacro/clash/transport/restls"
obfs "github.com/Dreamacro/clash/transport/simple-obfs"
shadowtls "github.com/Dreamacro/clash/transport/sing-shadowtls"
- "github.com/Dreamacro/clash/transport/socks5"
v2rayObfs "github.com/Dreamacro/clash/transport/v2ray-plugin"
restlsC "github.com/3andne/restls-client-go"
- shadowsocks "github.com/metacubex/sing-shadowsocks"
- "github.com/metacubex/sing-shadowsocks/shadowimpl"
- "github.com/sagernet/sing/common/bufio"
+ "github.com/metacubex/sing-shadowsocks2"
M "github.com/sagernet/sing/common/metadata"
"github.com/sagernet/sing/common/uot"
)
@@ -87,14 +83,7 @@ type restlsOption struct {
RestlsScript string `obfs:"restls-script,omitempty"`
}
-// StreamConn implements C.ProxyAdapter
-func (ss *ShadowSocks) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
- // fix tls handshake not timeout
- ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout)
- defer cancel()
- return ss.StreamConnContext(ctx, c, metadata)
-}
-
+// StreamConnContext implements C.ProxyAdapter
func (ss *ShadowSocks) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.Metadata) (net.Conn, error) {
useEarly := false
switch ss.obfsMode {
@@ -105,7 +94,7 @@ func (ss *ShadowSocks) StreamConnContext(ctx context.Context, c net.Conn, metada
c = obfs.NewHTTPObfs(c, ss.obfsOption.Host, port)
case "websocket":
var err error
- c, err = v2rayObfs.NewV2rayObfs(c, ss.v2rayOption)
+ c, err = v2rayObfs.NewV2rayObfs(ctx, c, ss.v2rayOption)
if err != nil {
return nil, fmt.Errorf("%s connect error: %w", ss.addr, err)
}
@@ -196,7 +185,7 @@ func (ss *ShadowSocks) ListenPacketWithDialer(ctx context.Context, dialer C.Dial
if err != nil {
return nil, err
}
- pc = ss.method.DialPacketConn(bufio.NewBindPacketConn(pc, addr))
+ pc = ss.method.DialPacketConn(N.NewBindPacketConn(pc, addr))
return newPacketConn(pc, ss), nil
}
@@ -234,7 +223,9 @@ func (ss *ShadowSocks) SupportUOT() bool {
func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port))
- method, err := shadowimpl.FetchMethod(option.Cipher, option.Password, time.Now)
+ method, err := shadowsocks.CreateMethod(context.Background(), option.Cipher, shadowsocks.MethodOptions{
+ Password: option.Password,
+ })
if err != nil {
return nil, fmt.Errorf("ss %s initialize error: %w", addr, err)
}
@@ -312,7 +303,7 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
switch option.UDPOverTCPVersion {
case uot.Version, uot.LegacyVersion:
case 0:
- option.UDPOverTCPVersion = uot.Version
+ option.UDPOverTCPVersion = uot.LegacyVersion
default:
return nil, fmt.Errorf("ss %s unknown udp over tcp protocol version: %d", addr, option.UDPOverTCPVersion)
}
@@ -338,36 +329,3 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
restlsConfig: restlsConfig,
}, nil
}
-
-type ssPacketConn struct {
- net.PacketConn
- rAddr net.Addr
-}
-
-func (spc *ssPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
- packet, err := socks5.EncodeUDPPacket(socks5.ParseAddrToSocksAddr(addr), b)
- if err != nil {
- return
- }
- return spc.PacketConn.WriteTo(packet[3:], spc.rAddr)
-}
-
-func (spc *ssPacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
- n, _, e := spc.PacketConn.ReadFrom(b)
- if e != nil {
- return 0, nil, e
- }
-
- addr := socks5.SplitAddr(b[:n])
- if addr == nil {
- return 0, nil, errors.New("parse addr error")
- }
-
- udpAddr := addr.UDPAddr()
- if udpAddr == nil {
- return 0, nil, errors.New("parse addr error")
- }
-
- copy(b, b[len(addr):])
- return n - len(addr), udpAddr, e
-}
diff --git a/adapter/outbound/shadowsocksr.go b/adapter/outbound/shadowsocksr.go
index 2b94ab0c..07778032 100644
--- a/adapter/outbound/shadowsocksr.go
+++ b/adapter/outbound/shadowsocksr.go
@@ -2,16 +2,19 @@ package outbound
import (
"context"
+ "errors"
"fmt"
"net"
"strconv"
+ N "github.com/Dreamacro/clash/common/net"
"github.com/Dreamacro/clash/component/dialer"
"github.com/Dreamacro/clash/component/proxydialer"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/transport/shadowsocks/core"
"github.com/Dreamacro/clash/transport/shadowsocks/shadowaead"
"github.com/Dreamacro/clash/transport/shadowsocks/shadowstream"
+ "github.com/Dreamacro/clash/transport/socks5"
"github.com/Dreamacro/clash/transport/ssr/obfs"
"github.com/Dreamacro/clash/transport/ssr/protocol"
)
@@ -38,8 +41,8 @@ type ShadowSocksROption struct {
UDP bool `proxy:"udp,omitempty"`
}
-// StreamConn implements C.ProxyAdapter
-func (ssr *ShadowSocksR) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
+// StreamConnContext implements C.ProxyAdapter
+func (ssr *ShadowSocksR) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.Metadata) (net.Conn, error) {
c = ssr.obfs.StreamConn(c)
c = ssr.cipher.StreamConn(c)
var (
@@ -83,7 +86,7 @@ func (ssr *ShadowSocksR) DialContextWithDialer(ctx context.Context, dialer C.Dia
safeConnClose(c, err)
}(c)
- c, err = ssr.StreamConn(c, metadata)
+ c, err = ssr.StreamConnContext(ctx, c, metadata)
return NewConn(c, ssr), err
}
@@ -110,9 +113,9 @@ func (ssr *ShadowSocksR) ListenPacketWithDialer(ctx context.Context, dialer C.Di
return nil, err
}
- pc = ssr.cipher.PacketConn(pc)
- pc = ssr.protocol.PacketConn(pc)
- return newPacketConn(&ssPacketConn{PacketConn: pc, rAddr: addr}, ssr), nil
+ epc := ssr.cipher.PacketConn(N.NewEnhancePacketConn(pc))
+ epc = ssr.protocol.PacketConn(epc)
+ return newPacketConn(&ssrPacketConn{EnhancePacketConn: epc, rAddr: addr}, ssr), nil
}
// SupportWithDialer implements C.ProxyAdapter
@@ -188,3 +191,62 @@ func NewShadowSocksR(option ShadowSocksROption) (*ShadowSocksR, error) {
protocol: protocol,
}, nil
}
+
+type ssrPacketConn struct {
+ N.EnhancePacketConn
+ rAddr net.Addr
+}
+
+func (spc *ssrPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
+ packet, err := socks5.EncodeUDPPacket(socks5.ParseAddrToSocksAddr(addr), b)
+ if err != nil {
+ return
+ }
+ return spc.EnhancePacketConn.WriteTo(packet[3:], spc.rAddr)
+}
+
+func (spc *ssrPacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
+ n, _, e := spc.EnhancePacketConn.ReadFrom(b)
+ if e != nil {
+ return 0, nil, e
+ }
+
+ addr := socks5.SplitAddr(b[:n])
+ if addr == nil {
+ return 0, nil, errors.New("parse addr error")
+ }
+
+ udpAddr := addr.UDPAddr()
+ if udpAddr == nil {
+ return 0, nil, errors.New("parse addr error")
+ }
+
+ copy(b, b[len(addr):])
+ return n - len(addr), udpAddr, e
+}
+
+func (spc *ssrPacketConn) WaitReadFrom() (data []byte, put func(), addr net.Addr, err error) {
+ data, put, _, err = spc.EnhancePacketConn.WaitReadFrom()
+ if err != nil {
+ return nil, nil, nil, err
+ }
+
+ _addr := socks5.SplitAddr(data)
+ if _addr == nil {
+ if put != nil {
+ put()
+ }
+ return nil, nil, nil, errors.New("parse addr error")
+ }
+
+ addr = _addr.UDPAddr()
+ if addr == nil {
+ if put != nil {
+ put()
+ }
+ return nil, nil, nil, errors.New("parse addr error")
+ }
+
+ data = data[len(_addr):]
+ return
+}
diff --git a/adapter/outbound/singmux.go b/adapter/outbound/singmux.go
index acfdfe99..555a0ecb 100644
--- a/adapter/outbound/singmux.go
+++ b/adapter/outbound/singmux.go
@@ -92,7 +92,7 @@ func (s *SingMux) ListenPacketContext(ctx context.Context, metadata *C.Metadata,
if pc == nil {
return nil, E.New("packetConn is nil")
}
- return newPacketConn(CN.NewRefPacketConn(pc, s), s.ProxyAdapter), nil
+ return newPacketConn(CN.NewRefPacketConn(CN.NewThreadSafePacketConn(pc), s), s.ProxyAdapter), nil
}
func (s *SingMux) SupportUDP() bool {
diff --git a/adapter/outbound/snell.go b/adapter/outbound/snell.go
index 1ec0a430..fc1f4eb3 100644
--- a/adapter/outbound/snell.go
+++ b/adapter/outbound/snell.go
@@ -52,8 +52,8 @@ func streamConn(c net.Conn, option streamOption) *snell.Snell {
return snell.StreamConn(c, option.psk, option.version)
}
-// StreamConn implements C.ProxyAdapter
-func (s *Snell) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
+// StreamConnContext implements C.ProxyAdapter
+func (s *Snell) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.Metadata) (net.Conn, error) {
c = streamConn(c, streamOption{s.psk, s.version, s.addr, s.obfsOption})
if metadata.NetWork == C.UDP {
err := snell.WriteUDPHeader(c, s.version)
@@ -101,7 +101,7 @@ func (s *Snell) DialContextWithDialer(ctx context.Context, dialer C.Dialer, meta
safeConnClose(c, err)
}(c)
- c, err = s.StreamConn(c, metadata)
+ c, err = s.StreamConnContext(ctx, c, metadata)
return NewConn(c, s), err
}
diff --git a/adapter/outbound/socks5.go b/adapter/outbound/socks5.go
index 26f5733b..9af4d0fc 100644
--- a/adapter/outbound/socks5.go
+++ b/adapter/outbound/socks5.go
@@ -39,12 +39,10 @@ type Socks5Option struct {
Fingerprint string `proxy:"fingerprint,omitempty"`
}
-// StreamConn implements C.ProxyAdapter
-func (ss *Socks5) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
+// StreamConnContext implements C.ProxyAdapter
+func (ss *Socks5) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.Metadata) (net.Conn, error) {
if ss.tls {
cc := tls.Client(c, ss.tlsConfig)
- ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout)
- defer cancel()
err := cc.HandshakeContext(ctx)
c = cc
if err != nil {
@@ -88,7 +86,7 @@ func (ss *Socks5) DialContextWithDialer(ctx context.Context, dialer C.Dialer, me
safeConnClose(c, err)
}(c)
- c, err = ss.StreamConn(c, metadata)
+ c, err = ss.StreamConnContext(ctx, c, metadata)
if err != nil {
return nil, err
}
diff --git a/adapter/outbound/trojan.go b/adapter/outbound/trojan.go
index b9bbc33f..81fb1ceb 100644
--- a/adapter/outbound/trojan.go
+++ b/adapter/outbound/trojan.go
@@ -50,7 +50,7 @@ type TrojanOption struct {
ClientFingerprint string `proxy:"client-fingerprint,omitempty"`
}
-func (t *Trojan) plainStream(c net.Conn) (net.Conn, error) {
+func (t *Trojan) plainStream(ctx context.Context, c net.Conn) (net.Conn, error) {
if t.option.Network == "ws" {
host, port, _ := net.SplitHostPort(t.addr)
wsOpts := &trojan.WebsocketOption{
@@ -71,14 +71,14 @@ func (t *Trojan) plainStream(c net.Conn) (net.Conn, error) {
wsOpts.Headers = header
}
- return t.instance.StreamWebsocketConn(c, wsOpts)
+ return t.instance.StreamWebsocketConn(ctx, c, wsOpts)
}
- return t.instance.StreamConn(c)
+ return t.instance.StreamConn(ctx, c)
}
-// StreamConn implements C.ProxyAdapter
-func (t *Trojan) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
+// StreamConnContext implements C.ProxyAdapter
+func (t *Trojan) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.Metadata) (net.Conn, error) {
var err error
if tlsC.HaveGlobalFingerprint() && len(t.option.ClientFingerprint) == 0 {
@@ -88,7 +88,7 @@ func (t *Trojan) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error)
if t.transport != nil {
c, err = gun.StreamGunWithConn(c, t.gunTLSConfig, t.gunConfig, t.realityConfig)
} else {
- c, err = t.plainStream(c)
+ c, err = t.plainStream(ctx, c)
}
if err != nil {
@@ -151,7 +151,7 @@ func (t *Trojan) DialContextWithDialer(ctx context.Context, dialer C.Dialer, met
safeConnClose(c, err)
}(c)
- c, err = t.StreamConn(c, metadata)
+ c, err = t.StreamConnContext(ctx, c, metadata)
if err != nil {
return nil, err
}
@@ -199,7 +199,7 @@ func (t *Trojan) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, me
safeConnClose(c, err)
}(c)
tcpKeepAlive(c)
- c, err = t.plainStream(c)
+ c, err = t.plainStream(ctx, c)
if err != nil {
return nil, fmt.Errorf("%s connect error: %w", t.addr, err)
}
diff --git a/adapter/outbound/vless.go b/adapter/outbound/vless.go
index d7db27d5..e3aff5fb 100644
--- a/adapter/outbound/vless.go
+++ b/adapter/outbound/vless.go
@@ -13,6 +13,7 @@ import (
"sync"
"github.com/Dreamacro/clash/common/convert"
+ N "github.com/Dreamacro/clash/common/net"
"github.com/Dreamacro/clash/component/dialer"
"github.com/Dreamacro/clash/component/proxydialer"
"github.com/Dreamacro/clash/component/resolver"
@@ -74,7 +75,7 @@ type VlessOption struct {
ClientFingerprint string `proxy:"client-fingerprint,omitempty"`
}
-func (v *Vless) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
+func (v *Vless) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.Metadata) (net.Conn, error) {
var err error
if tlsC.HaveGlobalFingerprint() && len(v.option.ClientFingerprint) == 0 {
@@ -128,10 +129,10 @@ func (v *Vless) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
convert.SetUserAgent(wsOpts.Headers)
}
}
- c, err = vmess.StreamWebsocketConn(c, wsOpts)
+ c, err = vmess.StreamWebsocketConn(ctx, c, wsOpts)
case "http":
// readability first, so just copy default TLS logic
- c, err = v.streamTLSOrXTLSConn(c, false)
+ c, err = v.streamTLSOrXTLSConn(ctx, c, false)
if err != nil {
return nil, err
}
@@ -146,7 +147,7 @@ func (v *Vless) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
c = vmess.StreamHTTPConn(c, httpOpts)
case "h2":
- c, err = v.streamTLSOrXTLSConn(c, true)
+ c, err = v.streamTLSOrXTLSConn(ctx, c, true)
if err != nil {
return nil, err
}
@@ -162,7 +163,7 @@ func (v *Vless) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
default:
// default tcp network
// handle TLS And XTLS
- c, err = v.streamTLSOrXTLSConn(c, false)
+ c, err = v.streamTLSOrXTLSConn(ctx, c, false)
}
if err != nil {
@@ -200,7 +201,7 @@ func (v *Vless) streamConn(c net.Conn, metadata *C.Metadata) (conn net.Conn, err
return
}
-func (v *Vless) streamTLSOrXTLSConn(conn net.Conn, isH2 bool) (net.Conn, error) {
+func (v *Vless) streamTLSOrXTLSConn(ctx context.Context, conn net.Conn, isH2 bool) (net.Conn, error) {
host, _, _ := net.SplitHostPort(v.addr)
if v.isLegacyXTLSEnabled() && !isH2 {
@@ -214,7 +215,7 @@ func (v *Vless) streamTLSOrXTLSConn(conn net.Conn, isH2 bool) (net.Conn, error)
xtlsOpts.Host = v.option.ServerName
}
- return vless.StreamXTLSConn(conn, &xtlsOpts)
+ return vless.StreamXTLSConn(ctx, conn, &xtlsOpts)
} else if v.option.TLS {
tlsOpts := vmess.TLSConfig{
@@ -233,7 +234,7 @@ func (v *Vless) streamTLSOrXTLSConn(conn net.Conn, isH2 bool) (net.Conn, error)
tlsOpts.Host = v.option.ServerName
}
- return vmess.StreamTLSConn(conn, &tlsOpts)
+ return vmess.StreamTLSConn(ctx, conn, &tlsOpts)
}
return conn, nil
@@ -282,7 +283,7 @@ func (v *Vless) DialContextWithDialer(ctx context.Context, dialer C.Dialer, meta
safeConnClose(c, err)
}(c)
- c, err = v.StreamConn(c, metadata)
+ c, err = v.StreamConnContext(ctx, c, metadata)
if err != nil {
return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error())
}
@@ -347,7 +348,7 @@ func (v *Vless) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, met
safeConnClose(c, err)
}(c)
- c, err = v.StreamConn(c, metadata)
+ c, err = v.StreamConnContext(ctx, c, metadata)
if err != nil {
return nil, fmt.Errorf("new vless client error: %v", err)
}
@@ -372,15 +373,15 @@ func (v *Vless) ListenPacketOnStreamConn(ctx context.Context, c net.Conn, metada
}
if v.option.XUDP {
- return newPacketConn(&threadSafePacketConn{
- PacketConn: vmessSing.NewXUDPConn(c, M.SocksaddrFromNet(metadata.UDPAddr())),
- }, v), nil
+ return newPacketConn(N.NewThreadSafePacketConn(
+ vmessSing.NewXUDPConn(c, M.SocksaddrFromNet(metadata.UDPAddr())),
+ ), v), nil
} else if v.option.PacketAddr {
- return newPacketConn(&threadSafePacketConn{
- PacketConn: packetaddr.NewConn(&vlessPacketConn{
+ return newPacketConn(N.NewThreadSafePacketConn(
+ packetaddr.NewConn(&vlessPacketConn{
Conn: c, rAddr: metadata.UDPAddr(),
}, M.SocksaddrFromNet(metadata.UDPAddr())),
- }, v), nil
+ ), v), nil
}
return newPacketConn(&vlessPacketConn{Conn: c, rAddr: metadata.UDPAddr()}, v), nil
}
@@ -595,15 +596,19 @@ func NewVless(option VlessOption) (*Vless, error) {
Host: v.option.ServerName,
ClientFingerprint: v.option.ClientFingerprint,
}
- tlsConfig := tlsC.GetGlobalTLSConfig(&tls.Config{
- InsecureSkipVerify: v.option.SkipCertVerify,
- ServerName: v.option.ServerName,
- })
-
- if v.option.ServerName == "" {
- host, _, _ := net.SplitHostPort(v.addr)
- tlsConfig.ServerName = host
- gunConfig.Host = host
+ if option.ServerName == "" {
+ gunConfig.Host = v.addr
+ }
+ var tlsConfig *tls.Config
+ if option.TLS {
+ tlsConfig = tlsC.GetGlobalTLSConfig(&tls.Config{
+ InsecureSkipVerify: v.option.SkipCertVerify,
+ ServerName: v.option.ServerName,
+ })
+ if option.ServerName == "" {
+ host, _, _ := net.SplitHostPort(v.addr)
+ tlsConfig.ServerName = host
+ }
}
v.gunTLSConfig = tlsConfig
diff --git a/adapter/outbound/vmess.go b/adapter/outbound/vmess.go
index 8901f3d5..058ce49d 100644
--- a/adapter/outbound/vmess.go
+++ b/adapter/outbound/vmess.go
@@ -89,8 +89,8 @@ type WSOptions struct {
EarlyDataHeaderName string `proxy:"early-data-header-name,omitempty"`
}
-// StreamConn implements C.ProxyAdapter
-func (v *Vmess) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
+// StreamConnContext implements C.ProxyAdapter
+func (v *Vmess) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.Metadata) (net.Conn, error) {
var err error
if tlsC.HaveGlobalFingerprint() && (len(v.option.ClientFingerprint) == 0) {
@@ -138,7 +138,7 @@ func (v *Vmess) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
wsOpts.TLSConfig.ServerName = host
}
}
- c, err = clashVMess.StreamWebsocketConn(c, wsOpts)
+ c, err = clashVMess.StreamWebsocketConn(ctx, c, wsOpts)
case "http":
// readability first, so just copy default TLS logic
if v.option.TLS {
@@ -153,7 +153,7 @@ func (v *Vmess) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
if v.option.ServerName != "" {
tlsOpts.Host = v.option.ServerName
}
- c, err = clashVMess.StreamTLSConn(c, tlsOpts)
+ c, err = clashVMess.StreamTLSConn(ctx, c, tlsOpts)
if err != nil {
return nil, err
}
@@ -182,7 +182,7 @@ func (v *Vmess) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
tlsOpts.Host = v.option.ServerName
}
- c, err = clashVMess.StreamTLSConn(c, &tlsOpts)
+ c, err = clashVMess.StreamTLSConn(ctx, c, &tlsOpts)
if err != nil {
return nil, err
}
@@ -210,7 +210,7 @@ func (v *Vmess) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
tlsOpts.Host = v.option.ServerName
}
- c, err = clashVMess.StreamTLSConn(c, tlsOpts)
+ c, err = clashVMess.StreamTLSConn(ctx, c, tlsOpts)
}
}
@@ -294,7 +294,7 @@ func (v *Vmess) DialContextWithDialer(ctx context.Context, dialer C.Dialer, meta
safeConnClose(c, err)
}(c)
- c, err = v.StreamConn(c, metadata)
+ c, err = v.StreamConnContext(ctx, c, metadata)
return NewConn(c, v), err
}
@@ -355,7 +355,7 @@ func (v *Vmess) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, met
safeConnClose(c, err)
}(c)
- c, err = v.StreamConn(c, metadata)
+ c, err = v.StreamConnContext(ctx, c, metadata)
if err != nil {
return nil, fmt.Errorf("new vmess client error: %v", err)
}
@@ -379,7 +379,7 @@ func (v *Vmess) ListenPacketOnStreamConn(ctx context.Context, c net.Conn, metada
}
if pc, ok := c.(net.PacketConn); ok {
- return newPacketConn(&threadSafePacketConn{PacketConn: pc}, v), nil
+ return newPacketConn(N.NewThreadSafePacketConn(pc), v), nil
}
return newPacketConn(&vmessPacketConn{Conn: c, rAddr: metadata.UDPAddr()}, v), nil
}
@@ -413,13 +413,6 @@ func NewVmess(option VmessOption) (*Vmess, error) {
option.PacketAddr = false
}
- switch option.Network {
- case "h2", "grpc":
- if !option.TLS {
- option.TLS = true
- }
- }
-
v := &Vmess{
Base: &Base{
name: option.Name,
@@ -464,15 +457,19 @@ func NewVmess(option VmessOption) (*Vmess, error) {
Host: v.option.ServerName,
ClientFingerprint: v.option.ClientFingerprint,
}
- tlsConfig := &tls.Config{
- InsecureSkipVerify: v.option.SkipCertVerify,
- ServerName: v.option.ServerName,
+ if option.ServerName == "" {
+ gunConfig.Host = v.addr
}
-
- if v.option.ServerName == "" {
- host, _, _ := net.SplitHostPort(v.addr)
- tlsConfig.ServerName = host
- gunConfig.Host = host
+ var tlsConfig *tls.Config
+ if option.TLS {
+ tlsConfig = tlsC.GetGlobalTLSConfig(&tls.Config{
+ InsecureSkipVerify: v.option.SkipCertVerify,
+ ServerName: v.option.ServerName,
+ })
+ if option.ServerName == "" {
+ host, _, _ := net.SplitHostPort(v.addr)
+ tlsConfig.ServerName = host
+ }
}
v.gunTLSConfig = tlsConfig
@@ -489,17 +486,6 @@ func NewVmess(option VmessOption) (*Vmess, error) {
return v, nil
}
-type threadSafePacketConn struct {
- net.PacketConn
- access sync.Mutex
-}
-
-func (c *threadSafePacketConn) WriteTo(b []byte, addr net.Addr) (int, error) {
- c.access.Lock()
- defer c.access.Unlock()
- return c.PacketConn.WriteTo(b, addr)
-}
-
type vmessPacketConn struct {
net.Conn
rAddr net.Addr
diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go
index 67cd9092..c12321f3 100644
--- a/adapter/outbound/wireguard.go
+++ b/adapter/outbound/wireguard.go
@@ -67,7 +67,7 @@ type WireGuardPeerOption struct {
PublicKey string `proxy:"public-key,omitempty"`
PreSharedKey string `proxy:"pre-shared-key,omitempty"`
Reserved []uint8 `proxy:"reserved,omitempty"`
- AllowedIPs []string `proxy:"allowed_ips,omitempty"`
+ AllowedIPs []string `proxy:"allowed-ips,omitempty"`
}
type wgSingDialer struct {
@@ -499,9 +499,9 @@ func (r *refProxyAdapter) MarshalJSON() ([]byte, error) {
return nil, C.ErrNotSupport
}
-func (r *refProxyAdapter) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
+func (r *refProxyAdapter) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.Metadata) (net.Conn, error) {
if r.proxyAdapter != nil {
- return r.proxyAdapter.StreamConn(c, metadata)
+ return r.proxyAdapter.StreamConnContext(ctx, c, metadata)
}
return nil, C.ErrNotSupport
}
diff --git a/adapter/outboundgroup/groupbase.go b/adapter/outboundgroup/groupbase.go
index 8e253e63..895ca421 100644
--- a/adapter/outboundgroup/groupbase.go
+++ b/adapter/outboundgroup/groupbase.go
@@ -130,10 +130,6 @@ func (gb *GroupBase) GetProxies(touch bool) []C.Proxy {
}
}
- if len(proxies) == 0 {
- return append(proxies, tunnel.Proxies()["COMPATIBLE"])
- }
-
if len(gb.providers) > 1 && len(gb.filterRegs) > 1 {
var newProxies []C.Proxy
proxiesSet := map[string]struct{}{}
@@ -189,6 +185,10 @@ func (gb *GroupBase) GetProxies(touch bool) []C.Proxy {
proxies = newProxies
}
+ if len(proxies) == 0 {
+ return append(proxies, tunnel.Proxies()["COMPATIBLE"])
+ }
+
return proxies
}
diff --git a/common/net/bind.go b/common/net/bind.go
index 1e20a8c0..231c24c2 100644
--- a/common/net/bind.go
+++ b/common/net/bind.go
@@ -3,34 +3,43 @@ package net
import "net"
type bindPacketConn struct {
- net.PacketConn
+ EnhancePacketConn
rAddr net.Addr
}
-func (wpc *bindPacketConn) Read(b []byte) (n int, err error) {
- n, _, err = wpc.PacketConn.ReadFrom(b)
+func (c *bindPacketConn) Read(b []byte) (n int, err error) {
+ n, _, err = c.EnhancePacketConn.ReadFrom(b)
return n, err
}
-func (wpc *bindPacketConn) Write(b []byte) (n int, err error) {
- return wpc.PacketConn.WriteTo(b, wpc.rAddr)
+func (c *bindPacketConn) WaitRead() (data []byte, put func(), err error) {
+ data, put, _, err = c.EnhancePacketConn.WaitReadFrom()
+ return
}
-func (wpc *bindPacketConn) RemoteAddr() net.Addr {
- return wpc.rAddr
+func (c *bindPacketConn) Write(b []byte) (n int, err error) {
+ return c.EnhancePacketConn.WriteTo(b, c.rAddr)
}
-func (wpc *bindPacketConn) LocalAddr() net.Addr {
- if wpc.PacketConn.LocalAddr() == nil {
+func (c *bindPacketConn) RemoteAddr() net.Addr {
+ return c.rAddr
+}
+
+func (c *bindPacketConn) LocalAddr() net.Addr {
+ if c.EnhancePacketConn.LocalAddr() == nil {
return &net.UDPAddr{IP: net.IPv4zero, Port: 0}
} else {
- return wpc.PacketConn.LocalAddr()
+ return c.EnhancePacketConn.LocalAddr()
}
}
+func (c *bindPacketConn) Upstream() any {
+ return c.EnhancePacketConn
+}
+
func NewBindPacketConn(pc net.PacketConn, rAddr net.Addr) net.Conn {
return &bindPacketConn{
- PacketConn: pc,
- rAddr: rAddr,
+ EnhancePacketConn: NewEnhancePacketConn(pc),
+ rAddr: rAddr,
}
}
diff --git a/common/net/bufconn.go b/common/net/bufconn.go
index 2ff73c82..6da2d9d1 100644
--- a/common/net/bufconn.go
+++ b/common/net/bufconn.go
@@ -62,7 +62,7 @@ func (c *BufferedConn) Buffered() int {
}
func (c *BufferedConn) ReadBuffer(buffer *buf.Buffer) (err error) {
- if c.r.Buffered() > 0 {
+ if c.r != nil && c.r.Buffered() > 0 {
_, err = buffer.ReadOnceFrom(c.r)
return
}
@@ -70,10 +70,11 @@ func (c *BufferedConn) ReadBuffer(buffer *buf.Buffer) (err error) {
}
func (c *BufferedConn) ReadCached() *buf.Buffer { // call in sing/common/bufio.Copy
- if c.r.Buffered() > 0 {
+ if c.r != nil && c.r.Buffered() > 0 {
length := c.r.Buffered()
b, _ := c.r.Peek(length)
_, _ = c.r.Discard(length)
+ c.r = nil // drop bufio.Reader to let gc can clean up its internal buf
return buf.As(b)
}
return nil
@@ -84,7 +85,7 @@ func (c *BufferedConn) Upstream() any {
}
func (c *BufferedConn) ReaderReplaceable() bool {
- if c.r.Buffered() > 0 {
+ if c.r != nil && c.r.Buffered() > 0 {
return false
}
return true
diff --git a/common/net/deadline/packet.go b/common/net/deadline/packet.go
new file mode 100644
index 00000000..bcf2db9d
--- /dev/null
+++ b/common/net/deadline/packet.go
@@ -0,0 +1,154 @@
+package deadline
+
+import (
+ "net"
+ "os"
+ "runtime"
+ "time"
+
+ "github.com/Dreamacro/clash/common/atomic"
+ "github.com/Dreamacro/clash/common/net/packet"
+)
+
+type readResult struct {
+ data []byte
+ addr net.Addr
+ err error
+}
+
+type NetPacketConn struct {
+ net.PacketConn
+ deadline atomic.TypedValue[time.Time]
+ pipeDeadline pipeDeadline
+ disablePipe atomic.Bool
+ inRead atomic.Bool
+ resultCh chan any
+}
+
+func NewNetPacketConn(pc net.PacketConn) net.PacketConn {
+ npc := &NetPacketConn{
+ PacketConn: pc,
+ pipeDeadline: makePipeDeadline(),
+ resultCh: make(chan any, 1),
+ }
+ npc.resultCh <- nil
+ if enhancePC, isEnhance := pc.(packet.EnhancePacketConn); isEnhance {
+ epc := &EnhancePacketConn{
+ NetPacketConn: npc,
+ enhancePacketConn: enhancePacketConn{
+ netPacketConn: npc,
+ enhancePacketConn: enhancePC,
+ },
+ }
+ if singPC, isSingPC := pc.(packet.SingPacketConn); isSingPC {
+ return &EnhanceSingPacketConn{
+ EnhancePacketConn: epc,
+ singPacketConn: singPacketConn{
+ netPacketConn: npc,
+ singPacketConn: singPC,
+ },
+ }
+ }
+ return epc
+ }
+ if singPC, isSingPC := pc.(packet.SingPacketConn); isSingPC {
+ return &SingPacketConn{
+ NetPacketConn: npc,
+ singPacketConn: singPacketConn{
+ netPacketConn: npc,
+ singPacketConn: singPC,
+ },
+ }
+ }
+ return npc
+}
+
+func (c *NetPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
+FOR:
+ for {
+ select {
+ case result := <-c.resultCh:
+ if result != nil {
+ if result, ok := result.(*readResult); ok {
+ n = copy(p, result.data)
+ addr = result.addr
+ err = result.err
+ c.resultCh <- nil // finish cache read
+ return
+ }
+ c.resultCh <- result // another type of read
+ runtime.Gosched() // allowing other goroutines to run
+ continue FOR
+ } else {
+ c.resultCh <- nil
+ break FOR
+ }
+ case <-c.pipeDeadline.wait():
+ return 0, nil, os.ErrDeadlineExceeded
+ }
+ }
+
+ if c.disablePipe.Load() {
+ return c.PacketConn.ReadFrom(p)
+ } else if c.deadline.Load().IsZero() {
+ c.inRead.Store(true)
+ defer c.inRead.Store(false)
+ n, addr, err = c.PacketConn.ReadFrom(p)
+ return
+ }
+
+ <-c.resultCh
+ go c.pipeReadFrom(len(p))
+
+ return c.ReadFrom(p)
+}
+
+func (c *NetPacketConn) pipeReadFrom(size int) {
+ buffer := make([]byte, size)
+ n, addr, err := c.PacketConn.ReadFrom(buffer)
+ buffer = buffer[:n]
+ result := &readResult{}
+ result.data = buffer
+ result.addr = addr
+ result.err = err
+ c.resultCh <- result
+}
+
+func (c *NetPacketConn) SetReadDeadline(t time.Time) error {
+ if c.disablePipe.Load() {
+ return c.PacketConn.SetReadDeadline(t)
+ } else if c.inRead.Load() {
+ c.disablePipe.Store(true)
+ return c.PacketConn.SetReadDeadline(t)
+ }
+ c.deadline.Store(t)
+ c.pipeDeadline.set(t)
+ return nil
+}
+
+func (c *NetPacketConn) ReaderReplaceable() bool {
+ select {
+ case result := <-c.resultCh:
+ c.resultCh <- result
+ if result != nil {
+ return false // cache reading
+ } else {
+ break
+ }
+ default:
+ return false // pipe reading
+ }
+ return c.disablePipe.Load() || c.deadline.Load().IsZero()
+}
+
+func (c *NetPacketConn) WriterReplaceable() bool {
+ return true
+}
+
+func (c *NetPacketConn) Upstream() any {
+ return c.PacketConn
+}
+
+func (c *NetPacketConn) NeedAdditionalReadDeadline() bool {
+ return false
+}
diff --git a/common/net/deadline/packet_enhance.go b/common/net/deadline/packet_enhance.go
new file mode 100644
index 00000000..5b7d767f
--- /dev/null
+++ b/common/net/deadline/packet_enhance.go
@@ -0,0 +1,83 @@
+package deadline
+
+import (
+ "net"
+ "os"
+ "runtime"
+
+ "github.com/Dreamacro/clash/common/net/packet"
+)
+
+type EnhancePacketConn struct {
+ *NetPacketConn
+ enhancePacketConn
+}
+
+var _ packet.EnhancePacketConn = (*EnhancePacketConn)(nil)
+
+func NewEnhancePacketConn(pc packet.EnhancePacketConn) packet.EnhancePacketConn {
+ return NewNetPacketConn(pc).(packet.EnhancePacketConn)
+}
+
+type enhanceReadResult struct {
+ data []byte
+ put func()
+ addr net.Addr
+ err error
+}
+
+type enhancePacketConn struct {
+ netPacketConn *NetPacketConn
+ enhancePacketConn packet.EnhancePacketConn
+}
+
+func (c *enhancePacketConn) WaitReadFrom() (data []byte, put func(), addr net.Addr, err error) {
+FOR:
+ for {
+ select {
+ case result := <-c.netPacketConn.resultCh:
+ if result != nil {
+ if result, ok := result.(*enhanceReadResult); ok {
+ data = result.data
+ put = result.put
+ addr = result.addr
+ err = result.err
+ c.netPacketConn.resultCh <- nil // finish cache read
+ return
+ }
+ c.netPacketConn.resultCh <- result // another type of read
+ runtime.Gosched() // allowing other goroutines to run
+ continue FOR
+ } else {
+ c.netPacketConn.resultCh <- nil
+ break FOR
+ }
+ case <-c.netPacketConn.pipeDeadline.wait():
+ return nil, nil, nil, os.ErrDeadlineExceeded
+ }
+ }
+
+ if c.netPacketConn.disablePipe.Load() {
+ return c.enhancePacketConn.WaitReadFrom()
+ } else if c.netPacketConn.deadline.Load().IsZero() {
+ c.netPacketConn.inRead.Store(true)
+ defer c.netPacketConn.inRead.Store(false)
+ data, put, addr, err = c.enhancePacketConn.WaitReadFrom()
+ return
+ }
+
+ <-c.netPacketConn.resultCh
+ go c.pipeWaitReadFrom()
+
+ return c.WaitReadFrom()
+}
+
+func (c *enhancePacketConn) pipeWaitReadFrom() {
+ data, put, addr, err := c.enhancePacketConn.WaitReadFrom()
+ result := &enhanceReadResult{}
+ result.data = data
+ result.put = put
+ result.addr = addr
+ result.err = err
+ c.netPacketConn.resultCh <- result
+}
diff --git a/common/net/deadline/packet_sing.go b/common/net/deadline/packet_sing.go
new file mode 100644
index 00000000..f41f3f5b
--- /dev/null
+++ b/common/net/deadline/packet_sing.go
@@ -0,0 +1,177 @@
+package deadline
+
+import (
+ "os"
+ "runtime"
+
+ "github.com/Dreamacro/clash/common/net/packet"
+ "github.com/sagernet/sing/common/buf"
+ "github.com/sagernet/sing/common/bufio"
+ M "github.com/sagernet/sing/common/metadata"
+ N "github.com/sagernet/sing/common/network"
+)
+
+type SingPacketConn struct {
+ *NetPacketConn
+ singPacketConn
+}
+
+var _ packet.SingPacketConn = (*SingPacketConn)(nil)
+
+func NewSingPacketConn(pc packet.SingPacketConn) packet.SingPacketConn {
+ return NewNetPacketConn(pc).(packet.SingPacketConn)
+}
+
+type EnhanceSingPacketConn struct {
+ *EnhancePacketConn
+ singPacketConn
+}
+
+func NewEnhanceSingPacketConn(pc packet.EnhanceSingPacketConn) packet.EnhanceSingPacketConn {
+ return NewNetPacketConn(pc).(packet.EnhanceSingPacketConn)
+}
+
+var _ packet.EnhanceSingPacketConn = (*EnhanceSingPacketConn)(nil)
+
+type singReadResult struct {
+ buffer *buf.Buffer
+ destination M.Socksaddr
+ err error
+}
+
+type singPacketConn struct {
+ netPacketConn *NetPacketConn
+ singPacketConn packet.SingPacketConn
+}
+
+func (c *singPacketConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) {
+FOR:
+ for {
+ select {
+ case result := <-c.netPacketConn.resultCh:
+ if result != nil {
+ if result, ok := result.(*singReadResult); ok {
+ destination = result.destination
+ err = result.err
+ n, _ := buffer.Write(result.buffer.Bytes())
+ result.buffer.Advance(n)
+ if result.buffer.IsEmpty() {
+ result.buffer.Release()
+ }
+ c.netPacketConn.resultCh <- nil // finish cache read
+ return
+ }
+ c.netPacketConn.resultCh <- result // another type of read
+ runtime.Gosched() // allowing other goroutines to run
+ continue FOR
+ } else {
+ c.netPacketConn.resultCh <- nil
+ break FOR
+ }
+ case <-c.netPacketConn.pipeDeadline.wait():
+ return M.Socksaddr{}, os.ErrDeadlineExceeded
+ }
+ }
+
+ if c.netPacketConn.disablePipe.Load() {
+ return c.singPacketConn.ReadPacket(buffer)
+ } else if c.netPacketConn.deadline.Load().IsZero() {
+ c.netPacketConn.inRead.Store(true)
+ defer c.netPacketConn.inRead.Store(false)
+ destination, err = c.singPacketConn.ReadPacket(buffer)
+ return
+ }
+
+ <-c.netPacketConn.resultCh
+ go c.pipeReadPacket(buffer.FreeLen())
+
+ return c.ReadPacket(buffer)
+}
+
+func (c *singPacketConn) pipeReadPacket(pLen int) {
+ buffer := buf.NewSize(pLen)
+ destination, err := c.singPacketConn.ReadPacket(buffer)
+ result := &singReadResult{}
+ result.destination = destination
+ result.err = err
+ c.netPacketConn.resultCh <- result
+}
+
+func (c *singPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
+ return c.singPacketConn.WritePacket(buffer, destination)
+}
+
+func (c *singPacketConn) CreateReadWaiter() (N.PacketReadWaiter, bool) {
+ prw, isReadWaiter := bufio.CreatePacketReadWaiter(c.singPacketConn)
+ if isReadWaiter {
+ return &singPacketReadWaiter{
+ netPacketConn: c.netPacketConn,
+ packetReadWaiter: prw,
+ }, true
+ }
+ return nil, false
+}
+
+var _ N.PacketReadWaiter = (*singPacketReadWaiter)(nil)
+
+type singPacketReadWaiter struct {
+ netPacketConn *NetPacketConn
+ packetReadWaiter N.PacketReadWaiter
+}
+
+type singWaitReadResult singReadResult
+
+func (c *singPacketReadWaiter) InitializeReadWaiter(newBuffer func() *buf.Buffer) {
+ c.packetReadWaiter.InitializeReadWaiter(newBuffer)
+}
+
+func (c *singPacketReadWaiter) WaitReadPacket() (destination M.Socksaddr, err error) {
+FOR:
+ for {
+ select {
+ case result := <-c.netPacketConn.resultCh:
+ if result != nil {
+ if result, ok := result.(*singWaitReadResult); ok {
+ destination = result.destination
+ err = result.err
+ c.netPacketConn.resultCh <- nil // finish cache read
+ return
+ }
+ c.netPacketConn.resultCh <- result // another type of read
+ runtime.Gosched() // allowing other goroutines to run
+ continue FOR
+ } else {
+ c.netPacketConn.resultCh <- nil
+ break FOR
+ }
+ case <-c.netPacketConn.pipeDeadline.wait():
+ return M.Socksaddr{}, os.ErrDeadlineExceeded
+ }
+ }
+
+ if c.netPacketConn.disablePipe.Load() {
+ return c.packetReadWaiter.WaitReadPacket()
+ } else if c.netPacketConn.deadline.Load().IsZero() {
+ c.netPacketConn.inRead.Store(true)
+ defer c.netPacketConn.inRead.Store(false)
+ destination, err = c.packetReadWaiter.WaitReadPacket()
+ return
+ }
+
+ <-c.netPacketConn.resultCh
+ go c.pipeWaitReadPacket()
+
+ return c.WaitReadPacket()
+}
+
+func (c *singPacketReadWaiter) pipeWaitReadPacket() {
+ destination, err := c.packetReadWaiter.WaitReadPacket()
+ result := &singWaitReadResult{}
+ result.destination = destination
+ result.err = err
+ c.netPacketConn.resultCh <- result
+}
+
+func (c *singPacketReadWaiter) Upstream() any {
+ return c.packetReadWaiter
+}
diff --git a/common/net/deadline/pipe.go b/common/net/deadline/pipe.go
new file mode 100644
index 00000000..2cccfb42
--- /dev/null
+++ b/common/net/deadline/pipe.go
@@ -0,0 +1,84 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package deadline
+
+import (
+ "sync"
+ "time"
+)
+
+// pipeDeadline is an abstraction for handling timeouts.
+type pipeDeadline struct {
+ mu sync.Mutex // Guards timer and cancel
+ timer *time.Timer
+ cancel chan struct{} // Must be non-nil
+}
+
+func makePipeDeadline() pipeDeadline {
+ return pipeDeadline{cancel: make(chan struct{})}
+}
+
+// set sets the point in time when the deadline will time out.
+// A timeout event is signaled by closing the channel returned by waiter.
+// Once a timeout has occurred, the deadline can be refreshed by specifying a
+// t value in the future.
+//
+// A zero value for t prevents timeout.
+func (d *pipeDeadline) set(t time.Time) {
+ d.mu.Lock()
+ defer d.mu.Unlock()
+
+ if d.timer != nil && !d.timer.Stop() {
+ <-d.cancel // Wait for the timer callback to finish and close cancel
+ }
+ d.timer = nil
+
+ // Time is zero, then there is no deadline.
+ closed := isClosedChan(d.cancel)
+ if t.IsZero() {
+ if closed {
+ d.cancel = make(chan struct{})
+ }
+ return
+ }
+
+ // Time in the future, setup a timer to cancel in the future.
+ if dur := time.Until(t); dur > 0 {
+ if closed {
+ d.cancel = make(chan struct{})
+ }
+ d.timer = time.AfterFunc(dur, func() {
+ close(d.cancel)
+ })
+ return
+ }
+
+ // Time in the past, so close immediately.
+ if !closed {
+ close(d.cancel)
+ }
+}
+
+// wait returns a channel that is closed when the deadline is exceeded.
+func (d *pipeDeadline) wait() chan struct{} {
+ d.mu.Lock()
+ defer d.mu.Unlock()
+ return d.cancel
+}
+
+func isClosedChan(c <-chan struct{}) bool {
+ select {
+ case <-c:
+ return true
+ default:
+ return false
+ }
+}
+
+func makeFilledChan() chan struct{} {
+ ch := make(chan struct{}, 1)
+ ch <- struct{}{}
+ return ch
+}
diff --git a/common/net/packet.go b/common/net/packet.go
new file mode 100644
index 00000000..fc562c42
--- /dev/null
+++ b/common/net/packet.go
@@ -0,0 +1,18 @@
+package net
+
+import (
+ "github.com/Dreamacro/clash/common/net/deadline"
+ "github.com/Dreamacro/clash/common/net/packet"
+)
+
+type EnhancePacketConn = packet.EnhancePacketConn
+type WaitReadFrom = packet.WaitReadFrom
+
+var NewEnhancePacketConn = packet.NewEnhancePacketConn
+var NewThreadSafePacketConn = packet.NewThreadSafePacketConn
+var NewRefPacketConn = packet.NewRefPacketConn
+
+var NewDeadlineNetPacketConn = deadline.NewNetPacketConn
+var NewDeadlineEnhancePacketConn = deadline.NewEnhancePacketConn
+var NewDeadlineSingPacketConn = deadline.NewSingPacketConn
+var NewDeadlineEnhanceSingPacketConn = deadline.NewEnhanceSingPacketConn
diff --git a/common/net/packet/packet.go b/common/net/packet/packet.go
new file mode 100644
index 00000000..6c9542c1
--- /dev/null
+++ b/common/net/packet/packet.go
@@ -0,0 +1,77 @@
+package packet
+
+import (
+ "net"
+
+ "github.com/Dreamacro/clash/common/pool"
+)
+
+type WaitReadFrom interface {
+ WaitReadFrom() (data []byte, put func(), addr net.Addr, err error)
+}
+
+type EnhancePacketConn interface {
+ net.PacketConn
+ WaitReadFrom
+}
+
+func NewEnhancePacketConn(pc net.PacketConn) EnhancePacketConn {
+ if udpConn, isUDPConn := pc.(*net.UDPConn); isUDPConn {
+ return &enhanceUDPConn{UDPConn: udpConn}
+ }
+ if enhancePC, isEnhancePC := pc.(EnhancePacketConn); isEnhancePC {
+ return enhancePC
+ }
+ if singPC, isSingPC := pc.(SingPacketConn); isSingPC {
+ return newEnhanceSingPacketConn(singPC)
+ }
+ return &enhancePacketConn{PacketConn: pc}
+}
+
+type enhancePacketConn struct {
+ net.PacketConn
+}
+
+func (c *enhancePacketConn) WaitReadFrom() (data []byte, put func(), addr net.Addr, err error) {
+ return waitReadFrom(c.PacketConn)
+}
+
+func (c *enhancePacketConn) Upstream() any {
+ return c.PacketConn
+}
+
+func (c *enhancePacketConn) WriterReplaceable() bool {
+ return true
+}
+
+func (c *enhancePacketConn) ReaderReplaceable() bool {
+ return true
+}
+
+func (c *enhanceUDPConn) Upstream() any {
+ return c.UDPConn
+}
+
+func (c *enhanceUDPConn) WriterReplaceable() bool {
+ return true
+}
+
+func (c *enhanceUDPConn) ReaderReplaceable() bool {
+ return true
+}
+
+func waitReadFrom(pc net.PacketConn) (data []byte, put func(), addr net.Addr, err error) {
+ readBuf := pool.Get(pool.UDPBufferSize)
+ put = func() {
+ _ = pool.Put(readBuf)
+ }
+ var readN int
+ readN, addr, err = pc.ReadFrom(readBuf)
+ if readN > 0 {
+ data = readBuf[:readN]
+ } else {
+ put()
+ put = nil
+ }
+ return
+}
diff --git a/common/net/packet/packet_posix.go b/common/net/packet/packet_posix.go
new file mode 100644
index 00000000..2861482f
--- /dev/null
+++ b/common/net/packet/packet_posix.go
@@ -0,0 +1,65 @@
+//go:build !windows
+
+package packet
+
+import (
+ "net"
+ "strconv"
+ "syscall"
+
+ "github.com/Dreamacro/clash/common/pool"
+)
+
+type enhanceUDPConn struct {
+ *net.UDPConn
+ rawConn syscall.RawConn
+}
+
+func (c *enhanceUDPConn) WaitReadFrom() (data []byte, put func(), addr net.Addr, err error) {
+ if c.rawConn == nil {
+ c.rawConn, _ = c.UDPConn.SyscallConn()
+ }
+ var readErr error
+ err = c.rawConn.Read(func(fd uintptr) (done bool) {
+ readBuf := pool.Get(pool.UDPBufferSize)
+ put = func() {
+ _ = pool.Put(readBuf)
+ }
+ var readFrom syscall.Sockaddr
+ var readN int
+ readN, _, _, readFrom, readErr = syscall.Recvmsg(int(fd), readBuf, nil, 0)
+ if readN > 0 {
+ data = readBuf[:readN]
+ } else {
+ put()
+ put = nil
+ data = nil
+ }
+ if readErr == syscall.EAGAIN {
+ return false
+ }
+ if readFrom != nil {
+ switch from := readFrom.(type) {
+ case *syscall.SockaddrInet4:
+ ip := from.Addr // copy from.Addr; ip escapes, so this line allocates 4 bytes
+ addr = &net.UDPAddr{IP: ip[:], Port: from.Port}
+ case *syscall.SockaddrInet6:
+ ip := from.Addr // copy from.Addr; ip escapes, so this line allocates 16 bytes
+ addr = &net.UDPAddr{IP: ip[:], Port: from.Port, Zone: strconv.FormatInt(int64(from.ZoneId), 10)}
+ }
+ }
+ // udp should not convert readN == 0 to io.EOF
+ //if readN == 0 {
+ // readErr = io.EOF
+ //}
+ return true
+ })
+ if err != nil {
+ return
+ }
+ if readErr != nil {
+ err = readErr
+ return
+ }
+ return
+}
diff --git a/common/net/packet/packet_sing.go b/common/net/packet/packet_sing.go
new file mode 100644
index 00000000..cfcf5ed0
--- /dev/null
+++ b/common/net/packet/packet_sing.go
@@ -0,0 +1,79 @@
+package packet
+
+import (
+ "net"
+
+ "github.com/sagernet/sing/common/buf"
+ "github.com/sagernet/sing/common/bufio"
+ M "github.com/sagernet/sing/common/metadata"
+ N "github.com/sagernet/sing/common/network"
+)
+
+type SingPacketConn = N.NetPacketConn
+
+type EnhanceSingPacketConn interface {
+ SingPacketConn
+ EnhancePacketConn
+}
+
+type enhanceSingPacketConn struct {
+ SingPacketConn
+ packetReadWaiter N.PacketReadWaiter
+}
+
+func (c *enhanceSingPacketConn) WaitReadFrom() (data []byte, put func(), addr net.Addr, err error) {
+ var buff *buf.Buffer
+ var dest M.Socksaddr
+ newBuffer := func() *buf.Buffer {
+ buff = buf.NewPacket() // do not use stack buffer
+ return buff
+ }
+ if c.packetReadWaiter != nil {
+ c.packetReadWaiter.InitializeReadWaiter(newBuffer)
+ defer c.packetReadWaiter.InitializeReadWaiter(nil)
+ dest, err = c.packetReadWaiter.WaitReadPacket()
+ } else {
+ dest, err = c.SingPacketConn.ReadPacket(newBuffer())
+ }
+ if dest.IsFqdn() {
+ addr = dest
+ } else {
+ addr = dest.UDPAddr()
+ }
+ if err != nil {
+ if buff != nil {
+ buff.Release()
+ }
+ return
+ }
+ if buff == nil {
+ return
+ }
+ if buff.IsEmpty() {
+ buff.Release()
+ return
+ }
+ data = buff.Bytes()
+ put = buff.Release
+ return
+}
+
+func (c *enhanceSingPacketConn) Upstream() any {
+ return c.SingPacketConn
+}
+
+func (c *enhanceSingPacketConn) WriterReplaceable() bool {
+ return true
+}
+
+func (c *enhanceSingPacketConn) ReaderReplaceable() bool {
+ return true
+}
+
+func newEnhanceSingPacketConn(conn SingPacketConn) *enhanceSingPacketConn {
+ epc := &enhanceSingPacketConn{SingPacketConn: conn}
+ if readWaiter, isReadWaiter := bufio.CreatePacketReadWaiter(conn); isReadWaiter {
+ epc.packetReadWaiter = readWaiter
+ }
+ return epc
+}
diff --git a/common/net/packet/packet_windows.go b/common/net/packet/packet_windows.go
new file mode 100644
index 00000000..cb4c518b
--- /dev/null
+++ b/common/net/packet/packet_windows.go
@@ -0,0 +1,15 @@
+//go:build windows
+
+package packet
+
+import (
+ "net"
+)
+
+type enhanceUDPConn struct {
+ *net.UDPConn
+}
+
+func (c *enhanceUDPConn) WaitReadFrom() (data []byte, put func(), addr net.Addr, err error) {
+ return waitReadFrom(c.UDPConn)
+}
diff --git a/common/net/packet/ref.go b/common/net/packet/ref.go
new file mode 100644
index 00000000..a562b2e2
--- /dev/null
+++ b/common/net/packet/ref.go
@@ -0,0 +1,75 @@
+package packet
+
+import (
+ "net"
+ "runtime"
+ "time"
+)
+
+type refPacketConn struct {
+ pc EnhancePacketConn
+ ref any
+}
+
+func (c *refPacketConn) WaitReadFrom() (data []byte, put func(), addr net.Addr, err error) {
+ defer runtime.KeepAlive(c.ref)
+ return c.pc.WaitReadFrom()
+}
+
+func (c *refPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
+ defer runtime.KeepAlive(c.ref)
+ return c.pc.ReadFrom(p)
+}
+
+func (c *refPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
+ defer runtime.KeepAlive(c.ref)
+ return c.pc.WriteTo(p, addr)
+}
+
+func (c *refPacketConn) Close() error {
+ defer runtime.KeepAlive(c.ref)
+ return c.pc.Close()
+}
+
+func (c *refPacketConn) LocalAddr() net.Addr {
+ defer runtime.KeepAlive(c.ref)
+ return c.pc.LocalAddr()
+}
+
+func (c *refPacketConn) SetDeadline(t time.Time) error {
+ defer runtime.KeepAlive(c.ref)
+ return c.pc.SetDeadline(t)
+}
+
+func (c *refPacketConn) SetReadDeadline(t time.Time) error {
+ defer runtime.KeepAlive(c.ref)
+ return c.pc.SetReadDeadline(t)
+}
+
+func (c *refPacketConn) SetWriteDeadline(t time.Time) error {
+ defer runtime.KeepAlive(c.ref)
+ return c.pc.SetWriteDeadline(t)
+}
+
+func (c *refPacketConn) Upstream() any {
+ return c.pc
+}
+
+func (c *refPacketConn) ReaderReplaceable() bool { // Relay() will handle reference
+ return true
+}
+
+func (c *refPacketConn) WriterReplaceable() bool { // Relay() will handle reference
+ return true
+}
+
+func NewRefPacketConn(pc net.PacketConn, ref any) EnhancePacketConn {
+ rPC := &refPacketConn{pc: NewEnhancePacketConn(pc), ref: ref}
+ if singPC, isSingPC := pc.(SingPacketConn); isSingPC {
+ return &refSingPacketConn{
+ refPacketConn: rPC,
+ singPacketConn: singPC,
+ }
+ }
+ return rPC
+}
diff --git a/common/net/packet/ref_sing.go b/common/net/packet/ref_sing.go
new file mode 100644
index 00000000..2ca955fa
--- /dev/null
+++ b/common/net/packet/ref_sing.go
@@ -0,0 +1,26 @@
+package packet
+
+import (
+ "runtime"
+
+ "github.com/sagernet/sing/common/buf"
+ M "github.com/sagernet/sing/common/metadata"
+ N "github.com/sagernet/sing/common/network"
+)
+
+type refSingPacketConn struct {
+ *refPacketConn
+ singPacketConn SingPacketConn
+}
+
+var _ N.NetPacketConn = (*refSingPacketConn)(nil)
+
+func (c *refSingPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
+ defer runtime.KeepAlive(c.ref)
+ return c.singPacketConn.WritePacket(buffer, destination)
+}
+
+func (c *refSingPacketConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) {
+ defer runtime.KeepAlive(c.ref)
+ return c.singPacketConn.ReadPacket(buffer)
+}
diff --git a/common/net/packet/thread.go b/common/net/packet/thread.go
new file mode 100644
index 00000000..14d64233
--- /dev/null
+++ b/common/net/packet/thread.go
@@ -0,0 +1,36 @@
+package packet
+
+import (
+ "net"
+ "sync"
+)
+
+type threadSafePacketConn struct {
+ EnhancePacketConn
+ access sync.Mutex
+}
+
+func (c *threadSafePacketConn) WriteTo(b []byte, addr net.Addr) (int, error) {
+ c.access.Lock()
+ defer c.access.Unlock()
+ return c.EnhancePacketConn.WriteTo(b, addr)
+}
+
+func (c *threadSafePacketConn) Upstream() any {
+ return c.EnhancePacketConn
+}
+
+func (c *threadSafePacketConn) ReaderReplaceable() bool {
+ return true
+}
+
+func NewThreadSafePacketConn(pc net.PacketConn) EnhancePacketConn {
+ tsPC := &threadSafePacketConn{EnhancePacketConn: NewEnhancePacketConn(pc)}
+ if singPC, isSingPC := pc.(SingPacketConn); isSingPC {
+ return &threadSafeSingPacketConn{
+ threadSafePacketConn: tsPC,
+ singPacketConn: singPC,
+ }
+ }
+ return tsPC
+}
diff --git a/common/net/packet/thread_sing.go b/common/net/packet/thread_sing.go
new file mode 100644
index 00000000..0869a512
--- /dev/null
+++ b/common/net/packet/thread_sing.go
@@ -0,0 +1,24 @@
+package packet
+
+import (
+ "github.com/sagernet/sing/common/buf"
+ M "github.com/sagernet/sing/common/metadata"
+ N "github.com/sagernet/sing/common/network"
+)
+
+type threadSafeSingPacketConn struct {
+ *threadSafePacketConn
+ singPacketConn SingPacketConn
+}
+
+var _ N.NetPacketConn = (*threadSafeSingPacketConn)(nil)
+
+func (c *threadSafeSingPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
+ c.access.Lock()
+ defer c.access.Unlock()
+ return c.singPacketConn.WritePacket(buffer, destination)
+}
+
+func (c *threadSafeSingPacketConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) {
+ return c.singPacketConn.ReadPacket(buffer)
+}
diff --git a/common/net/refconn.go b/common/net/refconn.go
index 537cb839..5caaebc8 100644
--- a/common/net/refconn.go
+++ b/common/net/refconn.go
@@ -80,47 +80,3 @@ var _ ExtendedConn = (*refConn)(nil)
func NewRefConn(conn net.Conn, ref any) net.Conn {
return &refConn{conn: NewExtendedConn(conn), ref: ref}
}
-
-type refPacketConn struct {
- pc net.PacketConn
- ref any
-}
-
-func (pc *refPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
- defer runtime.KeepAlive(pc.ref)
- return pc.pc.ReadFrom(p)
-}
-
-func (pc *refPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
- defer runtime.KeepAlive(pc.ref)
- return pc.pc.WriteTo(p, addr)
-}
-
-func (pc *refPacketConn) Close() error {
- defer runtime.KeepAlive(pc.ref)
- return pc.pc.Close()
-}
-
-func (pc *refPacketConn) LocalAddr() net.Addr {
- defer runtime.KeepAlive(pc.ref)
- return pc.pc.LocalAddr()
-}
-
-func (pc *refPacketConn) SetDeadline(t time.Time) error {
- defer runtime.KeepAlive(pc.ref)
- return pc.pc.SetDeadline(t)
-}
-
-func (pc *refPacketConn) SetReadDeadline(t time.Time) error {
- defer runtime.KeepAlive(pc.ref)
- return pc.pc.SetReadDeadline(t)
-}
-
-func (pc *refPacketConn) SetWriteDeadline(t time.Time) error {
- defer runtime.KeepAlive(pc.ref)
- return pc.pc.SetWriteDeadline(t)
-}
-
-func NewRefPacketConn(pc net.PacketConn, ref any) net.PacketConn {
- return &refPacketConn{pc: pc, ref: ref}
-}
diff --git a/common/net/sing.go b/common/net/sing.go
index 7eb92f03..c92008ba 100644
--- a/common/net/sing.go
+++ b/common/net/sing.go
@@ -23,10 +23,6 @@ func NewDeadlineConn(conn net.Conn) ExtendedConn {
return deadline.NewFallbackConn(conn)
}
-func NewDeadlinePacketConn(pc net.PacketConn) net.PacketConn {
- return deadline.NewFallbackPacketConn(bufio.NewPacketConn(pc))
-}
-
func NeedHandshake(conn any) bool {
if earlyConn, isEarlyConn := common.Cast[network.EarlyConn](conn); isEarlyConn && earlyConn.NeedHandshake() {
return true
diff --git a/component/dialer/tfo.go b/component/dialer/tfo.go
index 09d5cced..4863d6ae 100644
--- a/component/dialer/tfo.go
+++ b/component/dialer/tfo.go
@@ -18,10 +18,11 @@ type tfoConn struct {
}
func (c *tfoConn) Dial(earlyData []byte) (err error) {
- c.Conn, err = c.dialFn(c.ctx, earlyData)
+ conn, err := c.dialFn(c.ctx, earlyData)
if err != nil {
return
}
+ c.Conn = conn
c.dialed <- true
return err
}
diff --git a/component/geodata/strmatcher/mph_matcher.go b/component/geodata/strmatcher/mph_matcher.go
index 3c10cb49..8d8b0508 100644
--- a/component/geodata/strmatcher/mph_matcher.go
+++ b/component/geodata/strmatcher/mph_matcher.go
@@ -234,26 +234,26 @@ tail:
case s == 0:
case s < 4:
h ^= uint64(*(*byte)(p))
- h ^= uint64(*(*byte)(add(p, s>>1))) << 8
- h ^= uint64(*(*byte)(add(p, s-1))) << 16
+ h ^= uint64(*(*byte)(unsafe.Add(p, s>>1))) << 8
+ h ^= uint64(*(*byte)(unsafe.Add(p, s-1))) << 16
h = rotl31(h*m1) * m2
case s <= 8:
h ^= uint64(readUnaligned32(p))
- h ^= uint64(readUnaligned32(add(p, s-4))) << 32
+ h ^= uint64(readUnaligned32(unsafe.Add(p, s-4))) << 32
h = rotl31(h*m1) * m2
case s <= 16:
h ^= readUnaligned64(p)
h = rotl31(h*m1) * m2
- h ^= readUnaligned64(add(p, s-8))
+ h ^= readUnaligned64(unsafe.Add(p, s-8))
h = rotl31(h*m1) * m2
case s <= 32:
h ^= readUnaligned64(p)
h = rotl31(h*m1) * m2
- h ^= readUnaligned64(add(p, 8))
+ h ^= readUnaligned64(unsafe.Add(p, 8))
h = rotl31(h*m1) * m2
- h ^= readUnaligned64(add(p, s-16))
+ h ^= readUnaligned64(unsafe.Add(p, s-16))
h = rotl31(h*m1) * m2
- h ^= readUnaligned64(add(p, s-8))
+ h ^= readUnaligned64(unsafe.Add(p, s-8))
h = rotl31(h*m1) * m2
default:
v1 := h
@@ -263,16 +263,16 @@ tail:
for s >= 32 {
v1 ^= readUnaligned64(p)
v1 = rotl31(v1*m1) * m2
- p = add(p, 8)
+ p = unsafe.Add(p, 8)
v2 ^= readUnaligned64(p)
v2 = rotl31(v2*m2) * m3
- p = add(p, 8)
+ p = unsafe.Add(p, 8)
v3 ^= readUnaligned64(p)
v3 = rotl31(v3*m3) * m4
- p = add(p, 8)
+ p = unsafe.Add(p, 8)
v4 ^= readUnaligned64(p)
v4 = rotl31(v4*m4) * m1
- p = add(p, 8)
+ p = unsafe.Add(p, 8)
s -= 32
}
h = v1 ^ v2 ^ v3 ^ v4
@@ -285,10 +285,6 @@ tail:
return uintptr(h)
}
-func add(p unsafe.Pointer, x uintptr) unsafe.Pointer {
- return unsafe.Pointer(uintptr(p) + x)
-}
-
func readUnaligned32(p unsafe.Pointer) uint32 {
q := (*[4]byte)(p)
return uint32(q[0]) | uint32(q[1])<<8 | uint32(q[2])<<16 | uint32(q[3])<<24
diff --git a/component/http/http.go b/component/http/http.go
index ece7b442..bcede09f 100644
--- a/component/http/http.go
+++ b/component/http/http.go
@@ -53,8 +53,12 @@ func HttpRequest(ctx context.Context, url, method string, header map[string][]st
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
DialContext: func(ctx context.Context, network, address string) (net.Conn, error) {
- conn := inner.HandleTcp(address, "")
- return conn, nil
+ if conn, err := inner.HandleTcp(address); err == nil {
+ return conn, nil
+ } else {
+ d := net.Dialer{}
+ return d.DialContext(ctx, network, address)
+ }
},
TLSClientConfig: tls.GetDefaultTLSConfig(),
}
diff --git a/component/proxydialer/proxydialer.go b/component/proxydialer/proxydialer.go
index fc5cb294..83010f96 100644
--- a/component/proxydialer/proxydialer.go
+++ b/component/proxydialer/proxydialer.go
@@ -71,7 +71,7 @@ func (p proxyDialer) DialContext(ctx context.Context, network, address string) (
func (p proxyDialer) ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort) (net.PacketConn, error) {
currentMeta := &C.Metadata{Type: C.INNER}
- if err := currentMeta.SetRemoteAddress(address); err != nil {
+ if err := currentMeta.SetRemoteAddress(rAddrPort.String()); err != nil {
return nil, err
}
return p.listenPacket(ctx, currentMeta)
diff --git a/component/tls/config.go b/component/tls/config.go
index b5b56591..6f808248 100644
--- a/component/tls/config.go
+++ b/component/tls/config.go
@@ -33,10 +33,22 @@ func AddCertificate(certificate string) error {
}
}
+func initializeCertPool() {
+ var err error
+ certPool, err = x509.SystemCertPool()
+ if err != nil {
+ certPool = x509.NewCertPool()
+ }
+ for _, cert := range trustCerts {
+ certPool.AddCert(cert)
+ }
+}
+
func ResetCertificate() {
mutex.Lock()
defer mutex.Unlock()
trustCerts = nil
+ initializeCertPool()
}
func getCertPool() *x509.CertPool {
@@ -49,12 +61,7 @@ func getCertPool() *x509.CertPool {
if certPool != nil {
return certPool
}
- certPool, err := x509.SystemCertPool()
- if err == nil {
- for _, cert := range trustCerts {
- certPool.AddCert(cert)
- }
- }
+ initializeCertPool()
}
return certPool
}
diff --git a/component/tls/reality.go b/component/tls/reality.go
index dd4f3af8..b8a7fa3a 100644
--- a/component/tls/reality.go
+++ b/component/tls/reality.go
@@ -141,7 +141,7 @@ var pOffset = utils.MustOK(reflect.TypeOf((*utls.UConn)(nil)).Elem().FieldByName
func (c *realityVerifier) VerifyPeerCertificate(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
//p, _ := reflect.TypeOf(c.Conn).Elem().FieldByName("peerCertificates")
- certs := *(*[]*x509.Certificate)(unsafe.Pointer(uintptr(unsafe.Pointer(c.Conn)) + pOffset))
+ certs := *(*[]*x509.Certificate)(unsafe.Add(unsafe.Pointer(c.Conn), pOffset))
if pub, ok := certs[0].PublicKey.(ed25519.PublicKey); ok {
h := hmac.New(sha512.New, c.authKey)
h.Write(pub)
diff --git a/config/config.go b/config/config.go
index 71f4087e..ff16ac11 100644
--- a/config/config.go
+++ b/config/config.go
@@ -841,7 +841,7 @@ func parseHosts(cfg *RawConfig) (*trie.DomainTrie[resolver.HostValue], error) {
} else {
ips := make([]netip.Addr, 0)
for _, addr := range addrs {
- if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback()&&!ipnet.IP.IsLinkLocalUnicast() {
+ if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() && !ipnet.IP.IsLinkLocalUnicast() {
if ip, err := netip.ParseAddr(ipnet.IP.String()); err == nil {
ips = append(ips, ip)
}
@@ -938,6 +938,8 @@ func parseNameServer(servers []string, preferH3 bool) ([]dns.NameServer, error)
case "quic":
addr, err = hostWithDefaultPort(u.Host, "853")
dnsNetType = "quic" // DNS over QUIC
+ case "system":
+ dnsNetType = "system" // System DNS
default:
return nil, fmt.Errorf("DNS NameServer[%d] unsupport scheme: %s", idx, u.Scheme)
}
@@ -972,6 +974,10 @@ func parsePureDNSServer(server string) string {
return "udp://" + server
}
+ if server == "system" {
+ return "system://"
+ }
+
if ip, err := netip.ParseAddr(server); err != nil {
if strings.Contains(server, "://") {
return server
@@ -1142,6 +1148,9 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul
}
// check default nameserver is pure ip addr
for _, ns := range dnsCfg.DefaultNameserver {
+ if ns.Net == "system" {
+ continue
+ }
host, _, err := net.SplitHostPort(ns.Addr)
if err != nil || net.ParseIP(host) == nil {
u, err := url.Parse(ns.Addr)
diff --git a/constant/adapters.go b/constant/adapters.go
index 2a2c68c1..12579685 100644
--- a/constant/adapters.go
+++ b/constant/adapters.go
@@ -81,7 +81,7 @@ type Conn interface {
}
type PacketConn interface {
- net.PacketConn
+ N.EnhancePacketConn
Connection
// Deprecate WriteWithMetadata because of remote resolve DNS cause TURN failed
// WriteWithMetadata(p []byte, metadata *Metadata) (n int, err error)
@@ -106,11 +106,11 @@ type ProxyAdapter interface {
//
// Examples:
// conn, _ := net.DialContext(context.Background(), "tcp", "host:port")
- // conn, _ = adapter.StreamConn(conn, metadata)
+ // conn, _ = adapter.StreamConnContext(context.Background(), conn, metadata)
//
// It returns a C.Conn with protocol which start with
// a new session (if any)
- StreamConn(c net.Conn, metadata *Metadata) (net.Conn, error)
+ StreamConnContext(ctx context.Context, c net.Conn, metadata *Metadata) (net.Conn, error)
// DialContext return a C.Conn with protocol which
// contains multiplexing-related reuse logic (if any)
diff --git a/constant/metadata.go b/constant/metadata.go
index 1c344d5d..edc58aec 100644
--- a/constant/metadata.go
+++ b/constant/metadata.go
@@ -133,6 +133,7 @@ type Metadata struct {
InIP netip.Addr `json:"inboundIP"`
InPort string `json:"inboundPort"`
InName string `json:"inboundName"`
+ InUser string `json:"inboundUser"`
Host string `json:"host"`
DNSMode DNSMode `json:"dnsMode"`
Uid uint32 `json:"uid"`
@@ -205,15 +206,16 @@ func (m *Metadata) Pure() *Metadata {
return m
}
+func (m *Metadata) AddrPort() netip.AddrPort {
+ port, _ := strconv.ParseUint(m.DstPort, 10, 16)
+ return netip.AddrPortFrom(m.DstIP.Unmap(), uint16(port))
+}
+
func (m *Metadata) UDPAddr() *net.UDPAddr {
if m.NetWork != UDP || !m.DstIP.IsValid() {
return nil
}
- port, _ := strconv.ParseUint(m.DstPort, 10, 16)
- return &net.UDPAddr{
- IP: m.DstIP.AsSlice(),
- Port: int(port),
- }
+ return net.UDPAddrFromAddrPort(m.AddrPort())
}
func (m *Metadata) String() string {
diff --git a/constant/rule.go b/constant/rule.go
index 28c629a0..906f3cef 100644
--- a/constant/rule.go
+++ b/constant/rule.go
@@ -14,12 +14,14 @@ const (
SrcPort
DstPort
InPort
+ InUser
+ InName
+ InType
Process
ProcessPath
RuleSet
Network
Uid
- INTYPE
SubRules
MATCH
AND
@@ -55,6 +57,12 @@ func (rt RuleType) String() string {
return "DstPort"
case InPort:
return "InPort"
+ case InUser:
+ return "InUser"
+ case InName:
+ return "InName"
+ case InType:
+ return "InType"
case Process:
return "Process"
case ProcessPath:
@@ -67,8 +75,6 @@ func (rt RuleType) String() string {
return "Network"
case Uid:
return "Uid"
- case INTYPE:
- return "InType"
case SubRules:
return "SubRules"
case AND:
diff --git a/dns/system.go b/dns/system.go
new file mode 100644
index 00000000..f5ab0efb
--- /dev/null
+++ b/dns/system.go
@@ -0,0 +1,23 @@
+package dns
+
+import (
+ "net"
+)
+
+func loadSystemResolver() (clients []dnsClient, err error) {
+ nameservers, err := dnsReadConfig()
+ if err != nil {
+ return
+ }
+ if len(nameservers) == 0 {
+ return
+ }
+ servers := make([]NameServer, 0, len(nameservers))
+ for _, addr := range nameservers {
+ servers = append(servers, NameServer{
+ Addr: net.JoinHostPort(addr, "53"),
+ Net: "udp",
+ })
+ }
+ return transform(servers, nil), nil
+}
diff --git a/dns/system_posix.go b/dns/system_posix.go
new file mode 100644
index 00000000..d486b4fb
--- /dev/null
+++ b/dns/system_posix.go
@@ -0,0 +1,27 @@
+//go:build !windows
+
+package dns
+
+import (
+ "fmt"
+ "os"
+ "regexp"
+)
+
+var (
+ // nameserver xxx.xxx.xxx.xxx
+ nameserverPattern = regexp.MustCompile(`nameserver\s+(?P\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})`)
+)
+
+func dnsReadConfig() (servers []string, err error) {
+ content, err := os.ReadFile("/etc/resolv.conf")
+ if err != nil {
+ err = fmt.Errorf("failed to read /etc/resolv.conf: %w", err)
+ return
+ }
+ for _, line := range nameserverPattern.FindAllStringSubmatch(string(content), -1) {
+ addr := line[1]
+ servers = append(servers, addr)
+ }
+ return
+}
diff --git a/dns/system_windows.go b/dns/system_windows.go
new file mode 100644
index 00000000..47c1ebaa
--- /dev/null
+++ b/dns/system_windows.go
@@ -0,0 +1,77 @@
+//go:build windows
+
+package dns
+
+import (
+ "net"
+ "os"
+ "syscall"
+ "unsafe"
+
+ "golang.org/x/sys/windows"
+)
+
+func dnsReadConfig() (servers []string, err error) {
+ aas, err := adapterAddresses()
+ if err != nil {
+ return
+ }
+ for _, aa := range aas {
+ for dns := aa.FirstDnsServerAddress; dns != nil; dns = dns.Next {
+ sa, err := dns.Address.Sockaddr.Sockaddr()
+ if err != nil {
+ continue
+ }
+ var ip net.IP
+ switch sa := sa.(type) {
+ case *syscall.SockaddrInet4:
+ ip = net.IPv4(sa.Addr[0], sa.Addr[1], sa.Addr[2], sa.Addr[3])
+ case *syscall.SockaddrInet6:
+ ip = make(net.IP, net.IPv6len)
+ copy(ip, sa.Addr[:])
+ if ip[0] == 0xfe && ip[1] == 0xc0 {
+ // Ignore these fec0/10 ones. Windows seems to
+ // populate them as defaults on its misc rando
+ // interfaces.
+ continue
+ }
+ //continue
+ default:
+ // Unexpected type.
+ continue
+ }
+ servers = append(servers, ip.String())
+ }
+ }
+ return
+}
+
+// adapterAddresses returns a list of IP adapter and address
+// structures. The structure contains an IP adapter and flattened
+// multiple IP addresses including unicast, anycast and multicast
+// addresses.
+func adapterAddresses() ([]*windows.IpAdapterAddresses, error) {
+ var b []byte
+ l := uint32(15000) // recommended initial size
+ for {
+ b = make([]byte, l)
+ err := windows.GetAdaptersAddresses(syscall.AF_UNSPEC, windows.GAA_FLAG_INCLUDE_PREFIX, 0, (*windows.IpAdapterAddresses)(unsafe.Pointer(&b[0])), &l)
+ if err == nil {
+ if l == 0 {
+ return nil, nil
+ }
+ break
+ }
+ if err.(syscall.Errno) != syscall.ERROR_BUFFER_OVERFLOW {
+ return nil, os.NewSyscallError("getadaptersaddresses", err)
+ }
+ if l <= uint32(len(b)) {
+ return nil, os.NewSyscallError("getadaptersaddresses", err)
+ }
+ }
+ var aas []*windows.IpAdapterAddresses
+ for aa := (*windows.IpAdapterAddresses)(unsafe.Pointer(&b[0])); aa != nil; aa = aa.Next {
+ aas = append(aas, aa)
+ }
+ return aas, nil
+}
diff --git a/dns/util.go b/dns/util.go
index bfd2e9ed..d85deb17 100644
--- a/dns/util.go
+++ b/dns/util.go
@@ -79,6 +79,18 @@ func transform(servers []NameServer, resolver *Resolver) []dnsClient {
case "dhcp":
ret = append(ret, newDHCPClient(s.Addr))
continue
+ case "system":
+ clients, err := loadSystemResolver()
+ if err != nil {
+ log.Errorln("[DNS:system] load system resolver failed: %s", err.Error())
+ continue
+ }
+ if len(clients) == 0 {
+ log.Errorln("[DNS:system] no nameserver found in system")
+ continue
+ }
+ ret = append(ret, clients...)
+ continue
case "quic":
if doq, err := newDoQ(resolver, s.Addr, s.ProxyAdapter, s.ProxyName); err == nil {
ret = append(ret, doq)
diff --git a/docs/config.yaml b/docs/config.yaml
index 97bd40ec..4239abcf 100644
--- a/docs/config.yaml
+++ b/docs/config.yaml
@@ -77,32 +77,32 @@ tun:
# auto-detect-interface: true # 自动识别出口网卡
# auto-route: true # 配置路由表
# mtu: 9000 # 最大传输单元
- # strict_route: true # 将所有连接路由到tun来防止泄漏,但你的设备将无法其他设备被访问
- inet4_route_address: # 启用 auto_route 时使用自定义路由而不是默认路由
+ # strict-route: true # 将所有连接路由到tun来防止泄漏,但你的设备将无法其他设备被访问
+ inet4-route-address: # 启用 auto_route 时使用自定义路由而不是默认路由
- 0.0.0.0/1
- 128.0.0.0/1
- inet6_route_address: # 启用 auto_route 时使用自定义路由而不是默认路由
+ inet6-route-address: # 启用 auto_route 时使用自定义路由而不是默认路由
- "::/1"
- "8000::/1"
- # endpoint_independent_nat: false # 启用独立于端点的 NAT
- # include_uid: # UID 规则仅在 Linux 下被支持,并且需要 auto_route
+ # endpoint-independent-nat: false # 启用独立于端点的 NAT
+ # include-uid: # UID 规则仅在 Linux 下被支持,并且需要 auto_route
# - 0
- # include_uid_range: # 限制被路由的的用户范围
+ # include-uid-range: # 限制被路由的的用户范围
# - 1000-99999
- # exclude_uid: # 排除路由的的用户
+ # exclude-uid: # 排除路由的的用户
#- 1000
- # exclude_uid_range: # 排除路由的的用户范围
+ # exclude-uid-range: # 排除路由的的用户范围
# - 1000-99999
# Android 用户和应用规则仅在 Android 下被支持
- # 并且需要 auto_route
+ # 并且需要 auto-route
- # include_android_user: # 限制被路由的 Android 用户
+ # include-android-user: # 限制被路由的 Android 用户
# - 0
# - 10
- # include_package: # 限制被路由的 Android 应用包名
+ # include-package: # 限制被路由的 Android 应用包名
# - com.android.chrome
- # exclude_package: # 排除被路由的 Android 应用包名
+ # exclude-package: # 排除被路由的 Android 应用包名
# - com.android.captiveportallogin
#ebpf配置
@@ -177,6 +177,7 @@ dns:
- 8.8.8.8
- tls://1.12.12.12:853
- tls://223.5.5.5:853
+ - system # append DNS server from system configuration. If not found, it would print an error log and skip.
enhanced-mode: fake-ip # or redir-host
fake-ip-range: 198.18.0.1/16 # fake-ip 池设置
diff --git a/go.mod b/go.mod
index 0d4a638e..b79f8e70 100644
--- a/go.mod
+++ b/go.mod
@@ -5,9 +5,9 @@ go 1.19
require (
github.com/3andne/restls-client-go v0.1.4
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da
- github.com/cilium/ebpf v0.9.3
+ github.com/cilium/ebpf v0.10.0
github.com/coreos/go-iptables v0.6.0
- github.com/dlclark/regexp2 v1.7.0
+ github.com/dlclark/regexp2 v1.9.0
github.com/go-chi/chi/v5 v5.0.8
github.com/go-chi/cors v1.2.1
github.com/go-chi/render v1.0.2
@@ -17,38 +17,40 @@ require (
github.com/hashicorp/golang-lru v0.5.4
github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16
github.com/jpillora/backoff v1.0.0
+ github.com/klauspost/cpuid/v2 v2.0.12
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40
- github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7
- github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594
- github.com/metacubex/sing-shadowsocks v0.2.2-0.20230422111054-f54786eee8ba
- github.com/metacubex/sing-tun v0.1.4
+ github.com/mdlayher/netlink v1.7.2
+ github.com/metacubex/quic-go v0.33.3-0.20230510010206-687b537b6a58
+ github.com/metacubex/sing-shadowsocks v0.2.2-0.20230509230448-a5157cc00a1c
+ github.com/metacubex/sing-shadowsocks2 v0.0.0-20230529235701-a238874242ca
+ github.com/metacubex/sing-tun v0.1.5-0.20230530125750-171afb2dfd8e
github.com/metacubex/sing-wireguard v0.0.0-20230426030325-41db09ae771a
- github.com/miekg/dns v1.1.53
- github.com/mroth/weightedrand/v2 v2.0.0
+ github.com/miekg/dns v1.1.54
+ github.com/mroth/weightedrand/v2 v2.0.1
github.com/openacid/low v0.1.21
github.com/oschwald/geoip2-golang v1.8.0
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97
- github.com/sagernet/sing v0.2.5-0.20230425211221-a23ffbaeb5b9
- github.com/sagernet/sing-mux v0.0.0-20230427141602-9836fc9b052e
+ github.com/sagernet/sing v0.2.5-0.20230530114415-221f066dba7c
+ github.com/sagernet/sing-mux v0.0.0-20230517134606-1ebe6bb26646
github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b
github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2
github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77
github.com/samber/lo v1.38.1
- github.com/shirou/gopsutil/v3 v3.23.3
+ github.com/shirou/gopsutil/v3 v3.23.4
github.com/sirupsen/logrus v1.9.0
github.com/stretchr/testify v1.8.2
- github.com/xtls/go v0.0.0-20220914232946-0441cf4cf837
+ github.com/xtls/go v0.0.0-20230107031059-4610f88d00f3
github.com/zhangyunhao116/fastrand v0.3.0
- go.etcd.io/bbolt v1.3.6
+ go.etcd.io/bbolt v1.3.7
go.uber.org/automaxprocs v1.5.2
- golang.org/x/crypto v0.8.0
- golang.org/x/exp v0.0.0-20230321023759-10a507213a29
- golang.org/x/net v0.9.0
- golang.org/x/sync v0.1.0
- golang.org/x/sys v0.7.0
- google.golang.org/protobuf v1.28.2-0.20230118093459-a9481185b34d
+ golang.org/x/crypto v0.9.0
+ golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53
+ golang.org/x/net v0.10.0
+ golang.org/x/sync v0.2.0
+ golang.org/x/sys v0.8.0
+ google.golang.org/protobuf v1.30.0
gopkg.in/yaml.v3 v3.0.1
lukechampine.com/blake3 v1.1.7
)
@@ -67,17 +69,15 @@ require (
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
github.com/golang/mock v1.6.0 // indirect
- github.com/google/btree v1.0.1 // indirect
+ github.com/google/btree v1.1.2 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
github.com/hashicorp/yamux v0.1.1 // indirect
github.com/josharian/native v1.1.0 // indirect
github.com/klauspost/compress v1.15.15 // indirect
- github.com/klauspost/cpuid/v2 v2.0.12 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
- github.com/mdlayher/socket v0.4.0 // indirect
+ github.com/mdlayher/socket v0.4.1 // indirect
github.com/metacubex/gvisor v0.0.0-20230417114019-3c3ee672d60c // indirect
- github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // indirect
github.com/onsi/ginkgo/v2 v2.2.0 // indirect
github.com/oschwald/maxminddb-golang v1.10.0 // indirect
@@ -89,6 +89,7 @@ require (
github.com/quic-go/qtls-go1-20 v0.1.1 // indirect
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 // indirect
+ github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 // indirect
github.com/shoenig/go-m1cpu v0.1.5 // indirect
github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b // indirect
github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c // indirect
@@ -96,12 +97,15 @@ require (
github.com/tklauser/go-sysconf v0.3.11 // indirect
github.com/tklauser/numcpus v0.6.0 // indirect
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
- github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
+ github.com/vishvananda/netns v0.0.4 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect
golang.org/x/mod v0.8.0 // indirect
golang.org/x/text v0.9.0 // indirect
- golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
+ golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.6.0 // indirect
- gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
)
+
+replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20230530121223-b768faae5c6b
+
+replace github.com/sagernet/sing-vmess => github.com/metacubex/sing-vmess v0.1.5-0.20230520082358-78b126617899
diff --git a/go.sum b/go.sum
index 37cc84b1..11d98c03 100644
--- a/go.sum
+++ b/go.sum
@@ -14,15 +14,15 @@ github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnweb
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
-github.com/cilium/ebpf v0.9.3 h1:5KtxXZU+scyERvkJMEm16TbScVvuuMrlhPly78ZMbSc=
-github.com/cilium/ebpf v0.9.3/go.mod h1:w27N4UjpaQ9X/DGrSugxUG+H+NhgntDuPb5lCzxCn8A=
+github.com/cilium/ebpf v0.10.0 h1:nk5HPMeoBXtOzbkZBWym+ZWq1GIiHUsBFXxwewXAHLQ=
+github.com/cilium/ebpf v0.10.0/go.mod h1:DPiVdY/kT534dgc9ERmvP8mWA+9gvwgKfRvk4nNWnoE=
github.com/coreos/go-iptables v0.6.0 h1:is9qnZMPYjLd8LYqmm/qlE+wwEgJIkTYdhV3rfZo4jk=
github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo=
-github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
+github.com/dlclark/regexp2 v1.9.0 h1:pTK/l/3qYIKaRXuHnEnIf7Y5NxfRPfpb7dis6/gdlVI=
+github.com/dlclark/regexp2 v1.9.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9 h1:/5RkVc9Rc81XmMyVqawCiDyrBHZbLAZgTTCqou4mwj8=
github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9/go.mod h1:hkIFzoiIPZYxdFOOLyDho59b7SrDfo+w3h+yWdlg45I=
github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 h1:8j2RH289RJplhA6WfdaPqzg1MjH2K8wX5e0uhAxrw2g=
@@ -32,7 +32,7 @@ github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1 h1:tlDMEdcPRQKBE
github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1/go.mod h1:4RfsapbGx2j/vU5xC/5/9qB3kn9Awp1YDiEnN43QrJ4=
github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010 h1:fuGucgPk5dN6wzfnxl3D0D3rVLw4v2SbBT9jb4VnxzA=
github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010/go.mod h1:JtBcj7sBuTTRupn7c2bFspMDIObMJsVK8TeUvpShPok=
-github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss=
+github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0=
@@ -52,8 +52,8 @@ github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+Licev
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
-github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
-github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
+github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
+github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
@@ -82,34 +82,36 @@ github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.0.12 h1:p9dKCg8i4gmOxtv35DvrYoWqYzQrvEVdjQ762Y0OqZE=
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
-github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
-github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc=
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg=
-github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7 h1:HSkXG1bE/qcRuuPlZ2Jyf0Od8HLxOowi7CzKQqNtWn4=
-github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7/go.mod h1:1ztDZHGbU5MjN5lNZpkpG8ygndjjWzcojp/H7r6l6QQ=
-github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw=
-github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc=
+github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
+github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=
+github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
+github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
github.com/metacubex/gvisor v0.0.0-20230417114019-3c3ee672d60c h1:D62872jiuzC6b+3aI8tqfeyc6YgbfarYKywTnnvXwEM=
github.com/metacubex/gvisor v0.0.0-20230417114019-3c3ee672d60c/go.mod h1:wqEuzdImyqD2MCGE8CYRJXbB77oSEJeoSSXXdwKjnsE=
-github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594 h1:KD96JPdTIayTGGgRl6PuVqo2Bpo6+x3LqDDyqrYDDXw=
-github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594/go.mod h1:9nOiGX6kqV3+ZbkDKdTNzdFD726QQHPH6WDb36jUSpA=
-github.com/metacubex/sing-shadowsocks v0.2.2-0.20230422111054-f54786eee8ba h1:He8YwyK600lHAS1xxNsP4k/jnZ8zqQ34XjCGn925+Yk=
-github.com/metacubex/sing-shadowsocks v0.2.2-0.20230422111054-f54786eee8ba/go.mod h1:4uQQReKMTU7KTfOykVBe/oGJ00pl38d+BYJ99+mx26s=
-github.com/metacubex/sing-tun v0.1.4 h1:OQDBNHjuPKrOprCiK+sLt97YQ0K6b9ZWmJB6z51ibZQ=
-github.com/metacubex/sing-tun v0.1.4/go.mod h1:BMfG00enVf90/CzcdX9PK3Dymgl7BZqHXJfexEyB7Cc=
+github.com/metacubex/quic-go v0.33.3-0.20230510010206-687b537b6a58 h1:E/sNW9tugFjoBjAkth89MHlKHRaMdo43tGQ3MOPVayQ=
+github.com/metacubex/quic-go v0.33.3-0.20230510010206-687b537b6a58/go.mod h1:9nOiGX6kqV3+ZbkDKdTNzdFD726QQHPH6WDb36jUSpA=
+github.com/metacubex/sing v0.0.0-20230530121223-b768faae5c6b h1:Bw4j3ktf5vivi5qm/ZQGtyRAgybRKSGJaMV1t3rtC+I=
+github.com/metacubex/sing v0.0.0-20230530121223-b768faae5c6b/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w=
+github.com/metacubex/sing-shadowsocks v0.2.2-0.20230509230448-a5157cc00a1c h1:LpVNvlW/xE+mR8z76xJeYZlYznZXEmU4TeWeuygYdJg=
+github.com/metacubex/sing-shadowsocks v0.2.2-0.20230509230448-a5157cc00a1c/go.mod h1:4uQQReKMTU7KTfOykVBe/oGJ00pl38d+BYJ99+mx26s=
+github.com/metacubex/sing-shadowsocks2 v0.0.0-20230529235701-a238874242ca h1:10qc50Q1hHrfGO4NjEJpIAgHX63Y256tHE0dFCTN8J4=
+github.com/metacubex/sing-shadowsocks2 v0.0.0-20230529235701-a238874242ca/go.mod h1:jVDD4N22bDPPKA73NvB7aqdlLWiAwv8D+jx7HwhcWak=
+github.com/metacubex/sing-tun v0.1.5-0.20230530125750-171afb2dfd8e h1:7QlJQl4S3F3YXn48fYxjymMw8HkXg9bl++hLi4ZRyCY=
+github.com/metacubex/sing-tun v0.1.5-0.20230530125750-171afb2dfd8e/go.mod h1:u9onX49LZPYuIPQ7SdM64Gnins8y5wg4Cn6ZYRSxWHU=
+github.com/metacubex/sing-vmess v0.1.5-0.20230520082358-78b126617899 h1:iRfcuztp7REfmOyasSlCL/pqNWfUDMTJ2CwbGpxpeks=
+github.com/metacubex/sing-vmess v0.1.5-0.20230520082358-78b126617899/go.mod h1:RSt9rxGHllLdc5JUebkQwaqyWLx09Lqya37DlBe8CP8=
github.com/metacubex/sing-wireguard v0.0.0-20230426030325-41db09ae771a h1:cWKym33Qvl6HA3hj4/YuYD8hHyqQPb47wT5cJRAPgco=
github.com/metacubex/sing-wireguard v0.0.0-20230426030325-41db09ae771a/go.mod h1:Bsw2BvKMMMY0FhZPseDI50ZOalvoUPMKYyGpyqvIIqY=
-github.com/miekg/dns v1.1.53 h1:ZBkuHr5dxHtB1caEOlZTLPo7D3L3TWckgUUs/RHfDxw=
-github.com/miekg/dns v1.1.53/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
-github.com/mroth/weightedrand/v2 v2.0.0 h1:ADehnByWbliEDIazDAKFdBHoqgHSXAkgyKqM/9YsPoo=
-github.com/mroth/weightedrand/v2 v2.0.0/go.mod h1:f2faGsfOGOwc1p94wzHKKZyTpcJUW7OJ/9U4yfiNAOU=
-github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
-github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
+github.com/miekg/dns v1.1.54 h1:5jon9mWcb0sFJGpnI99tOMhCPyJ+RPVz5b63MQG0VWI=
+github.com/miekg/dns v1.1.54/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
+github.com/mroth/weightedrand/v2 v2.0.1 h1:zrEVDIaau/E4QLOKu02kpg8T8myweFlMGikIgbIdrRA=
+github.com/mroth/weightedrand/v2 v2.0.1/go.mod h1:f2faGsfOGOwc1p94wzHKKZyTpcJUW7OJ/9U4yfiNAOU=
github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 h1:1102pQc2SEPp5+xrS26wEaeb26sZy6k9/ZXlZN+eXE4=
github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7/go.mod h1:UqoUn6cHESlliMhOnKLWr+CBH+e3bazUPvFj1XZwAjs=
github.com/onsi/ginkgo/v2 v2.2.0 h1:3ZNA3L1c5FYDFTTxbFeVGGD8jYvjYauHD30YgLxVsNI=
@@ -138,21 +140,15 @@ github.com/quic-go/qtls-go1-19 v0.2.1 h1:aJcKNMkH5ASEJB9FXNeZCyTEIHU1J7MmHyz1Q1T
github.com/quic-go/qtls-go1-19 v0.2.1/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
github.com/quic-go/qtls-go1-20 v0.1.1 h1:KbChDlg82d3IHqaj2bn6GfKRj84Per2VGf5XV3wSwQk=
github.com/quic-go/qtls-go1-20 v0.1.1/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
-github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
+github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms=
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
-github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
-github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
-github.com/sagernet/sing v0.2.5-0.20230425211221-a23ffbaeb5b9 h1:kpgKJbhesj6BBLTKIfBCJGQPm2ww7pNxn566C6TrHdA=
-github.com/sagernet/sing v0.2.5-0.20230425211221-a23ffbaeb5b9/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w=
-github.com/sagernet/sing-mux v0.0.0-20230427141602-9836fc9b052e h1:t8nuY9plpHEzlnPxOpuv64jhjz3teIvccu3YMFX4fJI=
-github.com/sagernet/sing-mux v0.0.0-20230427141602-9836fc9b052e/go.mod h1:pF+RnLvCAOhECrvauy6LYOpBakJ/vuaF1Wm4lPsWryI=
+github.com/sagernet/sing-mux v0.0.0-20230517134606-1ebe6bb26646 h1:X3ADfMqeGns1Q1FlXc9kaL9FwW1UM6D6tEQo8jFstpc=
+github.com/sagernet/sing-mux v0.0.0-20230517134606-1ebe6bb26646/go.mod h1:pF+RnLvCAOhECrvauy6LYOpBakJ/vuaF1Wm4lPsWryI=
github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b h1:ouW/6IDCrxkBe19YSbdCd7buHix7b+UZ6BM4Zz74XF4=
github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b/go.mod h1:oG8bPerYI6cZ74KquY3DvA7ynECyrILPBnce6wtBqeI=
-github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3 h1:BHOnxrbC929JonuKqFdJ7ZbDp7zs4oTlH5KFvKtWu9U=
-github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3/go.mod h1:yKrAr+dqZd64DxBXCHWrYicp+n4qbqO73mtwv3dck8U=
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as=
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37/go.mod h1:3skNSftZDJWTGVtVaM2jfbce8qHnmH/AGDRe62iNOg0=
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 h1:2ItpW1nMNkPzmBTxV0/eClCklHrFSQMnUGcpUmJxVeE=
@@ -163,9 +159,10 @@ github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77 h1:g6QtRWQ2d
github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77/go.mod h1:pJDdXzZIwJ+2vmnT0TKzmf8meeum+e2mTDSehw79eE0=
github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM=
github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
-github.com/shirou/gopsutil/v3 v3.23.3 h1:Syt5vVZXUDXPEXpIBt5ziWsJ4LdSAAxF4l/xZeQgSEE=
-github.com/shirou/gopsutil/v3 v3.23.3/go.mod h1:lSBNN6t3+D6W5e5nXTxc8KIMMVxAcS+6IJlffjRRlMU=
-github.com/shoenig/go-m1cpu v0.1.4/go.mod h1:Wwvst4LR89UxjeFtLRMrpgRiyY4xPsejnVZym39dbAQ=
+github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 h1:rc/CcqLH3lh8n+csdOuDfP+NuykE0U6AeYSJJHKDgSg=
+github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9/go.mod h1:a/83NAfUXvEuLpmxDssAXxgUgrEy12MId3Wd7OTs76s=
+github.com/shirou/gopsutil/v3 v3.23.4 h1:hZwmDxZs7Ewt75DV81r4pFMqbq+di2cbt9FsQBqLD2o=
+github.com/shirou/gopsutil/v3 v3.23.4/go.mod h1:ZcGxyfzAMRevhUR2+cfhXDH6gQdFYE/t8j1nsU4mPI8=
github.com/shoenig/go-m1cpu v0.1.5 h1:LF57Z/Fpb/WdGLjt2HZilNnmZOxg/q2bSKTQhgbrLrQ=
github.com/shoenig/go-m1cpu v0.1.5/go.mod h1:Wwvst4LR89UxjeFtLRMrpgRiyY4xPsejnVZym39dbAQ=
github.com/shoenig/test v0.6.3 h1:GVXWJFk9PiOjN0KoJ7VrJGH6uLPnqxR7/fe3HUPfE0c=
@@ -195,10 +192,10 @@ github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYm
github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4=
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA=
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
-github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
-github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
-github.com/xtls/go v0.0.0-20220914232946-0441cf4cf837 h1:AHhUwwFJGl27E46OpdJHplZkK09m7aETNBNzhT6t15M=
-github.com/xtls/go v0.0.0-20220914232946-0441cf4cf837/go.mod h1:YJTRELIWrGxR1s8xcEBgxcxBfwQfMGjdvNLTjN9XFgY=
+github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
+github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
+github.com/xtls/go v0.0.0-20230107031059-4610f88d00f3 h1:a3Y4WVjCxwoyO4E2xdNvq577tW8lkSBgyrA8E9+2NtM=
+github.com/xtls/go v0.0.0-20230107031059-4610f88d00f3/go.mod h1:YJTRELIWrGxR1s8xcEBgxcxBfwQfMGjdvNLTjN9XFgY=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
@@ -206,16 +203,16 @@ github.com/zhangyunhao116/fastrand v0.3.0 h1:7bwe124xcckPulX6fxtr2lFdO2KQqaefdtb
github.com/zhangyunhao116/fastrand v0.3.0/go.mod h1:0v5KgHho0VE6HU192HnY15de/oDS8UrbBChIFjIhBtc=
gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec h1:FpfFs4EhNehiVfzQttTuxanPIT43FtkkCFypIod8LHo=
gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec/go.mod h1:BZ1RAoRPbCxum9Grlv5aeksu2H8BiKehBYooU2LFiOQ=
-go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
-go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
+go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ=
+go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
go.uber.org/automaxprocs v1.5.2 h1:2LxUOGiR3O6tw8ui5sZa2LAaHnsviZdVOUZw4fvbnME=
go.uber.org/automaxprocs v1.5.2/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
-golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
-golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug=
-golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
+golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
+golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
+golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 h1:5llv2sWeaMSnA3w2kS57ouQQ4pudlXrR0dCgw51QK9o=
+golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@@ -224,39 +221,35 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
-golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
-golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
+golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
+golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
-golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
+golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
+golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
-golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44=
-golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
+golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
@@ -268,11 +261,10 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
-google.golang.org/protobuf v1.28.2-0.20230118093459-a9481185b34d h1:qp0AnQCvRCMlu9jBjtdbTaaEmThIgZOrbVyDEOcmKhQ=
-google.golang.org/protobuf v1.28.2-0.20230118093459-a9481185b34d/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
+google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
-gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
diff --git a/hub/route/upgrade.go b/hub/route/upgrade.go
index 0a7c54f9..a58f224c 100644
--- a/hub/route/upgrade.go
+++ b/hub/route/upgrade.go
@@ -30,8 +30,9 @@ func upgrade(w http.ResponseWriter, r *http.Request) {
err = updater.Update(execPath)
if err != nil {
+ log.Warnln("%s", err)
render.Status(r, http.StatusInternalServerError)
- render.JSON(w, r, newError(fmt.Sprintf("Upgrade: %s", err)))
+ render.JSON(w, r, newError(fmt.Sprintf("%s", err)))
return
}
diff --git a/hub/updater/updater.go b/hub/updater/updater.go
index 3d80707a..90b14c27 100644
--- a/hub/updater/updater.go
+++ b/hub/updater/updater.go
@@ -8,6 +8,7 @@ import (
"io"
"net/http"
"os"
+ "os/exec"
"path/filepath"
"runtime"
"strings"
@@ -17,15 +18,16 @@ import (
clashHttp "github.com/Dreamacro/clash/component/http"
"github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/log"
+
+ "github.com/klauspost/cpuid/v2"
)
// modify from https://github.com/AdguardTeam/AdGuardHome/blob/595484e0b3fb4c457f9bb727a6b94faa78a66c5f/internal/updater/updater.go
// Updater is the Clash.Meta updater.
var (
- goarch string
- goos string
- goarm string
- gomips string
+ goarm string
+ gomips string
+ amd64Compatible string
workDir string
@@ -45,6 +47,12 @@ var (
latestVersion string
)
+func init() {
+ if runtime.GOARCH == "amd64" && cpuid.CPU.X64Level() < 3 {
+ amd64Compatible = "-compatible"
+ }
+}
+
type updateError struct {
Message string
}
@@ -59,8 +67,6 @@ func Update(execPath string) (err error) {
mu.Lock()
defer mu.Unlock()
- goos = runtime.GOOS
- goarch = runtime.GOARCH
latestVersion, err = getLatestVersion()
if err != nil {
return err
@@ -69,7 +75,7 @@ func Update(execPath string) (err error) {
log.Infoln("current version %s, latest version %s", constant.Version, latestVersion)
if latestVersion == constant.Version {
- err := &updateError{Message: "Already using latest version"}
+ err := &updateError{Message: "already using latest version"}
return err
}
@@ -128,12 +134,10 @@ func prepare(exePath string) (err error) {
//log.Infoln(packageName)
backupDir = filepath.Join(workDir, "meta-backup")
- if goos == "windows" && goarch == "amd64" {
- updateExeName = "clash.meta" + "-" + goos + "-" + goarch + "-compatible.exe"
- } else if goos == "linux" && goarch == "amd64" {
- updateExeName = "clash.meta" + "-" + goos + "-" + goarch + "-compatible"
+ if runtime.GOOS == "windows" {
+ updateExeName = "clash.meta" + "-" + runtime.GOOS + "-" + runtime.GOARCH + amd64Compatible + ".exe"
} else {
- updateExeName = "clash.meta" + "-" + goos + "-" + goarch
+ updateExeName = "clash.meta" + "-" + runtime.GOOS + "-" + runtime.GOARCH + amd64Compatible
}
log.Infoln("updateExeName: %s ", updateExeName)
@@ -198,7 +202,7 @@ func replace() error {
var err error
log.Infoln("replacing: %s to %s", updateExeName, currentExeName)
- if goos == "windows" {
+ if runtime.GOOS == "windows" {
// rename fails with "File in use" error
err = copyFile(updateExeName, currentExeName)
} else {
@@ -430,17 +434,19 @@ func getLatestVersion() (version string, err error) {
func updateDownloadURL() {
var middle string
- if goarch == "arm" && goarm != "" {
- middle = fmt.Sprintf("-%s-%sv%s-%s", goos, goarch, goarm, latestVersion)
- } else if isMIPS(goarch) && gomips != "" {
- middle = fmt.Sprintf("-%s-%s-%s-%s", goos, goarch, gomips, latestVersion)
- } else if goarch == "amd64" && (goos == "windows" || goos == "linux") {
- middle = fmt.Sprintf("-%s-%s-compatible-%s", goos, goarch, latestVersion)
+ if runtime.GOARCH == "arm" && probeGoARM() {
+ //-linux-armv7-alpha-e552b54.gz
+ middle = fmt.Sprintf("-%s-%s%s-%s", runtime.GOOS, runtime.GOARCH, goarm, latestVersion)
+ } else if runtime.GOARCH == "arm64" {
+ //-linux-arm64-alpha-e552b54.gz
+ middle = fmt.Sprintf("-%s-%s-%s", runtime.GOOS, runtime.GOARCH, latestVersion)
+ } else if isMIPS(runtime.GOARCH) && gomips != "" {
+ middle = fmt.Sprintf("-%s-%s-%s-%s", runtime.GOOS, runtime.GOARCH, gomips, latestVersion)
} else {
- middle = fmt.Sprintf("-%s-%s-%s", goos, goarch, latestVersion)
+ middle = fmt.Sprintf("-%s-%s%s-%s", runtime.GOOS, runtime.GOARCH, amd64Compatible, latestVersion)
}
- if goos == "windows" {
+ if runtime.GOOS == "windows" {
middle += ".zip"
} else {
middle += ".gz"
@@ -462,3 +468,22 @@ func isMIPS(arch string) (ok bool) {
return false
}
}
+
+// linux only
+func probeGoARM() (ok bool) {
+ cmd := exec.Command("cat", "/proc/cpuinfo")
+ output, err := cmd.Output()
+ if err != nil {
+ log.Errorln("probe goarm error:%s", err)
+ return false
+ }
+ cpuInfo := string(output)
+ if strings.Contains(cpuInfo, "vfpv3") || strings.Contains(cpuInfo, "vfpv4") {
+ goarm = "v7"
+ } else if strings.Contains(cpuInfo, "vfp") {
+ goarm = "v6"
+ } else {
+ goarm = "v5"
+ }
+ return true
+}
diff --git a/listener/inner/tcp.go b/listener/inner/tcp.go
index a7e38588..9ba87e2f 100644
--- a/listener/inner/tcp.go
+++ b/listener/inner/tcp.go
@@ -1,9 +1,11 @@
package inner
import (
+ "errors"
+ "net"
+
"github.com/Dreamacro/clash/adapter/inbound"
C "github.com/Dreamacro/clash/constant"
- "net"
)
var tcpIn chan<- C.ConnContext
@@ -12,9 +14,13 @@ func New(in chan<- C.ConnContext) {
tcpIn = in
}
-func HandleTcp(dst string, host string) net.Conn {
+func HandleTcp(address string) (conn net.Conn, err error) {
+ if tcpIn == nil {
+ return nil, errors.New("tcp uninitialized")
+ }
+ // executor Parsed
conn1, conn2 := net.Pipe()
- context := inbound.NewInner(conn2, dst, host)
+ context := inbound.NewInner(conn2, address)
tcpIn <- context
- return conn1
+ return conn1, nil
}
diff --git a/listener/shadowsocks/tcp.go b/listener/shadowsocks/tcp.go
index 6884b960..febf87c3 100644
--- a/listener/shadowsocks/tcp.go
+++ b/listener/shadowsocks/tcp.go
@@ -103,7 +103,7 @@ func (l *Listener) HandleConn(conn net.Conn, in chan<- C.ConnContext, additions
conn = l.pickCipher.StreamConn(conn)
conn = N.NewDeadlineConn(conn) // embed ss can't handle readDeadline correctly
- target, err := socks5.ReadAddr(conn, make([]byte, socks5.MaxAddrLen))
+ target, err := socks5.ReadAddr0(conn)
if err != nil {
_ = conn.Close()
return
diff --git a/listener/shadowsocks/udp.go b/listener/shadowsocks/udp.go
index 3f058406..4efafa60 100644
--- a/listener/shadowsocks/udp.go
+++ b/listener/shadowsocks/udp.go
@@ -4,7 +4,7 @@ import (
"net"
"github.com/Dreamacro/clash/adapter/inbound"
- "github.com/Dreamacro/clash/common/pool"
+ N "github.com/Dreamacro/clash/common/net"
"github.com/Dreamacro/clash/common/sockopt"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/log"
@@ -29,19 +29,20 @@ func NewUDP(addr string, pickCipher core.Cipher, in chan<- C.PacketAdapter) (*UD
}
sl := &UDPListener{l, false}
- conn := pickCipher.PacketConn(l)
+ conn := pickCipher.PacketConn(N.NewEnhancePacketConn(l))
go func() {
for {
- buf := pool.Get(pool.RelayBufferSize)
- n, remoteAddr, err := conn.ReadFrom(buf)
+ data, put, remoteAddr, err := conn.WaitReadFrom()
if err != nil {
- pool.Put(buf)
+ if put != nil {
+ put()
+ }
if sl.closed {
break
}
continue
}
- handleSocksUDP(conn, in, buf[:n], remoteAddr)
+ handleSocksUDP(conn, in, data, put, remoteAddr)
}
}()
@@ -57,11 +58,13 @@ func (l *UDPListener) LocalAddr() net.Addr {
return l.packetConn.LocalAddr()
}
-func handleSocksUDP(pc net.PacketConn, in chan<- C.PacketAdapter, buf []byte, addr net.Addr) {
+func handleSocksUDP(pc net.PacketConn, in chan<- C.PacketAdapter, buf []byte, put func(), addr net.Addr) {
tgtAddr := socks5.SplitAddr(buf)
if tgtAddr == nil {
// Unresolved UDP packet, return buffer to the pool
- pool.Put(buf)
+ if put != nil {
+ put()
+ }
return
}
target := socks5.ParseAddr(tgtAddr.String())
@@ -71,7 +74,7 @@ func handleSocksUDP(pc net.PacketConn, in chan<- C.PacketAdapter, buf []byte, ad
pc: pc,
rAddr: addr,
payload: payload,
- bufRef: buf,
+ put: put,
}
select {
case in <- inbound.NewPacket(target, packet, C.SHADOWSOCKS):
diff --git a/listener/shadowsocks/utils.go b/listener/shadowsocks/utils.go
index 2e9fd003..c34c5cd0 100644
--- a/listener/shadowsocks/utils.go
+++ b/listener/shadowsocks/utils.go
@@ -6,7 +6,6 @@ import (
"net"
"net/url"
- "github.com/Dreamacro/clash/common/pool"
"github.com/Dreamacro/clash/transport/socks5"
)
@@ -14,7 +13,7 @@ type packet struct {
pc net.PacketConn
rAddr net.Addr
payload []byte
- bufRef []byte
+ put func()
}
func (c *packet) Data() []byte {
@@ -37,7 +36,9 @@ func (c *packet) LocalAddr() net.Addr {
}
func (c *packet) Drop() {
- pool.Put(c.bufRef)
+ if c.put != nil {
+ c.put()
+ }
}
func (c *packet) InAddr() net.Addr {
diff --git a/listener/sing/context.go b/listener/sing/context.go
index f7aed851..a500e4a4 100644
--- a/listener/sing/context.go
+++ b/listener/sing/context.go
@@ -2,8 +2,11 @@ package sing
import (
"context"
+ "golang.org/x/exp/slices"
"github.com/Dreamacro/clash/adapter/inbound"
+
+ "github.com/sagernet/sing/common/auth"
)
type contextKey string
@@ -22,3 +25,20 @@ func getAdditions(ctx context.Context) []inbound.Addition {
}
return nil
}
+
+func combineAdditions(ctx context.Context, additions []inbound.Addition) []inbound.Addition {
+ additionsCloned := false
+ if ctxAdditions := getAdditions(ctx); len(ctxAdditions) > 0 {
+ additions = slices.Clone(additions)
+ additionsCloned = true
+ additions = append(additions, ctxAdditions...)
+ }
+ if user, ok := auth.UserFromContext[string](ctx); ok {
+ if !additionsCloned {
+ additions = slices.Clone(additions)
+ additionsCloned = true
+ }
+ additions = append(additions, inbound.WithInUser(user))
+ }
+ return additions
+}
diff --git a/listener/sing/sing.go b/listener/sing/sing.go
index 2ccdfe2d..c60bbe67 100644
--- a/listener/sing/sing.go
+++ b/listener/sing/sing.go
@@ -3,7 +3,6 @@ package sing
import (
"context"
"errors"
- "golang.org/x/exp/slices"
"net"
"net/netip"
"sync"
@@ -18,6 +17,7 @@ import (
mux "github.com/sagernet/sing-mux"
vmess "github.com/sagernet/sing-vmess"
"github.com/sagernet/sing/common/buf"
+ "github.com/sagernet/sing/common/bufio"
"github.com/sagernet/sing/common/bufio/deadline"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
@@ -57,6 +57,14 @@ func (c *waitCloseConn) Upstream() any {
return c.ExtendedConn
}
+func (c *waitCloseConn) ReaderReplaceable() bool {
+ return true
+}
+
+func (c *waitCloseConn) WriterReplaceable() bool {
+ return true
+}
+
func UpstreamMetadata(metadata M.Metadata) M.Metadata {
return M.Metadata{
Source: metadata.Source,
@@ -65,11 +73,6 @@ func UpstreamMetadata(metadata M.Metadata) M.Metadata {
}
func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
- additions := h.Additions
- if ctxAdditions := getAdditions(ctx); len(ctxAdditions) > 0 {
- additions = slices.Clone(additions)
- additions = append(additions, ctxAdditions...)
- }
switch metadata.Destination.Fqdn {
case mux.Destination.Fqdn:
return mux.HandleConnection(ctx, h, log.SingLogger, conn, UpstreamMetadata(metadata))
@@ -94,15 +97,13 @@ func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, meta
if deadline.NeedAdditionalReadDeadline(conn) {
conn = N.NewDeadlineConn(conn) // conn from sing should check NeedAdditionalReadDeadline
}
- h.TcpIn <- inbound.NewSocket(target, &waitCloseConn{ExtendedConn: N.NewExtendedConn(conn), wg: wg, rAddr: metadata.Source.TCPAddr()}, h.Type, additions...)
+ h.TcpIn <- inbound.NewSocket(target, &waitCloseConn{ExtendedConn: N.NewExtendedConn(conn), wg: wg, rAddr: metadata.Source.TCPAddr()}, h.Type, combineAdditions(ctx, h.Additions)...)
return nil
}
func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.PacketConn, metadata M.Metadata) error {
- additions := h.Additions
- if ctxAdditions := getAdditions(ctx); len(ctxAdditions) > 0 {
- additions = slices.Clone(additions)
- additions = append(additions, ctxAdditions...)
+ if deadline.NeedAdditionalReadDeadline(conn) {
+ conn = deadline.NewFallbackPacketConn(bufio.NewNetPacketConn(conn)) // conn from sing should check NeedAdditionalReadDeadline
}
defer func() { _ = conn.Close() }()
mutex := sync.Mutex{}
@@ -112,11 +113,30 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.
defer mutex.Unlock()
conn2 = nil
}()
+ var buff *buf.Buffer
+ newBuffer := func() *buf.Buffer {
+ buff = buf.NewPacket() // do not use stack buffer
+ return buff
+ }
+ readWaiter, isReadWaiter := bufio.CreatePacketReadWaiter(conn)
+ if isReadWaiter {
+ readWaiter.InitializeReadWaiter(newBuffer)
+ }
for {
- buff := buf.NewPacket() // do not use stack buffer
- dest, err := conn.ReadPacket(buff)
+ var (
+ dest M.Socksaddr
+ err error
+ )
+ buff = nil // clear last loop status, avoid repeat release
+ if isReadWaiter {
+ dest, err = readWaiter.WaitReadPacket()
+ } else {
+ dest, err = conn.ReadPacket(newBuffer())
+ }
if err != nil {
- buff.Release()
+ if buff != nil {
+ buff.Release()
+ }
if ShouldIgnorePacketError(err) {
break
}
@@ -131,7 +151,7 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.
buff: buff,
}
select {
- case h.UdpIn <- inbound.NewPacket(target, packet, h.Type, additions...):
+ case h.UdpIn <- inbound.NewPacket(target, packet, h.Type, combineAdditions(ctx, h.Additions)...):
default:
}
}
diff --git a/listener/sing_shadowsocks/server.go b/listener/sing_shadowsocks/server.go
index c7e05bb5..1fa9a3ba 100644
--- a/listener/sing_shadowsocks/server.go
+++ b/listener/sing_shadowsocks/server.go
@@ -21,7 +21,7 @@ import (
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio"
- "github.com/sagernet/sing/common/metadata"
+ M "github.com/sagernet/sing/common/metadata"
)
type Listener struct {
@@ -92,19 +92,38 @@ func New(config LC.ShadowsocksServer, tcpIn chan<- C.ConnContext, udpIn chan<- C
go func() {
conn := bufio.NewPacketConn(ul)
+ var buff *buf.Buffer
+ newBuffer := func() *buf.Buffer {
+ buff = buf.NewPacket() // do not use stack buffer
+ return buff
+ }
+ readWaiter, isReadWaiter := bufio.CreatePacketReadWaiter(conn)
+ if isReadWaiter {
+ readWaiter.InitializeReadWaiter(newBuffer)
+ }
for {
- buff := buf.NewPacket()
- remoteAddr, err := conn.ReadPacket(buff)
+ var (
+ dest M.Socksaddr
+ err error
+ )
+ buff = nil // clear last loop status, avoid repeat release
+ if isReadWaiter {
+ dest, err = readWaiter.WaitReadPacket()
+ } else {
+ dest, err = conn.ReadPacket(newBuffer())
+ }
if err != nil {
- buff.Release()
+ if buff != nil {
+ buff.Release()
+ }
if sl.closed {
break
}
continue
}
- _ = sl.service.NewPacket(context.TODO(), conn, buff, metadata.Metadata{
+ _ = sl.service.NewPacket(context.TODO(), conn, buff, M.Metadata{
Protocol: "shadowsocks",
- Source: remoteAddr,
+ Source: dest,
})
}
}()
@@ -170,9 +189,9 @@ func (l *Listener) AddrList() (addrList []net.Addr) {
func (l *Listener) HandleConn(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Addition) {
ctx := sing.WithAdditions(context.TODO(), additions...)
- err := l.service.NewConnection(ctx, conn, metadata.Metadata{
+ err := l.service.NewConnection(ctx, conn, M.Metadata{
Protocol: "shadowsocks",
- Source: metadata.ParseSocksaddr(conn.RemoteAddr().String()),
+ Source: M.ParseSocksaddr(conn.RemoteAddr().String()),
})
if err != nil {
_ = conn.Close()
diff --git a/listener/sing_tun/dns.go b/listener/sing_tun/dns.go
index dc33be91..88e3f6d6 100644
--- a/listener/sing_tun/dns.go
+++ b/listener/sing_tun/dns.go
@@ -17,6 +17,7 @@ import (
D "github.com/miekg/dns"
"github.com/sagernet/sing/common/buf"
+ "github.com/sagernet/sing/common/bufio"
M "github.com/sagernet/sing/common/metadata"
"github.com/sagernet/sing/common/network"
)
@@ -108,20 +109,40 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.
defer mutex.Unlock()
conn2 = nil
}()
- for {
+
+ var buff *buf.Buffer
+ newBuffer := func() *buf.Buffer {
// safe size which is 1232 from https://dnsflagday.net/2020/.
// so 2048 is enough
- buff := buf.NewSize(2 * 1024)
+ buff = buf.NewSize(2 * 1024)
+ return buff
+ }
+ readWaiter, isReadWaiter := bufio.CreatePacketReadWaiter(conn)
+ if isReadWaiter {
+ readWaiter.InitializeReadWaiter(newBuffer)
+ }
+ for {
+ var (
+ dest M.Socksaddr
+ err error
+ )
_ = conn.SetReadDeadline(time.Now().Add(DefaultDnsReadTimeout))
- dest, err := conn.ReadPacket(buff)
+ buff = nil // clear last loop status, avoid repeat release
+ if isReadWaiter {
+ dest, err = readWaiter.WaitReadPacket()
+ } else {
+ dest, err = conn.ReadPacket(newBuffer())
+ }
if err != nil {
- buff.Release()
+ if buff != nil {
+ buff.Release()
+ }
if sing.ShouldIgnorePacketError(err) {
break
}
return err
}
- go func() {
+ go func(buff *buf.Buffer) {
ctx, cancel := context.WithTimeout(ctx, DefaultDnsRelayTimeout)
defer cancel()
inData := buff.Bytes()
@@ -146,7 +167,7 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.
if err != nil {
return
}
- }()
+ }(buff) // catch buff at goroutine create, avoid next loop change buff
}
return nil
}
diff --git a/listener/tproxy/packet.go b/listener/tproxy/packet.go
index 2a274f61..4967adc6 100644
--- a/listener/tproxy/packet.go
+++ b/listener/tproxy/packet.go
@@ -78,7 +78,7 @@ func createOrGetLocalConn(rAddr, lAddr net.Addr, in chan<- C.PacketAdapter, natT
}()
conn, err := listenLocalConn(rAddr, lAddr, in, natTable)
if err != nil {
- log.Errorln("listenLocalConn failed with error: %s, packet loss", err.Error())
+ log.Errorln("listenLocalConn failed with error: %s, packet loss (rAddr[%T]=%s lAddr[%T]=%s)", err.Error(), rAddr, remote, lAddr, local)
return nil, err
}
natTable.AddLocalConn(local, remote, conn)
diff --git a/rules/common/in_name.go b/rules/common/in_name.go
new file mode 100644
index 00000000..1e2abe15
--- /dev/null
+++ b/rules/common/in_name.go
@@ -0,0 +1,49 @@
+package common
+
+import (
+ "fmt"
+ C "github.com/Dreamacro/clash/constant"
+ "strings"
+)
+
+type InName struct {
+ *Base
+ names []string
+ adapter string
+ payload string
+}
+
+func (u *InName) Match(metadata *C.Metadata) (bool, string) {
+ for _, name := range u.names {
+ if metadata.InName == name {
+ return true, u.adapter
+ }
+ }
+ return false, ""
+}
+
+func (u *InName) RuleType() C.RuleType {
+ return C.InName
+}
+
+func (u *InName) Adapter() string {
+ return u.adapter
+}
+
+func (u *InName) Payload() string {
+ return u.payload
+}
+
+func NewInName(iNames, adapter string) (*InName, error) {
+ names := strings.Split(iNames, "/")
+ if len(names) == 0 {
+ return nil, fmt.Errorf("in name couldn't be empty")
+ }
+
+ return &InName{
+ Base: &Base{},
+ names: names,
+ adapter: adapter,
+ payload: iNames,
+ }, nil
+}
diff --git a/rules/common/in_type.go b/rules/common/in_type.go
index 520c9594..453045d8 100644
--- a/rules/common/in_type.go
+++ b/rules/common/in_type.go
@@ -23,7 +23,7 @@ func (u *InType) Match(metadata *C.Metadata) (bool, string) {
}
func (u *InType) RuleType() C.RuleType {
- return C.INTYPE
+ return C.InType
}
func (u *InType) Adapter() string {
@@ -37,7 +37,7 @@ func (u *InType) Payload() string {
func NewInType(iTypes, adapter string) (*InType, error) {
types := strings.Split(iTypes, "/")
if len(types) == 0 {
- return nil, fmt.Errorf("in type could be empty")
+ return nil, fmt.Errorf("in type couldn't be empty")
}
tps, err := parseInTypes(types)
diff --git a/rules/common/in_user.go b/rules/common/in_user.go
new file mode 100644
index 00000000..24f4b2e5
--- /dev/null
+++ b/rules/common/in_user.go
@@ -0,0 +1,49 @@
+package common
+
+import (
+ "fmt"
+ C "github.com/Dreamacro/clash/constant"
+ "strings"
+)
+
+type InUser struct {
+ *Base
+ users []string
+ adapter string
+ payload string
+}
+
+func (u *InUser) Match(metadata *C.Metadata) (bool, string) {
+ for _, user := range u.users {
+ if metadata.InUser == user {
+ return true, u.adapter
+ }
+ }
+ return false, ""
+}
+
+func (u *InUser) RuleType() C.RuleType {
+ return C.InUser
+}
+
+func (u *InUser) Adapter() string {
+ return u.adapter
+}
+
+func (u *InUser) Payload() string {
+ return u.payload
+}
+
+func NewInUser(iUsers, adapter string) (*InUser, error) {
+ users := strings.Split(iUsers, "/")
+ if len(users) == 0 {
+ return nil, fmt.Errorf("in user couldn't be empty")
+ }
+
+ return &InUser{
+ Base: &Base{},
+ users: users,
+ adapter: adapter,
+ payload: iUsers,
+ }, nil
+}
diff --git a/rules/parser.go b/rules/parser.go
index 1a336225..df790bc3 100644
--- a/rules/parser.go
+++ b/rules/parser.go
@@ -47,6 +47,10 @@ func ParseRule(tp, payload, target string, params []string, subRules map[string]
parsed, parseErr = RC.NewUid(payload, target)
case "IN-TYPE":
parsed, parseErr = RC.NewInType(payload, target)
+ case "IN-USER":
+ parsed, parseErr = RC.NewInUser(payload, target)
+ case "IN-NAME":
+ parsed, parseErr = RC.NewInName(payload, target)
case "SUB-RULE":
parsed, parseErr = logic.NewSubRule(payload, target, subRules, ParseRule)
case "AND":
diff --git a/test/go.mod b/test/go.mod
index 6cbf50e8..1327f84f 100644
--- a/test/go.mod
+++ b/test/go.mod
@@ -6,26 +6,34 @@ require (
github.com/Dreamacro/clash v0.0.0
github.com/docker/docker v20.10.21+incompatible
github.com/docker/go-connections v0.4.0
- github.com/miekg/dns v1.1.50
- github.com/stretchr/testify v1.8.1
- golang.org/x/net v0.2.1-0.20221117215542-ecf7fda6a59e
+ github.com/miekg/dns v1.1.53
+ github.com/stretchr/testify v1.8.2
+ golang.org/x/net v0.9.0
)
replace github.com/Dreamacro/clash => ../
require (
- github.com/Microsoft/go-winio v0.5.1 // indirect
+ github.com/3andne/restls-client-go v0.1.4 // indirect
+ github.com/Microsoft/go-winio v0.6.0 // indirect
+ github.com/RyuaNerin/go-krypto v1.0.2 // indirect
+ github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 // indirect
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
+ github.com/andybalholm/brotli v1.0.5 // indirect
github.com/cilium/ebpf v0.9.3 // indirect
github.com/coreos/go-iptables v0.6.0 // indirect
- github.com/database64128/tfo-go/v2 v2.0.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dlclark/regexp2 v1.7.0 // indirect
github.com/docker/distribution v2.8.1+incompatible // indirect
github.com/docker/go-units v0.4.0 // indirect
+ github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9 // indirect
+ github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 // indirect
+ github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1 // indirect
+ github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
+ github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
- github.com/gofrs/uuid v4.3.1+incompatible // indirect
+ github.com/gofrs/uuid/v5 v5.0.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/google/btree v1.0.1 // indirect
@@ -33,53 +41,74 @@ require (
github.com/google/gopacket v1.1.19 // indirect
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
- github.com/insomniacslk/dhcp v0.0.0-20221001123530-5308ebe5334c // indirect
- github.com/josharian/native v1.0.0 // indirect
+ github.com/hashicorp/yamux v0.1.1 // indirect
+ github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16 // indirect
+ github.com/josharian/native v1.1.0 // indirect
github.com/jpillora/backoff v1.0.0 // indirect
+ github.com/klauspost/compress v1.15.15 // indirect
github.com/klauspost/cpuid/v2 v2.0.12 // indirect
+ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 // indirect
- github.com/marten-seemann/qpack v0.3.0 // indirect
- github.com/marten-seemann/qtls-go1-18 v0.1.3 // indirect
- github.com/marten-seemann/qtls-go1-19 v0.1.1 // indirect
- github.com/mdlayher/netlink v1.7.0 // indirect
+ github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7 // indirect
github.com/mdlayher/socket v0.4.0 // indirect
- github.com/metacubex/quic-go v0.31.1-0.20221127023445-9f0ce65a734e // indirect
- github.com/metacubex/sing-shadowsocks v0.1.0 // indirect
- github.com/metacubex/sing-tun v0.1.0 // indirect
- github.com/metacubex/sing-wireguard v0.0.0-20221109114053-16c22adda03c // indirect
+ github.com/metacubex/gvisor v0.0.0-20230417114019-3c3ee672d60c // indirect
+ github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594 // indirect
+ github.com/metacubex/sing-shadowsocks v0.2.2-0.20230422111054-f54786eee8ba // indirect
+ github.com/metacubex/sing-tun v0.1.4 // indirect
+ github.com/metacubex/sing-wireguard v0.0.0-20230426030325-41db09ae771a // indirect
github.com/moby/term v0.0.0-20221105221325-4eb28fa6025c // indirect
github.com/morikuni/aec v1.0.0 // indirect
+ github.com/mroth/weightedrand/v2 v2.0.0 // indirect
+ github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // indirect
github.com/onsi/ginkgo/v2 v2.2.0 // indirect
+ github.com/openacid/low v0.1.21 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.2 // indirect
github.com/oschwald/geoip2-golang v1.8.0 // indirect
github.com/oschwald/maxminddb-golang v1.10.0 // indirect
+ github.com/pierrec/lz4/v4 v4.1.14 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
- github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e // indirect
+ github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
+ github.com/quic-go/qpack v0.4.0 // indirect
+ github.com/quic-go/qtls-go1-19 v0.2.1 // indirect
+ github.com/quic-go/qtls-go1-20 v0.1.1 // indirect
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
- github.com/sagernet/sing v0.1.0 // indirect
- github.com/sagernet/sing-vmess v0.1.0 // indirect
- github.com/sagernet/wireguard-go v0.0.0-20221108054404-7c2acadba17c // indirect
- github.com/samber/lo v1.35.0 // indirect
+ github.com/sagernet/sing v0.2.5-0.20230501044132-8365dd48a17a // indirect
+ github.com/sagernet/sing-mux v0.0.0-20230427141602-9836fc9b052e // indirect
+ github.com/sagernet/sing-shadowsocks2 v0.0.0-20230501032827-681c9c4ee0e9 // indirect
+ github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b // indirect
+ github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3 // indirect
+ github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 // indirect
+ github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 // indirect
+ github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 // indirect
+ github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77 // indirect
+ github.com/samber/lo v1.38.1 // indirect
+ github.com/shirou/gopsutil/v3 v3.23.4 // indirect
+ github.com/shoenig/go-m1cpu v0.1.5 // indirect
+ github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b // indirect
+ github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c // indirect
+ github.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e // indirect
github.com/sirupsen/logrus v1.9.0 // indirect
- github.com/u-root/uio v0.0.0-20210528114334-82958018845c // indirect
+ github.com/tklauser/go-sysconf v0.3.11 // indirect
+ github.com/tklauser/numcpus v0.6.0 // indirect
+ github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
github.com/xtls/go v0.0.0-20220914232946-0441cf4cf837 // indirect
+ github.com/yusufpapurcu/wmi v1.2.2 // indirect
+ github.com/zhangyunhao116/fastrand v0.3.0 // indirect
+ gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect
go.etcd.io/bbolt v1.3.6 // indirect
- go.uber.org/atomic v1.10.0 // indirect
- golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a // indirect
- golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9 // indirect
- golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
+ golang.org/x/crypto v0.8.0 // indirect
+ golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect
+ golang.org/x/mod v0.8.0 // indirect
golang.org/x/sync v0.1.0 // indirect
- golang.org/x/sys v0.2.1-0.20221110211117-d684c6f88669 // indirect
- golang.org/x/text v0.4.0 // indirect
- golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
- golang.org/x/tools v0.1.12 // indirect
- google.golang.org/protobuf v1.28.1 // indirect
+ golang.org/x/sys v0.7.0 // indirect
+ golang.org/x/text v0.9.0 // indirect
+ golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
+ golang.org/x/tools v0.6.0 // indirect
+ google.golang.org/protobuf v1.28.2-0.20230118093459-a9481185b34d // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
- gotest.tools/v3 v3.4.0 // indirect
- gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c // indirect
lukechampine.com/blake3 v1.1.7 // indirect
)
diff --git a/test/go.sum b/test/go.sum
index 22e24a16..b6f03871 100644
--- a/test/go.sum
+++ b/test/go.sum
@@ -1,8 +1,17 @@
+github.com/3andne/restls-client-go v0.1.4 h1:kLNC2aSRHPlEVYmTj6EOqJoorCpobEe2toMRSfBF7FU=
+github.com/3andne/restls-client-go v0.1.4/go.mod h1:04CGbRk1BwBiEDles8b5mlKgTqIwE5MqF7JDloJV47I=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
-github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY=
-github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
+github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg=
+github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE=
+github.com/RyuaNerin/go-krypto v1.0.2 h1:9KiZrrBs+tDrQ66dNy4nrX6SzntKtSKdm0wKHhdB4WM=
+github.com/RyuaNerin/go-krypto v1.0.2/go.mod h1:17LzMeJCgzGTkPH3TmfzRnEJ/yA7ErhTPp9sxIqONtA=
+github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 h1:cDVUiFo+npB0ZASqnw4q90ylaVAbnYyx0JYqK4YcGok=
+github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344/go.mod h1:9pIqrY6SXNL8vjRQE5Hd/OL5GyK/9MrGUWs87z/eFfk=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
+github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
+github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
+github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
@@ -10,8 +19,6 @@ github.com/cilium/ebpf v0.9.3 h1:5KtxXZU+scyERvkJMEm16TbScVvuuMrlhPly78ZMbSc=
github.com/cilium/ebpf v0.9.3/go.mod h1:w27N4UjpaQ9X/DGrSugxUG+H+NhgntDuPb5lCzxCn8A=
github.com/coreos/go-iptables v0.6.0 h1:is9qnZMPYjLd8LYqmm/qlE+wwEgJIkTYdhV3rfZo4jk=
github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
-github.com/database64128/tfo-go/v2 v2.0.2 h1:5rGgkJeLEKlNaqredfrPQNLnctn1b+1fq/8tdKdOzJg=
-github.com/database64128/tfo-go/v2 v2.0.2/go.mod h1:FDdt4JaAsRU66wsYHxSVytYimPkKIHupVsxM+5DhvjY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -25,94 +32,100 @@ github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKoh
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
-github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc=
+github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9 h1:/5RkVc9Rc81XmMyVqawCiDyrBHZbLAZgTTCqou4mwj8=
+github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9/go.mod h1:hkIFzoiIPZYxdFOOLyDho59b7SrDfo+w3h+yWdlg45I=
+github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 h1:8j2RH289RJplhA6WfdaPqzg1MjH2K8wX5e0uhAxrw2g=
+github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391/go.mod h1:K2R7GhgxrlJzHw2qiPWsCZXf/kXEJN9PLnQK73Ll0po=
+github.com/ericlagergren/saferand v0.0.0-20220206064634-960a4dd2bc5c h1:RUzBDdZ+e/HEe2Nh8lYsduiPAZygUfVXJn0Ncj5sHMg=
+github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1 h1:tlDMEdcPRQKBEz5nGDMvswiajqh7k8ogWRlhRwKy5mY=
+github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1/go.mod h1:4RfsapbGx2j/vU5xC/5/9qB3kn9Awp1YDiEnN43QrJ4=
+github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010 h1:fuGucgPk5dN6wzfnxl3D0D3rVLw4v2SbBT9jb4VnxzA=
+github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010/go.mod h1:JtBcj7sBuTTRupn7c2bFspMDIObMJsVK8TeUvpShPok=
github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
+github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
+github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
-github.com/gofrs/uuid v4.3.1+incompatible h1:0/KbAdpx3UXAx1kEOWHJeOkpbgRFGHVgv+CFIY7dBJI=
-github.com/gofrs/uuid v4.3.1+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
+github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M=
+github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
-github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
-github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/google/tink/go v1.6.1 h1:t7JHqO8Ath2w2ig5vjwQYJzhGEZymedQc90lQXUBa4I=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
-github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis=
+github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
+github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
-github.com/insomniacslk/dhcp v0.0.0-20221001123530-5308ebe5334c h1:OCFM4+DXTWfNlyeoddrTwdup/ztkGSyAMR2UGcPckNQ=
-github.com/insomniacslk/dhcp v0.0.0-20221001123530-5308ebe5334c/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E=
-github.com/josharian/native v1.0.0 h1:Ts/E8zCSEsG17dUqv7joXJFybuMLjQfWE04tsBODTxk=
-github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
+github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16 h1:+aAGyK41KRn8jbF2Q7PLL0Sxwg6dShGcQSeCC7nZQ8E=
+github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16/go.mod h1:IKrnDWs3/Mqq5n0lI+RxA2sB7MvN/vbMBP3ehXg65UI=
+github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
+github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
+github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
-github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=
-github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ=
-github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok=
-github.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg=
-github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw=
+github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.0.12 h1:p9dKCg8i4gmOxtv35DvrYoWqYzQrvEVdjQ762Y0OqZE=
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
+github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc=
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg=
-github.com/marten-seemann/qpack v0.3.0 h1:UiWstOgT8+znlkDPOg2+3rIuYXJ2CnGDkGUXN6ki6hE=
-github.com/marten-seemann/qpack v0.3.0/go.mod h1:cGfKPBiP4a9EQdxCwEwI/GEeWAsjSekBvx/X8mh58+g=
-github.com/marten-seemann/qtls-go1-18 v0.1.3 h1:R4H2Ks8P6pAtUagjFty2p7BVHn3XiwDAl7TTQf5h7TI=
-github.com/marten-seemann/qtls-go1-18 v0.1.3/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4=
-github.com/marten-seemann/qtls-go1-19 v0.1.1 h1:mnbxeq3oEyQxQXwI4ReCgW9DPoPR94sNlqWoDZnjRIE=
-github.com/marten-seemann/qtls-go1-19 v0.1.1/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI=
-github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y=
-github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=
-github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=
-github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY=
-github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o=
-github.com/mdlayher/netlink v1.7.0 h1:ZNGI4V7i1fJ94DPYtWhI/R85i/Q7ZxnuhUJQcJMoodI=
-github.com/mdlayher/netlink v1.7.0/go.mod h1:nKO5CSjE/DJjVhk/TNp6vCE1ktVxEA8VEh8drhZzxsQ=
-github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
-github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
+github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7 h1:HSkXG1bE/qcRuuPlZ2Jyf0Od8HLxOowi7CzKQqNtWn4=
+github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7/go.mod h1:1ztDZHGbU5MjN5lNZpkpG8ygndjjWzcojp/H7r6l6QQ=
github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw=
github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc=
-github.com/metacubex/quic-go v0.31.1-0.20221127023445-9f0ce65a734e h1:RnfC6+sShJ3biU2Q2wuh4FxZ8/3fp1QG+1zAfswVehA=
-github.com/metacubex/quic-go v0.31.1-0.20221127023445-9f0ce65a734e/go.mod h1:7NPWVTLiX2Ss9q9gBNZaNHsPqZ3Tg/ApyrXxxUYbl78=
-github.com/metacubex/sing-shadowsocks v0.1.0 h1:uGBtNkpy4QFlofaNkJf+iFegeLU11VzTUlkC46FHF8A=
-github.com/metacubex/sing-shadowsocks v0.1.0/go.mod h1:8pBSYDKVxTtqUtGZyEh4ZpFJXwP6wBVVKrs6oQiOwmQ=
-github.com/metacubex/sing-tun v0.1.0 h1:iQj0+0WjJynSKAtfv87wOZlVKWl3w9RvkOSkVe9zuMg=
-github.com/metacubex/sing-tun v0.1.0/go.mod h1:l4JyI6RTrlHLQz5vSakg+wxA+LwGVI0Mz5ZtlOv67dA=
-github.com/metacubex/sing-wireguard v0.0.0-20221109114053-16c22adda03c h1:VHtXDny/TNOF7YDT9d9Qkr+x6K1O4cejXLlyPUXDeXQ=
-github.com/metacubex/sing-wireguard v0.0.0-20221109114053-16c22adda03c/go.mod h1:fULJ451x1/XlpIhl+Oo+EPGKla9tFZaqT5dKLrZ+NvM=
-github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
-github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
+github.com/metacubex/gvisor v0.0.0-20230417114019-3c3ee672d60c h1:D62872jiuzC6b+3aI8tqfeyc6YgbfarYKywTnnvXwEM=
+github.com/metacubex/gvisor v0.0.0-20230417114019-3c3ee672d60c/go.mod h1:wqEuzdImyqD2MCGE8CYRJXbB77oSEJeoSSXXdwKjnsE=
+github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594 h1:KD96JPdTIayTGGgRl6PuVqo2Bpo6+x3LqDDyqrYDDXw=
+github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594/go.mod h1:9nOiGX6kqV3+ZbkDKdTNzdFD726QQHPH6WDb36jUSpA=
+github.com/metacubex/sing-shadowsocks v0.2.2-0.20230422111054-f54786eee8ba h1:He8YwyK600lHAS1xxNsP4k/jnZ8zqQ34XjCGn925+Yk=
+github.com/metacubex/sing-shadowsocks v0.2.2-0.20230422111054-f54786eee8ba/go.mod h1:4uQQReKMTU7KTfOykVBe/oGJ00pl38d+BYJ99+mx26s=
+github.com/metacubex/sing-tun v0.1.4 h1:OQDBNHjuPKrOprCiK+sLt97YQ0K6b9ZWmJB6z51ibZQ=
+github.com/metacubex/sing-tun v0.1.4/go.mod h1:BMfG00enVf90/CzcdX9PK3Dymgl7BZqHXJfexEyB7Cc=
+github.com/metacubex/sing-wireguard v0.0.0-20230426030325-41db09ae771a h1:cWKym33Qvl6HA3hj4/YuYD8hHyqQPb47wT5cJRAPgco=
+github.com/metacubex/sing-wireguard v0.0.0-20230426030325-41db09ae771a/go.mod h1:Bsw2BvKMMMY0FhZPseDI50ZOalvoUPMKYyGpyqvIIqY=
+github.com/miekg/dns v1.1.53 h1:ZBkuHr5dxHtB1caEOlZTLPo7D3L3TWckgUUs/RHfDxw=
+github.com/miekg/dns v1.1.53/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
github.com/moby/term v0.0.0-20221105221325-4eb28fa6025c h1:RC8WMpjonrBfyAh6VN/POIPtYD5tRAq0qMqCRjQNK+g=
github.com/moby/term v0.0.0-20221105221325-4eb28fa6025c/go.mod h1:9OcmHNQQUTbk4XCffrLgN1NEKc2mh5u++biHVrvHsSU=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
+github.com/mroth/weightedrand/v2 v2.0.0 h1:ADehnByWbliEDIazDAKFdBHoqgHSXAkgyKqM/9YsPoo=
+github.com/mroth/weightedrand/v2 v2.0.0/go.mod h1:f2faGsfOGOwc1p94wzHKKZyTpcJUW7OJ/9U4yfiNAOU=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
+github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 h1:1102pQc2SEPp5+xrS26wEaeb26sZy6k9/ZXlZN+eXE4=
+github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7/go.mod h1:UqoUn6cHESlliMhOnKLWr+CBH+e3bazUPvFj1XZwAjs=
github.com/onsi/ginkgo/v2 v2.2.0 h1:3ZNA3L1c5FYDFTTxbFeVGGD8jYvjYauHD30YgLxVsNI=
github.com/onsi/ginkgo/v2 v2.2.0/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk=
github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q=
+github.com/openacid/errors v0.8.1/go.mod h1:GUQEJJOJE3W9skHm8E8Y4phdl2LLEN8iD7c5gcGgdx0=
+github.com/openacid/low v0.1.21 h1:Tr2GNu4N/+rGRYdOsEHOE89cxUIaDViZbVmKz29uKGo=
+github.com/openacid/low v0.1.21/go.mod h1:q+MsKI6Pz2xsCkzV4BLj7NR5M4EX0sGz5AqotpZDVh0=
+github.com/openacid/must v0.1.3/go.mod h1:luPiXCuJlEo3UUFQngVQokV0MPGryeYvtCbQPs3U1+I=
+github.com/openacid/testkeys v0.1.6/go.mod h1:MfA7cACzBpbiwekivj8StqX0WIRmqlMsci1c37CA3Do=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
@@ -121,45 +134,78 @@ github.com/oschwald/geoip2-golang v1.8.0 h1:KfjYB8ojCEn/QLqsDU0AzrJ3R5Qa9vFlx3z6
github.com/oschwald/geoip2-golang v1.8.0/go.mod h1:R7bRvYjOeaoenAp9sKRS8GX5bJWcZ0laWO5+DauEktw=
github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg=
github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0=
+github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
+github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
+github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
+github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
+github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
+github.com/quic-go/qtls-go1-19 v0.2.1 h1:aJcKNMkH5ASEJB9FXNeZCyTEIHU1J7MmHyz1Q1TSG1A=
+github.com/quic-go/qtls-go1-19 v0.2.1/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
+github.com/quic-go/qtls-go1-20 v0.1.1 h1:KbChDlg82d3IHqaj2bn6GfKRj84Per2VGf5XV3wSwQk=
+github.com/quic-go/qtls-go1-20 v0.1.1/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
-github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e h1:5CFRo8FJbCuf5s/eTBdZpmMbn8Fe2eSMLNAYfKanA34=
-github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e/go.mod h1:qbt0dWObotCfcjAJJ9AxtFPNSDUfZF+6dCpgKEOBn/g=
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms=
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
-github.com/sagernet/sing v0.1.0 h1:FGmaP2BVPYO2IyC/3R1DaQa/zr+kOKHRgWqrmOF+Gu8=
-github.com/sagernet/sing v0.1.0/go.mod h1:zvgDYKI+vCAW9RyfyrKTgleI+DOa8lzHMPC7VZo3OL4=
-github.com/sagernet/sing-vmess v0.1.0 h1:x0tYBJRbVi7zVXpMEW45eApGpXIDs9ub3raglouAKMo=
-github.com/sagernet/sing-vmess v0.1.0/go.mod h1:4lwj6EHrUlgRnKhbmtboGbt+wtl5+tHMv96Ez8LZArw=
-github.com/sagernet/wireguard-go v0.0.0-20221108054404-7c2acadba17c h1:qP3ZOHnjZalvqbjundbXiv/YrNlo3HOgrKc+S1QGs0U=
-github.com/sagernet/wireguard-go v0.0.0-20221108054404-7c2acadba17c/go.mod h1:euOmN6O5kk9dQmgSS8Df4psAl3TCjxOz0NW60EWkSaI=
-github.com/samber/lo v1.35.0 h1:GlT8CV1GE+v97Y7MLF1wXvX6mjoxZ+hi61tj/ZcQwY0=
-github.com/samber/lo v1.35.0/go.mod h1:HLeWcJRRyLKp3+/XBJvOrerCQn9mhdKMHyd7IRlgeQ8=
-github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
+github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
+github.com/sagernet/sing v0.2.5-0.20230501044132-8365dd48a17a h1:s2kkd/eR3mWGkYioknxhgQzG8uft4VRx9skhqxxeyVQ=
+github.com/sagernet/sing v0.2.5-0.20230501044132-8365dd48a17a/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w=
+github.com/sagernet/sing-mux v0.0.0-20230427141602-9836fc9b052e h1:t8nuY9plpHEzlnPxOpuv64jhjz3teIvccu3YMFX4fJI=
+github.com/sagernet/sing-mux v0.0.0-20230427141602-9836fc9b052e/go.mod h1:pF+RnLvCAOhECrvauy6LYOpBakJ/vuaF1Wm4lPsWryI=
+github.com/sagernet/sing-shadowsocks2 v0.0.0-20230501032827-681c9c4ee0e9 h1:0Dc1t9ao9EyvRil6l/950PLwND1qO1rgnxwbcctE8KE=
+github.com/sagernet/sing-shadowsocks2 v0.0.0-20230501032827-681c9c4ee0e9/go.mod h1:Dpib342FFR68SZ3CSRYxk/zWbanAqRBrCxoLuda5I0A=
+github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b h1:ouW/6IDCrxkBe19YSbdCd7buHix7b+UZ6BM4Zz74XF4=
+github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b/go.mod h1:oG8bPerYI6cZ74KquY3DvA7ynECyrILPBnce6wtBqeI=
+github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3 h1:BHOnxrbC929JonuKqFdJ7ZbDp7zs4oTlH5KFvKtWu9U=
+github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3/go.mod h1:yKrAr+dqZd64DxBXCHWrYicp+n4qbqO73mtwv3dck8U=
+github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as=
+github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37/go.mod h1:3skNSftZDJWTGVtVaM2jfbce8qHnmH/AGDRe62iNOg0=
+github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 h1:2ItpW1nMNkPzmBTxV0/eClCklHrFSQMnUGcpUmJxVeE=
+github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9/go.mod h1:FUyTEc5ye5NjKnDTDMuiLF2M6T4BE6y6KZuax//UCEg=
+github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 h1:kDUqhc9Vsk5HJuhfIATJ8oQwBmpOZJuozQG7Vk88lL4=
+github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2/go.mod h1:JKQMZq/O2qnZjdrt+B57olmfgEmLtY9iiSIEYtWvoSM=
+github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77 h1:g6QtRWQ2dKX7EQP++1JLNtw4C2TNxd4/ov8YUpOPOSo=
+github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77/go.mod h1:pJDdXzZIwJ+2vmnT0TKzmf8meeum+e2mTDSehw79eE0=
+github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM=
+github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
+github.com/shirou/gopsutil/v3 v3.23.4 h1:hZwmDxZs7Ewt75DV81r4pFMqbq+di2cbt9FsQBqLD2o=
+github.com/shirou/gopsutil/v3 v3.23.4/go.mod h1:ZcGxyfzAMRevhUR2+cfhXDH6gQdFYE/t8j1nsU4mPI8=
+github.com/shoenig/go-m1cpu v0.1.5 h1:LF57Z/Fpb/WdGLjt2HZilNnmZOxg/q2bSKTQhgbrLrQ=
+github.com/shoenig/go-m1cpu v0.1.5/go.mod h1:Wwvst4LR89UxjeFtLRMrpgRiyY4xPsejnVZym39dbAQ=
+github.com/shoenig/test v0.6.3 h1:GVXWJFk9PiOjN0KoJ7VrJGH6uLPnqxR7/fe3HUPfE0c=
+github.com/shoenig/test v0.6.3/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
+github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b h1:rXHg9GrUEtWZhEkrykicdND3VPjlVbYiLdX9J7gimS8=
+github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b/go.mod h1:X7qrxNQViEaAN9LNZOPl9PfvQtp3V3c7LTo0dvGi0fM=
+github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c h1:DjKMC30y6yjG3IxDaeAj3PCoRr+IsO+bzyT+Se2m2Hk=
+github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c/go.mod h1:NV/a66PhhWYVmUMaotlXJ8fIEFB98u+c8l/CQIEFLrU=
+github.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e h1:ur8uMsPIFG3i4Gi093BQITvwH9znsz2VUZmnmwHvpIo=
+github.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e/go.mod h1:+e5fBW3bpPyo+3uLo513gIUblc03egGjMM0+5GKbzK8=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
-github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
-github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
-github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
-github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
-github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
-github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M=
-github.com/u-root/uio v0.0.0-20210528114334-82958018845c h1:BFvcl34IGnw8yvJi8hlqLFo9EshRInwWBs2M5fGWzQA=
-github.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA=
+github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
+github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM=
+github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI=
+github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms=
+github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4=
+github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA=
+github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/xtls/go v0.0.0-20220914232946-0441cf4cf837 h1:AHhUwwFJGl27E46OpdJHplZkK09m7aETNBNzhT6t15M=
@@ -167,40 +213,35 @@ github.com/xtls/go v0.0.0-20220914232946-0441cf4cf837/go.mod h1:YJTRELIWrGxR1s8x
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
+github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
+github.com/zhangyunhao116/fastrand v0.3.0 h1:7bwe124xcckPulX6fxtr2lFdO2KQqaefdtbk+mqO/Ig=
+github.com/zhangyunhao116/fastrand v0.3.0/go.mod h1:0v5KgHho0VE6HU192HnY15de/oDS8UrbBChIFjIhBtc=
+gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec h1:FpfFs4EhNehiVfzQttTuxanPIT43FtkkCFypIod8LHo=
+gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec/go.mod h1:BZ1RAoRPbCxum9Grlv5aeksu2H8BiKehBYooU2LFiOQ=
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
-go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
-go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a h1:diz9pEYuTIuLMJLs3rGDkeaTsNyRs6duYdFyPAxzE/U=
-golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
-golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9 h1:RjggHMcaTVp0LOVZcW0bo8alwHrOaCrGUDgfWUHhnN4=
-golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
+golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
+golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
+golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug=
+golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/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=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
-golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
-golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
+golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
-golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.2.1-0.20221117215542-ecf7fda6a59e h1:IVOjWZQH/57UDcpX19vSmMz8w3ohroOMWohn8qWpRkg=
-golang.org/x/net v0.2.1-0.20221117215542-ecf7fda6a59e/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
+golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
+golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -208,60 +249,47 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.2.1-0.20221110211117-d684c6f88669 h1:pvmSpBoSG0gD2LLPAX15QHPig8xsbU0tu1sSAmResqk=
-golang.org/x/sys v0.2.1-0.20221110211117-d684c6f88669/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
+golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
-golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
-golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
+golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
+golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44=
+golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
-golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
+golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
-google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
-google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+google.golang.org/protobuf v1.28.2-0.20230118093459-a9481185b34d h1:qp0AnQCvRCMlu9jBjtdbTaaEmThIgZOrbVyDEOcmKhQ=
+google.golang.org/protobuf v1.28.2-0.20230118093459-a9481185b34d/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@@ -269,8 +297,5 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
-gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g=
-gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c h1:m5lcgWnL3OElQNVyp3qcncItJ2c0sQlSGjYK2+nJTA4=
-gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM=
lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0=
lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA=
diff --git a/transport/gun/gun.go b/transport/gun/gun.go
index 36cf68f8..e98f7fb5 100644
--- a/transport/gun/gun.go
+++ b/transport/gun/gun.go
@@ -199,9 +199,12 @@ func NewHTTP2Client(dialFn DialFn, tlsConfig *tls.Config, Fingerprint string, re
if err != nil {
return nil, err
}
-
wrap.remoteAddr = pconn.RemoteAddr()
+ if tlsConfig == nil {
+ return pconn, nil
+ }
+
if len(Fingerprint) != 0 {
if realityConfig == nil {
if fingerprint, exists := tlsC.GetFingerprint(Fingerprint); exists {
diff --git a/transport/shadowsocks/core/cipher.go b/transport/shadowsocks/core/cipher.go
index 7f4f7f71..cd30360c 100644
--- a/transport/shadowsocks/core/cipher.go
+++ b/transport/shadowsocks/core/cipher.go
@@ -7,6 +7,7 @@ import (
"sort"
"strings"
+ N "github.com/Dreamacro/clash/common/net"
"github.com/Dreamacro/clash/transport/shadowsocks/shadowaead"
"github.com/Dreamacro/clash/transport/shadowsocks/shadowstream"
)
@@ -21,7 +22,7 @@ type StreamConnCipher interface {
}
type PacketConnCipher interface {
- PacketConn(net.PacketConn) net.PacketConn
+ PacketConn(N.EnhancePacketConn) N.EnhancePacketConn
}
// ErrCipherNotSupported occurs when a cipher is not supported (likely because of security concerns).
@@ -128,7 +129,7 @@ type AeadCipher struct {
}
func (aead *AeadCipher) StreamConn(c net.Conn) net.Conn { return shadowaead.NewConn(c, aead) }
-func (aead *AeadCipher) PacketConn(c net.PacketConn) net.PacketConn {
+func (aead *AeadCipher) PacketConn(c N.EnhancePacketConn) N.EnhancePacketConn {
return shadowaead.NewPacketConn(c, aead)
}
@@ -139,7 +140,7 @@ type StreamCipher struct {
}
func (ciph *StreamCipher) StreamConn(c net.Conn) net.Conn { return shadowstream.NewConn(c, ciph) }
-func (ciph *StreamCipher) PacketConn(c net.PacketConn) net.PacketConn {
+func (ciph *StreamCipher) PacketConn(c N.EnhancePacketConn) N.EnhancePacketConn {
return shadowstream.NewPacketConn(c, ciph)
}
@@ -147,8 +148,8 @@ func (ciph *StreamCipher) PacketConn(c net.PacketConn) net.PacketConn {
type dummy struct{}
-func (dummy) StreamConn(c net.Conn) net.Conn { return c }
-func (dummy) PacketConn(c net.PacketConn) net.PacketConn { return c }
+func (dummy) StreamConn(c net.Conn) net.Conn { return c }
+func (dummy) PacketConn(c N.EnhancePacketConn) N.EnhancePacketConn { return c }
// key-derivation function from original Shadowsocks
func Kdf(password string, keyLen int) []byte {
diff --git a/transport/shadowsocks/shadowaead/packet.go b/transport/shadowsocks/shadowaead/packet.go
index 7043ead7..e84ac570 100644
--- a/transport/shadowsocks/shadowaead/packet.go
+++ b/transport/shadowsocks/shadowaead/packet.go
@@ -6,6 +6,7 @@ import (
"io"
"net"
+ N "github.com/Dreamacro/clash/common/net"
"github.com/Dreamacro/clash/common/pool"
)
@@ -57,15 +58,15 @@ func Unpack(dst, pkt []byte, ciph Cipher) ([]byte, error) {
}
type PacketConn struct {
- net.PacketConn
+ N.EnhancePacketConn
Cipher
}
const maxPacketSize = 64 * 1024
-// NewPacketConn wraps a net.PacketConn with cipher
-func NewPacketConn(c net.PacketConn, ciph Cipher) *PacketConn {
- return &PacketConn{PacketConn: c, Cipher: ciph}
+// NewPacketConn wraps an N.EnhancePacketConn with cipher
+func NewPacketConn(c N.EnhancePacketConn, ciph Cipher) *PacketConn {
+ return &PacketConn{EnhancePacketConn: c, Cipher: ciph}
}
// WriteTo encrypts b and write to addr using the embedded PacketConn.
@@ -76,13 +77,13 @@ func (c *PacketConn) WriteTo(b []byte, addr net.Addr) (int, error) {
if err != nil {
return 0, err
}
- _, err = c.PacketConn.WriteTo(buf, addr)
+ _, err = c.EnhancePacketConn.WriteTo(buf, addr)
return len(b), err
}
// ReadFrom reads from the embedded PacketConn and decrypts into b.
func (c *PacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
- n, addr, err := c.PacketConn.ReadFrom(b)
+ n, addr, err := c.EnhancePacketConn.ReadFrom(b)
if err != nil {
return n, addr, err
}
@@ -93,3 +94,20 @@ func (c *PacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
copy(b, bb)
return len(bb), addr, err
}
+
+func (c *PacketConn) WaitReadFrom() (data []byte, put func(), addr net.Addr, err error) {
+ data, put, addr, err = c.EnhancePacketConn.WaitReadFrom()
+ if err != nil {
+ return
+ }
+ data, err = Unpack(data[c.Cipher.SaltSize():], data, c)
+ if err != nil {
+ if put != nil {
+ put()
+ }
+ data = nil
+ put = nil
+ return
+ }
+ return
+}
diff --git a/transport/shadowsocks/shadowstream/packet.go b/transport/shadowsocks/shadowstream/packet.go
index 0b46dea1..f0bf43ef 100644
--- a/transport/shadowsocks/shadowstream/packet.go
+++ b/transport/shadowsocks/shadowstream/packet.go
@@ -6,6 +6,7 @@ import (
"io"
"net"
+ N "github.com/Dreamacro/clash/common/net"
"github.com/Dreamacro/clash/common/pool"
)
@@ -43,13 +44,13 @@ func Unpack(dst, pkt []byte, s Cipher) ([]byte, error) {
}
type PacketConn struct {
- net.PacketConn
+ N.EnhancePacketConn
Cipher
}
-// NewPacketConn wraps a net.PacketConn with stream cipher encryption/decryption.
-func NewPacketConn(c net.PacketConn, ciph Cipher) *PacketConn {
- return &PacketConn{PacketConn: c, Cipher: ciph}
+// NewPacketConn wraps an N.EnhancePacketConn with stream cipher encryption/decryption.
+func NewPacketConn(c N.EnhancePacketConn, ciph Cipher) *PacketConn {
+ return &PacketConn{EnhancePacketConn: c, Cipher: ciph}
}
const maxPacketSize = 64 * 1024
@@ -61,12 +62,12 @@ func (c *PacketConn) WriteTo(b []byte, addr net.Addr) (int, error) {
if err != nil {
return 0, err
}
- _, err = c.PacketConn.WriteTo(buf, addr)
+ _, err = c.EnhancePacketConn.WriteTo(buf, addr)
return len(b), err
}
func (c *PacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
- n, addr, err := c.PacketConn.ReadFrom(b)
+ n, addr, err := c.EnhancePacketConn.ReadFrom(b)
if err != nil {
return n, addr, err
}
@@ -77,3 +78,20 @@ func (c *PacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
copy(b, bb)
return len(bb), addr, err
}
+
+func (c *PacketConn) WaitReadFrom() (data []byte, put func(), addr net.Addr, err error) {
+ data, put, addr, err = c.EnhancePacketConn.WaitReadFrom()
+ if err != nil {
+ return
+ }
+ data, err = Unpack(data[c.IVSize():], data, c)
+ if err != nil {
+ if put != nil {
+ put()
+ }
+ data = nil
+ put = nil
+ return
+ }
+ return
+}
diff --git a/transport/socks5/socks5.go b/transport/socks5/socks5.go
index 7d4f11ae..6f95cce9 100644
--- a/transport/socks5/socks5.go
+++ b/transport/socks5/socks5.go
@@ -299,6 +299,50 @@ func ReadAddr(r io.Reader, b []byte) (Addr, error) {
return nil, ErrAddressNotSupported
}
+func ReadAddr0(r io.Reader) (Addr, error) {
+ aType, err := ReadByte(r) // read 1st byte for address type
+ if err != nil {
+ return nil, err
+ }
+
+ switch aType {
+ case AtypDomainName:
+ var domainLength byte
+ domainLength, err = ReadByte(r) // read 2nd byte for domain length
+ if err != nil {
+ return nil, err
+ }
+ b := make([]byte, 1+1+uint16(domainLength)+2)
+ _, err = io.ReadFull(r, b[2:])
+ b[0] = aType
+ b[1] = domainLength
+ return b, err
+ case AtypIPv4:
+ var b [1 + net.IPv4len + 2]byte
+ _, err = io.ReadFull(r, b[1:])
+ b[0] = aType
+ return b[:], err
+ case AtypIPv6:
+ var b [1 + net.IPv6len + 2]byte
+ _, err = io.ReadFull(r, b[1:])
+ b[0] = aType
+ return b[:], err
+ }
+
+ return nil, ErrAddressNotSupported
+}
+
+func ReadByte(reader io.Reader) (byte, error) {
+ if br, isBr := reader.(io.ByteReader); isBr {
+ return br.ReadByte()
+ }
+ var b [1]byte
+ if _, err := io.ReadFull(reader, b[:]); err != nil {
+ return 0, err
+ }
+ return b[0], nil
+}
+
// SplitAddr slices a SOCKS address from beginning of b. Returns nil if failed.
func SplitAddr(b []byte) Addr {
addrLen := 1
diff --git a/transport/ssr/protocol/auth_aes128_sha1.go b/transport/ssr/protocol/auth_aes128_sha1.go
index 4de48151..e2f0e143 100644
--- a/transport/ssr/protocol/auth_aes128_sha1.go
+++ b/transport/ssr/protocol/auth_aes128_sha1.go
@@ -8,6 +8,7 @@ import (
"strconv"
"strings"
+ N "github.com/Dreamacro/clash/common/net"
"github.com/Dreamacro/clash/common/pool"
"github.com/Dreamacro/clash/log"
"github.com/Dreamacro/clash/transport/ssr/tools"
@@ -82,13 +83,13 @@ func (a *authAES128) StreamConn(c net.Conn, iv []byte) net.Conn {
return &Conn{Conn: c, Protocol: p}
}
-func (a *authAES128) PacketConn(c net.PacketConn) net.PacketConn {
+func (a *authAES128) PacketConn(c N.EnhancePacketConn) N.EnhancePacketConn {
p := &authAES128{
Base: a.Base,
authAES128Function: a.authAES128Function,
userData: a.userData,
}
- return &PacketConn{PacketConn: c, Protocol: p}
+ return &PacketConn{EnhancePacketConn: c, Protocol: p}
}
func (a *authAES128) Decode(dst, src *bytes.Buffer) error {
diff --git a/transport/ssr/protocol/auth_chain_a.go b/transport/ssr/protocol/auth_chain_a.go
index 6b12ab9b..23efb390 100644
--- a/transport/ssr/protocol/auth_chain_a.go
+++ b/transport/ssr/protocol/auth_chain_a.go
@@ -11,6 +11,7 @@ import (
"strconv"
"strings"
+ N "github.com/Dreamacro/clash/common/net"
"github.com/Dreamacro/clash/common/pool"
"github.com/Dreamacro/clash/log"
"github.com/Dreamacro/clash/transport/shadowsocks/core"
@@ -83,13 +84,13 @@ func (a *authChainA) StreamConn(c net.Conn, iv []byte) net.Conn {
return &Conn{Conn: c, Protocol: p}
}
-func (a *authChainA) PacketConn(c net.PacketConn) net.PacketConn {
+func (a *authChainA) PacketConn(c N.EnhancePacketConn) N.EnhancePacketConn {
p := &authChainA{
Base: a.Base,
salt: a.salt,
userData: a.userData,
}
- return &PacketConn{PacketConn: c, Protocol: p}
+ return &PacketConn{EnhancePacketConn: c, Protocol: p}
}
func (a *authChainA) Decode(dst, src *bytes.Buffer) error {
diff --git a/transport/ssr/protocol/auth_sha1_v4.go b/transport/ssr/protocol/auth_sha1_v4.go
index 9e814ac2..26039181 100644
--- a/transport/ssr/protocol/auth_sha1_v4.go
+++ b/transport/ssr/protocol/auth_sha1_v4.go
@@ -7,6 +7,7 @@ import (
"hash/crc32"
"net"
+ N "github.com/Dreamacro/clash/common/net"
"github.com/Dreamacro/clash/common/pool"
"github.com/Dreamacro/clash/transport/ssr/tools"
@@ -35,7 +36,7 @@ func (a *authSHA1V4) StreamConn(c net.Conn, iv []byte) net.Conn {
return &Conn{Conn: c, Protocol: p}
}
-func (a *authSHA1V4) PacketConn(c net.PacketConn) net.PacketConn {
+func (a *authSHA1V4) PacketConn(c N.EnhancePacketConn) N.EnhancePacketConn {
return c
}
diff --git a/transport/ssr/protocol/origin.go b/transport/ssr/protocol/origin.go
index 80fdfa9a..52525a2f 100644
--- a/transport/ssr/protocol/origin.go
+++ b/transport/ssr/protocol/origin.go
@@ -3,6 +3,8 @@ package protocol
import (
"bytes"
"net"
+
+ N "github.com/Dreamacro/clash/common/net"
)
type origin struct{}
@@ -13,7 +15,7 @@ func newOrigin(b *Base) Protocol { return &origin{} }
func (o *origin) StreamConn(c net.Conn, iv []byte) net.Conn { return c }
-func (o *origin) PacketConn(c net.PacketConn) net.PacketConn { return c }
+func (o *origin) PacketConn(c N.EnhancePacketConn) N.EnhancePacketConn { return c }
func (o *origin) Decode(dst, src *bytes.Buffer) error {
dst.ReadFrom(src)
diff --git a/transport/ssr/protocol/packet.go b/transport/ssr/protocol/packet.go
index 249db70a..988ff75d 100644
--- a/transport/ssr/protocol/packet.go
+++ b/transport/ssr/protocol/packet.go
@@ -3,11 +3,12 @@ package protocol
import (
"net"
+ N "github.com/Dreamacro/clash/common/net"
"github.com/Dreamacro/clash/common/pool"
)
type PacketConn struct {
- net.PacketConn
+ N.EnhancePacketConn
Protocol
}
@@ -18,12 +19,12 @@ func (c *PacketConn) WriteTo(b []byte, addr net.Addr) (int, error) {
if err != nil {
return 0, err
}
- _, err = c.PacketConn.WriteTo(buf.Bytes(), addr)
+ _, err = c.EnhancePacketConn.WriteTo(buf.Bytes(), addr)
return len(b), err
}
func (c *PacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
- n, addr, err := c.PacketConn.ReadFrom(b)
+ n, addr, err := c.EnhancePacketConn.ReadFrom(b)
if err != nil {
return n, addr, err
}
@@ -34,3 +35,20 @@ func (c *PacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
copy(b, decoded)
return len(decoded), addr, nil
}
+
+func (c *PacketConn) WaitReadFrom() (data []byte, put func(), addr net.Addr, err error) {
+ data, put, addr, err = c.EnhancePacketConn.WaitReadFrom()
+ if err != nil {
+ return
+ }
+ data, err = c.DecodePacket(data)
+ if err != nil {
+ if put != nil {
+ put()
+ }
+ data = nil
+ put = nil
+ return
+ }
+ return
+}
diff --git a/transport/ssr/protocol/protocol.go b/transport/ssr/protocol/protocol.go
index 5b86ecb9..1c27da48 100644
--- a/transport/ssr/protocol/protocol.go
+++ b/transport/ssr/protocol/protocol.go
@@ -6,6 +6,8 @@ import (
"fmt"
"net"
+ N "github.com/Dreamacro/clash/common/net"
+
"github.com/zhangyunhao116/fastrand"
)
@@ -22,7 +24,7 @@ var (
type Protocol interface {
StreamConn(net.Conn, []byte) net.Conn
- PacketConn(net.PacketConn) net.PacketConn
+ PacketConn(N.EnhancePacketConn) N.EnhancePacketConn
Decode(dst, src *bytes.Buffer) error
Encode(buf *bytes.Buffer, b []byte) error
DecodePacket([]byte) ([]byte, error)
diff --git a/transport/trojan/trojan.go b/transport/trojan/trojan.go
index 8eae8237..abe21f34 100644
--- a/transport/trojan/trojan.go
+++ b/transport/trojan/trojan.go
@@ -13,6 +13,7 @@ import (
"net/http"
"sync"
+ N "github.com/Dreamacro/clash/common/net"
"github.com/Dreamacro/clash/common/pool"
tlsC "github.com/Dreamacro/clash/component/tls"
C "github.com/Dreamacro/clash/constant"
@@ -70,7 +71,7 @@ type Trojan struct {
hexPassword []byte
}
-func (t *Trojan) StreamConn(conn net.Conn) (net.Conn, error) {
+func (t *Trojan) StreamConn(ctx context.Context, conn net.Conn) (net.Conn, error) {
alpn := defaultALPN
if len(t.option.ALPN) != 0 {
alpn = t.option.ALPN
@@ -149,7 +150,7 @@ func (t *Trojan) StreamConn(conn net.Conn) (net.Conn, error) {
}
}
-func (t *Trojan) StreamWebsocketConn(conn net.Conn, wsOptions *WebsocketOption) (net.Conn, error) {
+func (t *Trojan) StreamWebsocketConn(ctx context.Context, conn net.Conn, wsOptions *WebsocketOption) (net.Conn, error) {
alpn := defaultWebsocketALPN
if len(t.option.ALPN) != 0 {
alpn = t.option.ALPN
@@ -162,7 +163,7 @@ func (t *Trojan) StreamWebsocketConn(conn net.Conn, wsOptions *WebsocketOption)
ServerName: t.option.ServerName,
}
- return vmess.StreamWebsocketConn(conn, &vmess.WebsocketConfig{
+ return vmess.StreamWebsocketConn(ctx, conn, &vmess.WebsocketConfig{
Host: wsOptions.Host,
Port: wsOptions.Port,
Path: wsOptions.Path,
@@ -303,6 +304,8 @@ func New(option *Option) *Trojan {
return &Trojan{option, hexSha224([]byte(option.Password))}
}
+var _ N.EnhancePacketConn = (*PacketConn)(nil)
+
type PacketConn struct {
net.Conn
remain int
@@ -350,6 +353,49 @@ func (pc *PacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
return n, addr, nil
}
+func (pc *PacketConn) WaitReadFrom() (data []byte, put func(), addr net.Addr, err error) {
+ pc.mux.Lock()
+ defer pc.mux.Unlock()
+
+ destination, err := socks5.ReadAddr0(pc.Conn)
+ if err != nil {
+ return nil, nil, nil, err
+ }
+ addr = destination.UDPAddr()
+
+ data = pool.Get(pool.UDPBufferSize)
+ put = func() {
+ _ = pool.Put(data)
+ }
+
+ _, err = io.ReadFull(pc.Conn, data[:2+2]) // u16be length + CR LF
+ if err != nil {
+ if put != nil {
+ put()
+ }
+ return nil, nil, nil, err
+ }
+ length := binary.BigEndian.Uint16(data)
+
+ if length > 0 {
+ data = data[:length]
+ _, err = io.ReadFull(pc.Conn, data)
+ if err != nil {
+ if put != nil {
+ put()
+ }
+ return nil, nil, nil, err
+ }
+ } else {
+ if put != nil {
+ put()
+ }
+ return nil, nil, addr, nil
+ }
+
+ return
+}
+
func hexSha224(data []byte) []byte {
buf := make([]byte, 56)
hash := sha256.New224()
diff --git a/transport/tuic/client.go b/transport/tuic/client.go
index af00da03..c18a9049 100644
--- a/transport/tuic/client.go
+++ b/transport/tuic/client.go
@@ -11,6 +11,7 @@ import (
"sync"
"sync/atomic"
"time"
+ "unsafe"
"github.com/Dreamacro/clash/common/buf"
N "github.com/Dreamacro/clash/common/net"
@@ -289,7 +290,8 @@ func (t *clientImpl) DialContextWithDialer(ctx context.Context, metadata *C.Meta
return nil, err
}
- conn := &earlyConn{BufferedConn: N.NewBufferedConn(stream), RequestTimeout: t.RequestTimeout}
+ bufConn := N.NewBufferedConn(stream)
+ conn := &earlyConn{ExtendedConn: bufConn, bufConn: bufConn, RequestTimeout: t.RequestTimeout}
if !t.FastOpen {
err = conn.Response()
if err != nil {
@@ -300,9 +302,10 @@ func (t *clientImpl) DialContextWithDialer(ctx context.Context, metadata *C.Meta
}
type earlyConn struct {
- *N.BufferedConn
- resOnce sync.Once
- resErr error
+ N.ExtendedConn // only expose standard N.ExtendedConn function to outside
+ bufConn *N.BufferedConn
+ resOnce sync.Once
+ resErr error
RequestTimeout time.Duration
}
@@ -311,7 +314,7 @@ func (conn *earlyConn) response() error {
if conn.RequestTimeout > 0 {
_ = conn.SetReadDeadline(time.Now().Add(conn.RequestTimeout))
}
- response, err := ReadResponse(conn)
+ response, err := ReadResponse(conn.bufConn)
if err != nil {
_ = conn.Close()
return err
@@ -336,7 +339,7 @@ func (conn *earlyConn) Read(b []byte) (n int, err error) {
if err != nil {
return 0, err
}
- return conn.BufferedConn.Read(b)
+ return conn.bufConn.Read(b)
}
func (conn *earlyConn) ReadBuffer(buffer *buf.Buffer) (err error) {
@@ -344,7 +347,19 @@ func (conn *earlyConn) ReadBuffer(buffer *buf.Buffer) (err error) {
if err != nil {
return err
}
- return conn.BufferedConn.ReadBuffer(buffer)
+ return conn.bufConn.ReadBuffer(buffer)
+}
+
+func (conn *earlyConn) Upstream() any {
+ return conn.bufConn
+}
+
+func (conn *earlyConn) ReaderReplaceable() bool {
+ return atomic.LoadUint32((*uint32)(unsafe.Pointer(&conn.resOnce))) == 1 && conn.resErr == nil
+}
+
+func (conn *earlyConn) WriterReplaceable() bool {
+ return true
}
func (t *clientImpl) ListenPacketWithDialer(ctx context.Context, metadata *C.Metadata, dialer C.Dialer, dialFn DialFunc) (net.PacketConn, error) {
diff --git a/transport/tuic/congestion/bbr_sender.go b/transport/tuic/congestion/bbr_sender.go
index 99164362..17368386 100644
--- a/transport/tuic/congestion/bbr_sender.go
+++ b/transport/tuic/congestion/bbr_sender.go
@@ -17,17 +17,12 @@ const (
InitialMaxDatagramSize = 1252
InitialPacketSizeIPv4 = 1252
InitialPacketSizeIPv6 = 1232
- InitialCongestionWindow = 10
+ InitialCongestionWindow = 32
DefaultBBRMaxCongestionWindow = 10000
)
-const (
- initialMinCongestionWindow = 4
- minInitialPacketSize = 1200
-)
-
func GetInitialPacketSize(addr net.Addr) congestion.ByteCount {
- maxSize := congestion.ByteCount(minInitialPacketSize)
+ maxSize := congestion.ByteCount(1200)
// If this is not a UDP address, we don't know anything about the MTU.
// Use the minimum size of an Initial packet as the max packet size.
if udpAddr, ok := addr.(*net.UDPAddr); ok {
@@ -37,7 +32,7 @@ func GetInitialPacketSize(addr net.Addr) congestion.ByteCount {
maxSize = InitialPacketSizeIPv6
}
}
- return maxSize
+ return congestion.ByteCount(maxSize)
}
var (
@@ -45,8 +40,8 @@ var (
// Default initial rtt used before any samples are received.
InitialRtt = 100 * time.Millisecond
- // The gain used for the STARTUP, equal to 2/ln(2).
- DefaultHighGain = 2.89
+ // The gain used for the STARTUP, equal to 4*ln(2).
+ DefaultHighGain = 2.77
// The gain used in STARTUP after loss has been detected.
// 1.5 is enough to allow for 25% exogenous loss and still observe a 25% growth
@@ -281,7 +276,7 @@ func (b *bbrSender) maxCongestionWindow() congestion.ByteCount {
}
func (b *bbrSender) minCongestionWindow() congestion.ByteCount {
- return b.maxDatagramSize * initialMinCongestionWindow
+ return b.maxDatagramSize * b.initialCongestionWindow
}
func (b *bbrSender) SetRTTStatsProvider(provider congestion.RTTStatsProvider) {
diff --git a/transport/tuic/conn.go b/transport/tuic/conn.go
index 567f6ce5..f226746d 100644
--- a/transport/tuic/conn.go
+++ b/transport/tuic/conn.go
@@ -197,6 +197,21 @@ func (q *quicStreamPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err err
return
}
+func (q *quicStreamPacketConn) WaitReadFrom() (data []byte, put func(), addr net.Addr, err error) {
+ if q.inputConn != nil {
+ var packet Packet
+ packet, err = ReadPacket(q.inputConn)
+ if err != nil {
+ return
+ }
+ data = packet.DATA
+ addr = packet.ADDR.UDPAddr()
+ } else {
+ err = net.ErrClosed
+ }
+ return
+}
+
func (q *quicStreamPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
if q.udpRelayMode != "quic" && len(p) > q.maxUdpRelayPacketSize {
return 0, quic.ErrMessageTooLarge(q.maxUdpRelayPacketSize)
diff --git a/transport/tuic/protocol.go b/transport/tuic/protocol.go
index a460eecc..472bb980 100644
--- a/transport/tuic/protocol.go
+++ b/transport/tuic/protocol.go
@@ -465,6 +465,11 @@ func NewAddress(metadata *C.Metadata) Address {
}
func NewAddressNetAddr(addr net.Addr) (Address, error) {
+ if addr, ok := addr.(interface{ AddrPort() netip.AddrPort }); ok {
+ if addrPort := addr.AddrPort(); addrPort.IsValid() { // sing's M.Socksaddr maybe return an invalid AddrPort if it's a DomainName
+ return NewAddressAddrPort(addrPort), nil
+ }
+ }
addrStr := addr.String()
if addrPort, err := netip.ParseAddrPort(addrStr); err == nil {
return NewAddressAddrPort(addrPort), nil
diff --git a/transport/v2ray-plugin/websocket.go b/transport/v2ray-plugin/websocket.go
index 7c2c8a88..25483670 100644
--- a/transport/v2ray-plugin/websocket.go
+++ b/transport/v2ray-plugin/websocket.go
@@ -1,6 +1,7 @@
package obfs
import (
+ "context"
"crypto/tls"
"net"
"net/http"
@@ -22,7 +23,7 @@ type Option struct {
}
// NewV2rayObfs return a HTTPObfs
-func NewV2rayObfs(conn net.Conn, option *Option) (net.Conn, error) {
+func NewV2rayObfs(ctx context.Context, conn net.Conn, option *Option) (net.Conn, error) {
header := http.Header{}
for k, v := range option.Headers {
header.Add(k, v)
@@ -57,7 +58,7 @@ func NewV2rayObfs(conn net.Conn, option *Option) (net.Conn, error) {
}
var err error
- conn, err = vmess.StreamWebsocketConn(conn, config)
+ conn, err = vmess.StreamWebsocketConn(ctx, conn, config)
if err != nil {
return nil, err
}
diff --git a/transport/vless/conn.go b/transport/vless/conn.go
index 6c3714e0..9e2e5e89 100644
--- a/transport/vless/conn.go
+++ b/transport/vless/conn.go
@@ -1,25 +1,18 @@
package vless
import (
- "bytes"
- "crypto/subtle"
- gotls "crypto/tls"
"encoding/binary"
"errors"
"fmt"
"io"
"net"
- "reflect"
"sync"
- "unsafe"
"github.com/Dreamacro/clash/common/buf"
N "github.com/Dreamacro/clash/common/net"
- tlsC "github.com/Dreamacro/clash/component/tls"
- "github.com/Dreamacro/clash/log"
+ "github.com/Dreamacro/clash/transport/vless/vision"
"github.com/gofrs/uuid/v5"
- utls "github.com/sagernet/utls"
xtls "github.com/xtls/go"
"google.golang.org/protobuf/proto"
)
@@ -36,33 +29,10 @@ type Conn struct {
handshakeMutex sync.Mutex
needHandshake bool
err error
-
- tlsConn net.Conn
- input *bytes.Reader
- rawInput *bytes.Buffer
-
- packetsToFilter int
- isTLS bool
- isTLS12orAbove bool
- enableXTLS bool
- cipher uint16
- remainingServerHello uint16
- readRemainingContent int
- readRemainingPadding int
- readProcess bool
- readFilterUUID bool
- readLastCommand byte
- writeFilterApplicationData bool
- writeDirect bool
}
func (vc *Conn) Read(b []byte) (int, error) {
if vc.received {
- if vc.readProcess {
- buffer := buf.With(b)
- err := vc.ReadBuffer(buffer)
- return buffer.Len(), err
- }
return vc.ExtendedReader.Read(b)
}
@@ -70,106 +40,11 @@ func (vc *Conn) Read(b []byte) (int, error) {
return 0, err
}
vc.received = true
- return vc.Read(b)
+ return vc.ExtendedReader.Read(b)
}
func (vc *Conn) ReadBuffer(buffer *buf.Buffer) error {
if vc.received {
- toRead := buffer.FreeBytes()
- if vc.readRemainingContent > 0 {
- if vc.readRemainingContent < buffer.FreeLen() {
- toRead = toRead[:vc.readRemainingContent]
- }
- n, err := vc.ExtendedReader.Read(toRead)
- buffer.Truncate(n)
- vc.readRemainingContent -= n
- vc.FilterTLS(toRead)
- return err
- }
- if vc.readRemainingPadding > 0 {
- _, err := io.CopyN(io.Discard, vc.ExtendedReader, int64(vc.readRemainingPadding))
- if err != nil {
- return err
- }
- vc.readRemainingPadding = 0
- }
- if vc.readProcess {
- switch vc.readLastCommand {
- case commandPaddingContinue:
- //if vc.isTLS || vc.packetsToFilter > 0 {
- headerUUIDLen := 0
- if vc.readFilterUUID {
- headerUUIDLen = uuid.Size
- }
- var header []byte
- if need := headerUUIDLen + paddingHeaderLen; buffer.FreeLen() < need {
- header = make([]byte, need)
- } else {
- header = buffer.FreeBytes()[:need]
- }
- _, err := io.ReadFull(vc.ExtendedReader, header)
- if err != nil {
- return err
- }
- pos := 0
- if vc.readFilterUUID {
- vc.readFilterUUID = false
- pos = uuid.Size
- if subtle.ConstantTimeCompare(vc.id.Bytes(), header[:uuid.Size]) != 1 {
- err = fmt.Errorf("XTLS Vision server responded unknown UUID: %s",
- uuid.FromBytesOrNil(header[:uuid.Size]).String())
- log.Errorln(err.Error())
- return err
- }
- }
- vc.readLastCommand = header[pos]
- vc.readRemainingContent = int(binary.BigEndian.Uint16(header[pos+1:]))
- vc.readRemainingPadding = int(binary.BigEndian.Uint16(header[pos+3:]))
- log.Debugln("XTLS Vision read padding: command=%d, payloadLen=%d, paddingLen=%d",
- vc.readLastCommand, vc.readRemainingContent, vc.readRemainingPadding)
- return vc.ReadBuffer(buffer)
- //}
- case commandPaddingEnd:
- vc.readProcess = false
- return vc.ReadBuffer(buffer)
- case commandPaddingDirect:
- needReturn := false
- if vc.input != nil {
- _, err := buffer.ReadFrom(vc.input)
- if err != nil {
- return err
- }
- if vc.input.Len() == 0 {
- needReturn = true
- vc.input = nil
- } else { // buffer is full
- return nil
- }
- }
- if vc.rawInput != nil {
- _, err := buffer.ReadFrom(vc.rawInput)
- if err != nil {
- return err
- }
- needReturn = true
- if vc.rawInput.Len() == 0 {
- vc.rawInput = nil
- }
- }
- if vc.input == nil && vc.rawInput == nil {
- vc.readProcess = false
- vc.ExtendedReader = N.NewExtendedReader(vc.Conn)
- log.Debugln("XTLS Vision direct read start")
- }
- if needReturn {
- return nil
- }
- default:
- err := fmt.Errorf("XTLS Vision read unknown command: %d", vc.readLastCommand)
- log.Debugln(err.Error())
- return err
- }
- }
return vc.ExtendedReader.ReadBuffer(buffer)
}
@@ -177,7 +52,7 @@ func (vc *Conn) ReadBuffer(buffer *buf.Buffer) error {
return err
}
vc.received = true
- return vc.ReadBuffer(buffer)
+ return vc.ExtendedReader.ReadBuffer(buffer)
}
func (vc *Conn) Write(p []byte) (int, error) {
@@ -200,18 +75,6 @@ func (vc *Conn) Write(p []byte) (int, error) {
vc.handshakeMutex.Unlock()
}
- if vc.writeFilterApplicationData {
- _buffer := buf.StackNew()
- defer buf.KeepAlive(_buffer)
- buffer := buf.Dup(_buffer)
- defer buffer.Release()
- buffer.Write(p)
- err := vc.WriteBuffer(buffer)
- if err != nil {
- return 0, err
- }
- return len(p), nil
- }
return vc.ExtendedWriter.Write(p)
}
@@ -232,63 +95,6 @@ func (vc *Conn) WriteBuffer(buffer *buf.Buffer) error {
vc.handshakeMutex.Unlock()
}
- if vc.writeFilterApplicationData {
- buffer2 := ReshapeBuffer(buffer)
- defer buffer2.Release()
- vc.FilterTLS(buffer.Bytes())
- command := commandPaddingContinue
- if !vc.isTLS {
- command = commandPaddingEnd
-
- // disable XTLS
- //vc.readProcess = false
- vc.writeFilterApplicationData = false
- vc.packetsToFilter = 0
- } else if buffer.Len() > 6 && bytes.Equal(buffer.To(3), tlsApplicationDataStart) || vc.packetsToFilter <= 0 {
- command = commandPaddingEnd
- if vc.enableXTLS {
- command = commandPaddingDirect
- vc.writeDirect = true
- }
- vc.writeFilterApplicationData = false
- }
- ApplyPadding(buffer, command, nil, vc.isTLS)
- err := vc.ExtendedWriter.WriteBuffer(buffer)
- if err != nil {
- return err
- }
- if vc.writeDirect {
- vc.ExtendedWriter = N.NewExtendedWriter(vc.Conn)
- log.Debugln("XTLS Vision direct write start")
- //time.Sleep(5 * time.Millisecond)
- }
- if buffer2 != nil {
- if vc.writeDirect || !vc.isTLS {
- return vc.ExtendedWriter.WriteBuffer(buffer2)
- }
- vc.FilterTLS(buffer2.Bytes())
- command = commandPaddingContinue
- if buffer2.Len() > 6 && bytes.Equal(buffer2.To(3), tlsApplicationDataStart) || vc.packetsToFilter <= 0 {
- command = commandPaddingEnd
- if vc.enableXTLS {
- command = commandPaddingDirect
- vc.writeDirect = true
- }
- vc.writeFilterApplicationData = false
- }
- ApplyPadding(buffer2, command, nil, vc.isTLS)
- err = vc.ExtendedWriter.WriteBuffer(buffer2)
- if vc.writeDirect {
- vc.ExtendedWriter = N.NewExtendedWriter(vc.Conn)
- log.Debugln("XTLS Vision direct write start")
- //time.Sleep(10 * time.Millisecond)
- }
- }
- return err
- }
- /*if vc.writeDirect {
- log.Debugln("XTLS Vision Direct write, payloadLen=%d", buffer.Len())
- }*/
return vc.ExtendedWriter.WriteBuffer(buffer)
}
@@ -300,10 +106,9 @@ func (vc *Conn) sendRequest(p []byte) bool {
return true
}
}
- isVision := vc.IsXTLSVisionEnabled()
var buffer *buf.Buffer
- if isVision {
+ if vc.IsXTLSVisionEnabled() {
_buffer := buf.StackNew()
defer buf.KeepAlive(_buffer)
buffer = buf.Dup(_buffer)
@@ -350,50 +155,14 @@ func (vc *Conn) sendRequest(p []byte) bool {
)
}
- if isVision && !vc.dst.UDP && !vc.dst.Mux {
- if len(p) == 0 {
- WriteWithPadding(buffer, nil, commandPaddingContinue, vc.id, vc.isTLS)
- } else {
- vc.FilterTLS(p)
- //if vc.isTLS {
- WriteWithPadding(buffer, p, commandPaddingContinue, vc.id, vc.isTLS)
- //} else {
- // buf.Must(buf.Error(buffer.Write(p)))
- //
- // // disable XTLS
- // vc.readProcess = false
- // vc.writeFilterApplicationData = false
- // vc.packetsToFilter = 0
- //}
- }
- } else {
- buf.Must(buf.Error(buffer.Write(p)))
- }
+ buf.Must(buf.Error(buffer.Write(p)))
_, vc.err = vc.ExtendedWriter.Write(buffer.Bytes())
- if vc.err != nil {
- return true
- }
- if isVision {
- switch underlying := vc.tlsConn.(type) {
- case *gotls.Conn:
- if underlying.ConnectionState().Version != gotls.VersionTLS13 {
- vc.err = ErrNotTLS13
- }
- case *utls.UConn:
- if underlying.ConnectionState().Version != utls.VersionTLS13 {
- vc.err = ErrNotTLS13
- }
- default:
- vc.err = fmt.Errorf(`failed to use %s, maybe "security" is not "tls" or "utls"`, vc.addons.Flow)
- }
- vc.tlsConn = nil
- }
return true
}
func (vc *Conn) recvResponse() error {
- var buffer [1]byte
+ var buffer [2]byte
_, vc.err = io.ReadFull(vc.ExtendedReader, buffer[:])
if vc.err != nil {
return vc.err
@@ -403,12 +172,7 @@ func (vc *Conn) recvResponse() error {
return errors.New("unexpected response version")
}
- _, vc.err = io.ReadFull(vc.ExtendedReader, buffer[:])
- if vc.err != nil {
- return vc.err
- }
-
- length := int64(buffer[0])
+ length := int64(buffer[1])
if length != 0 { // addon data length > 0
io.CopyN(io.Discard, vc.ExtendedReader, length) // just discard
}
@@ -416,18 +180,8 @@ func (vc *Conn) recvResponse() error {
return nil
}
-func (vc *Conn) FrontHeadroom() int {
- if vc.IsXTLSVisionEnabled() {
- return paddingHeaderLen
- }
- return 0
-}
-
func (vc *Conn) Upstream() any {
- if vc.tlsConn == nil {
- return vc.Conn
- }
- return vc.tlsConn
+ return vc.Conn
}
func (vc *Conn) NeedHandshake() bool {
@@ -439,7 +193,7 @@ func (vc *Conn) IsXTLSVisionEnabled() bool {
}
// newConn return a Conn instance
-func newConn(conn net.Conn, client *Client, dst *DstAddr) (*Conn, error) {
+func newConn(conn net.Conn, client *Client, dst *DstAddr) (net.Conn, error) {
c := &Conn{
ExtendedReader: N.NewExtendedReader(conn),
ExtendedWriter: N.NewExtendedWriter(conn),
@@ -468,43 +222,12 @@ func newConn(conn net.Conn, client *Client, dst *DstAddr) (*Conn, error) {
return nil, fmt.Errorf("failed to use %s, maybe \"security\" is not \"xtls\"", client.Addons.Flow)
}
case XRV:
- c.packetsToFilter = 6
- c.readProcess = true
- c.readFilterUUID = true
- c.writeFilterApplicationData = true
- c.addons = client.Addons
- var t reflect.Type
- var p uintptr
- switch underlying := conn.(type) {
- case *gotls.Conn:
- //log.Debugln("type tls")
- c.Conn = underlying.NetConn()
- c.tlsConn = underlying
- t = reflect.TypeOf(underlying).Elem()
- p = uintptr(unsafe.Pointer(underlying))
- case *utls.UConn:
- //log.Debugln("type *utls.UConn")
- c.Conn = underlying.NetConn()
- c.tlsConn = underlying
- t = reflect.TypeOf(underlying.Conn).Elem()
- p = uintptr(unsafe.Pointer(underlying.Conn))
- case *tlsC.UConn:
- //log.Debugln("type *tlsC.UConn")
- c.Conn = underlying.NetConn()
- c.tlsConn = underlying.UConn
- t = reflect.TypeOf(underlying.Conn).Elem()
- //log.Debugln("t:%v", t)
- p = uintptr(unsafe.Pointer(underlying.Conn))
- default:
- return nil, fmt.Errorf(`failed to use %s, maybe "security" is not "tls" or "utls"`, client.Addons.Flow)
+ visionConn, err := vision.NewConn(c, c.id)
+ if err != nil {
+ return nil, err
}
- i, _ := t.FieldByName("input")
- r, _ := t.FieldByName("rawInput")
- c.input = (*bytes.Reader)(unsafe.Pointer(p + i.Offset))
- c.rawInput = (*bytes.Buffer)(unsafe.Pointer(p + r.Offset))
- //if _, ok := c.Conn.(*net.TCPConn); !ok {
- // log.Debugln("XTLS underlying conn is not *net.TCPConn, got %T", c.Conn)
- //}
+ c.addons = client.Addons
+ return visionConn, nil
}
}
diff --git a/transport/vless/vision/conn.go b/transport/vless/vision/conn.go
new file mode 100644
index 00000000..23721c0b
--- /dev/null
+++ b/transport/vless/vision/conn.go
@@ -0,0 +1,304 @@
+package vision
+
+import (
+ "bytes"
+ "crypto/subtle"
+ gotls "crypto/tls"
+ "encoding/binary"
+ "fmt"
+ "io"
+ "net"
+
+ "github.com/Dreamacro/clash/common/buf"
+ N "github.com/Dreamacro/clash/common/net"
+ "github.com/Dreamacro/clash/log"
+
+ "github.com/gofrs/uuid/v5"
+ utls "github.com/sagernet/utls"
+)
+
+var (
+ _ N.ExtendedConn = (*Conn)(nil)
+)
+
+type Conn struct {
+ net.Conn
+ N.ExtendedReader
+ N.ExtendedWriter
+ upstream net.Conn
+ userUUID *uuid.UUID
+
+ tlsConn net.Conn
+ input *bytes.Reader
+ rawInput *bytes.Buffer
+
+ needHandshake bool
+ packetsToFilter int
+ isTLS bool
+ isTLS12orAbove bool
+ enableXTLS bool
+ cipher uint16
+ remainingServerHello uint16
+ readRemainingContent int
+ readRemainingPadding int
+ readProcess bool
+ readFilterUUID bool
+ readLastCommand byte
+ writeFilterApplicationData bool
+ writeDirect bool
+}
+
+func (vc *Conn) Read(b []byte) (int, error) {
+ if vc.readProcess {
+ buffer := buf.With(b)
+ err := vc.ReadBuffer(buffer)
+ return buffer.Len(), err
+ }
+ return vc.ExtendedReader.Read(b)
+}
+
+func (vc *Conn) ReadBuffer(buffer *buf.Buffer) error {
+ toRead := buffer.FreeBytes()
+ if vc.readRemainingContent > 0 {
+ if vc.readRemainingContent < buffer.FreeLen() {
+ toRead = toRead[:vc.readRemainingContent]
+ }
+ n, err := vc.ExtendedReader.Read(toRead)
+ buffer.Truncate(n)
+ vc.readRemainingContent -= n
+ vc.FilterTLS(toRead)
+ return err
+ }
+ if vc.readRemainingPadding > 0 {
+ _, err := io.CopyN(io.Discard, vc.ExtendedReader, int64(vc.readRemainingPadding))
+ if err != nil {
+ return err
+ }
+ vc.readRemainingPadding = 0
+ }
+ if vc.readProcess {
+ switch vc.readLastCommand {
+ case commandPaddingContinue:
+ //if vc.isTLS || vc.packetsToFilter > 0 {
+ headerUUIDLen := 0
+ if vc.readFilterUUID {
+ headerUUIDLen = uuid.Size
+ }
+ var header []byte
+ if need := headerUUIDLen + PaddingHeaderLen - uuid.Size; buffer.FreeLen() < need {
+ header = make([]byte, need)
+ } else {
+ header = buffer.FreeBytes()[:need]
+ }
+ _, err := io.ReadFull(vc.ExtendedReader, header)
+ if err != nil {
+ return err
+ }
+ if vc.readFilterUUID {
+ vc.readFilterUUID = false
+ if subtle.ConstantTimeCompare(vc.userUUID.Bytes(), header[:uuid.Size]) != 1 {
+ err = fmt.Errorf("XTLS Vision server responded unknown UUID: %s",
+ uuid.FromBytesOrNil(header[:uuid.Size]).String())
+ log.Errorln(err.Error())
+ return err
+ }
+ header = header[uuid.Size:]
+ }
+ vc.readRemainingPadding = int(binary.BigEndian.Uint16(header[3:]))
+ vc.readRemainingContent = int(binary.BigEndian.Uint16(header[1:]))
+ vc.readLastCommand = header[0]
+ log.Debugln("XTLS Vision read padding: command=%d, payloadLen=%d, paddingLen=%d",
+ vc.readLastCommand, vc.readRemainingContent, vc.readRemainingPadding)
+ return vc.ReadBuffer(buffer)
+ //}
+ case commandPaddingEnd:
+ vc.readProcess = false
+ return vc.ReadBuffer(buffer)
+ case commandPaddingDirect:
+ needReturn := false
+ if vc.input != nil {
+ _, err := buffer.ReadFrom(vc.input)
+ if err != nil {
+ return err
+ }
+ if vc.input.Len() == 0 {
+ needReturn = true
+ vc.input = nil
+ } else { // buffer is full
+ return nil
+ }
+ }
+ if vc.rawInput != nil {
+ _, err := buffer.ReadFrom(vc.rawInput)
+ if err != nil {
+ return err
+ }
+ needReturn = true
+ if vc.rawInput.Len() == 0 {
+ vc.rawInput = nil
+ }
+ }
+ if vc.input == nil && vc.rawInput == nil {
+ vc.readProcess = false
+ vc.ExtendedReader = N.NewExtendedReader(vc.Conn)
+ log.Debugln("XTLS Vision direct read start")
+ }
+ if needReturn {
+ return nil
+ }
+ default:
+ err := fmt.Errorf("XTLS Vision read unknown command: %d", vc.readLastCommand)
+ log.Debugln(err.Error())
+ return err
+ }
+ }
+ return vc.ExtendedReader.ReadBuffer(buffer)
+}
+
+func (vc *Conn) Write(p []byte) (int, error) {
+ if vc.writeFilterApplicationData {
+ _buffer := buf.StackNew()
+ defer buf.KeepAlive(_buffer)
+ buffer := buf.Dup(_buffer)
+ defer buffer.Release()
+ buffer.Write(p)
+ err := vc.WriteBuffer(buffer)
+ if err != nil {
+ return 0, err
+ }
+ return len(p), nil
+ }
+ return vc.ExtendedWriter.Write(p)
+}
+
+func (vc *Conn) WriteBuffer(buffer *buf.Buffer) (err error) {
+ if vc.needHandshake {
+ vc.needHandshake = false
+ if buffer.IsEmpty() {
+ ApplyPadding(buffer, commandPaddingContinue, vc.userUUID, false)
+ } else {
+ vc.FilterTLS(buffer.Bytes())
+ ApplyPadding(buffer, commandPaddingContinue, vc.userUUID, vc.isTLS)
+ }
+ err = vc.ExtendedWriter.WriteBuffer(buffer)
+ if err != nil {
+ buffer.Release()
+ return err
+ }
+ switch underlying := vc.tlsConn.(type) {
+ case *gotls.Conn:
+ if underlying.ConnectionState().Version != gotls.VersionTLS13 {
+ buffer.Release()
+ return ErrNotTLS13
+ }
+ case *utls.UConn:
+ if underlying.ConnectionState().Version != utls.VersionTLS13 {
+ buffer.Release()
+ return ErrNotTLS13
+ }
+ }
+ vc.tlsConn = nil
+ return nil
+ }
+
+ if vc.writeFilterApplicationData {
+ buffer2 := ReshapeBuffer(buffer)
+ defer buffer2.Release()
+ vc.FilterTLS(buffer.Bytes())
+ command := commandPaddingContinue
+ if !vc.isTLS {
+ command = commandPaddingEnd
+
+ // disable XTLS
+ //vc.readProcess = false
+ vc.writeFilterApplicationData = false
+ vc.packetsToFilter = 0
+ } else if buffer.Len() > 6 && bytes.Equal(buffer.To(3), tlsApplicationDataStart) || vc.packetsToFilter <= 0 {
+ command = commandPaddingEnd
+ if vc.enableXTLS {
+ command = commandPaddingDirect
+ vc.writeDirect = true
+ }
+ vc.writeFilterApplicationData = false
+ }
+ ApplyPadding(buffer, command, nil, vc.isTLS)
+ err = vc.ExtendedWriter.WriteBuffer(buffer)
+ if err != nil {
+ return err
+ }
+ if vc.writeDirect {
+ vc.ExtendedWriter = N.NewExtendedWriter(vc.Conn)
+ log.Debugln("XTLS Vision direct write start")
+ //time.Sleep(5 * time.Millisecond)
+ }
+ if buffer2 != nil {
+ if vc.writeDirect || !vc.isTLS {
+ return vc.ExtendedWriter.WriteBuffer(buffer2)
+ }
+ vc.FilterTLS(buffer2.Bytes())
+ command = commandPaddingContinue
+ if buffer2.Len() > 6 && bytes.Equal(buffer2.To(3), tlsApplicationDataStart) || vc.packetsToFilter <= 0 {
+ command = commandPaddingEnd
+ if vc.enableXTLS {
+ command = commandPaddingDirect
+ vc.writeDirect = true
+ }
+ vc.writeFilterApplicationData = false
+ }
+ ApplyPadding(buffer2, command, nil, vc.isTLS)
+ err = vc.ExtendedWriter.WriteBuffer(buffer2)
+ if vc.writeDirect {
+ vc.ExtendedWriter = N.NewExtendedWriter(vc.Conn)
+ log.Debugln("XTLS Vision direct write start")
+ //time.Sleep(10 * time.Millisecond)
+ }
+ }
+ return err
+ }
+ /*if vc.writeDirect {
+ log.Debugln("XTLS Vision Direct write, payloadLen=%d", buffer.Len())
+ }*/
+ return vc.ExtendedWriter.WriteBuffer(buffer)
+}
+
+func (vc *Conn) FrontHeadroom() int {
+ if vc.readFilterUUID {
+ return PaddingHeaderLen
+ }
+ return PaddingHeaderLen - uuid.Size
+}
+
+func (vc *Conn) NeedHandshake() bool {
+ return vc.needHandshake
+}
+
+func (vc *Conn) Upstream() any {
+ if vc.writeDirect ||
+ vc.readLastCommand == commandPaddingDirect {
+ return vc.Conn
+ }
+ return vc.upstream
+}
+
+func (vc *Conn) ReaderPossiblyReplaceable() bool {
+ return vc.readProcess
+}
+
+func (vc *Conn) ReaderReplaceable() bool {
+ if !vc.readProcess &&
+ vc.readLastCommand == commandPaddingDirect {
+ return true
+ }
+ return false
+}
+
+func (vc *Conn) WriterPossiblyReplaceable() bool {
+ return vc.writeFilterApplicationData
+}
+
+func (vc *Conn) WriterReplaceable() bool {
+ if vc.writeDirect {
+ return true
+ }
+ return false
+}
diff --git a/transport/vless/filter.go b/transport/vless/vision/filter.go
similarity index 99%
rename from transport/vless/filter.go
rename to transport/vless/vision/filter.go
index f577be7a..e070de35 100644
--- a/transport/vless/filter.go
+++ b/transport/vless/vision/filter.go
@@ -1,4 +1,4 @@
-package vless
+package vision
import (
"bytes"
diff --git a/transport/vless/vision.go b/transport/vless/vision/padding.go
similarity index 94%
rename from transport/vless/vision.go
rename to transport/vless/vision/padding.go
index 5649dde4..d5a230d1 100644
--- a/transport/vless/vision.go
+++ b/transport/vless/vision/padding.go
@@ -1,4 +1,4 @@
-package vless
+package vision
import (
"bytes"
@@ -12,7 +12,7 @@ import (
)
const (
- paddingHeaderLen = 1 + 2 + 2 // =5
+ PaddingHeaderLen = uuid.Size + 1 + 2 + 2 // =21
commandPaddingContinue byte = 0x00
commandPaddingEnd byte = 0x01
@@ -67,7 +67,7 @@ func ApplyPadding(buffer *buf.Buffer, command byte, userUUID *uuid.UUID, padding
}
func ReshapeBuffer(buffer *buf.Buffer) *buf.Buffer {
- if buffer.Len() <= buf.BufferSize-paddingHeaderLen {
+ if buffer.Len() <= buf.BufferSize-PaddingHeaderLen {
return nil
}
cutAt := bytes.LastIndex(buffer.Bytes(), tlsApplicationDataStart)
diff --git a/transport/vless/vision/vision.go b/transport/vless/vision/vision.go
new file mode 100644
index 00000000..3b52dd4b
--- /dev/null
+++ b/transport/vless/vision/vision.go
@@ -0,0 +1,70 @@
+// Package vision implements VLESS flow `xtls-rprx-vision` introduced by Xray-core.
+package vision
+
+import (
+ "bytes"
+ gotls "crypto/tls"
+ "errors"
+ "fmt"
+ "net"
+ "reflect"
+ "unsafe"
+
+ N "github.com/Dreamacro/clash/common/net"
+ tlsC "github.com/Dreamacro/clash/component/tls"
+
+ "github.com/gofrs/uuid/v5"
+ "github.com/sagernet/sing/common"
+ utls "github.com/sagernet/utls"
+)
+
+var ErrNotTLS13 = errors.New("XTLS Vision based on TLS 1.3 outer connection")
+
+type connWithUpstream interface {
+ net.Conn
+ common.WithUpstream
+}
+
+func NewConn(conn connWithUpstream, userUUID *uuid.UUID) (*Conn, error) {
+ c := &Conn{
+ ExtendedReader: N.NewExtendedReader(conn),
+ ExtendedWriter: N.NewExtendedWriter(conn),
+ upstream: conn,
+ userUUID: userUUID,
+ packetsToFilter: 6,
+ needHandshake: true,
+ readProcess: true,
+ readFilterUUID: true,
+ writeFilterApplicationData: true,
+ }
+ var t reflect.Type
+ var p unsafe.Pointer
+ switch underlying := conn.Upstream().(type) {
+ case *gotls.Conn:
+ //log.Debugln("type tls")
+ c.Conn = underlying.NetConn()
+ c.tlsConn = underlying
+ t = reflect.TypeOf(underlying).Elem()
+ p = unsafe.Pointer(underlying)
+ case *utls.UConn:
+ //log.Debugln("type *utls.UConn")
+ c.Conn = underlying.NetConn()
+ c.tlsConn = underlying
+ t = reflect.TypeOf(underlying.Conn).Elem()
+ p = unsafe.Pointer(underlying.Conn)
+ case *tlsC.UConn:
+ //log.Debugln("type *tlsC.UConn")
+ c.Conn = underlying.NetConn()
+ c.tlsConn = underlying.UConn
+ t = reflect.TypeOf(underlying.Conn).Elem()
+ //log.Debugln("t:%v", t)
+ p = unsafe.Pointer(underlying.Conn)
+ default:
+ return nil, fmt.Errorf(`failed to use vision, maybe "security" is not "tls" or "utls"`)
+ }
+ i, _ := t.FieldByName("input")
+ r, _ := t.FieldByName("rawInput")
+ c.input = (*bytes.Reader)(unsafe.Add(p, i.Offset))
+ c.rawInput = (*bytes.Buffer)(unsafe.Add(p, r.Offset))
+ return c, nil
+}
diff --git a/transport/vless/xtls.go b/transport/vless/xtls.go
index 3a319568..071e6e8f 100644
--- a/transport/vless/xtls.go
+++ b/transport/vless/xtls.go
@@ -2,18 +2,12 @@ package vless
import (
"context"
- "errors"
"net"
tlsC "github.com/Dreamacro/clash/component/tls"
- C "github.com/Dreamacro/clash/constant"
xtls "github.com/xtls/go"
)
-var (
- ErrNotTLS13 = errors.New("XTLS Vision based on TLS 1.3 outer connection")
-)
-
type XTLSConfig struct {
Host string
SkipCertVerify bool
@@ -21,7 +15,7 @@ type XTLSConfig struct {
NextProtos []string
}
-func StreamXTLSConn(conn net.Conn, cfg *XTLSConfig) (net.Conn, error) {
+func StreamXTLSConn(ctx context.Context, conn net.Conn, cfg *XTLSConfig) (net.Conn, error) {
xtlsConfig := &xtls.Config{
ServerName: cfg.Host,
InsecureSkipVerify: cfg.SkipCertVerify,
@@ -38,9 +32,6 @@ func StreamXTLSConn(conn net.Conn, cfg *XTLSConfig) (net.Conn, error) {
xtlsConn := xtls.Client(conn, xtlsConfig)
- // fix xtls handshake not timeout
- ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout)
- defer cancel()
err := xtlsConn.HandshakeContext(ctx)
return xtlsConn, err
}
diff --git a/transport/vmess/tls.go b/transport/vmess/tls.go
index f020d273..54813029 100644
--- a/transport/vmess/tls.go
+++ b/transport/vmess/tls.go
@@ -7,7 +7,6 @@ import (
"net"
tlsC "github.com/Dreamacro/clash/component/tls"
- C "github.com/Dreamacro/clash/constant"
)
type TLSConfig struct {
@@ -19,7 +18,7 @@ type TLSConfig struct {
Reality *tlsC.RealityConfig
}
-func StreamTLSConn(conn net.Conn, cfg *TLSConfig) (net.Conn, error) {
+func StreamTLSConn(ctx context.Context, conn net.Conn, cfg *TLSConfig) (net.Conn, error) {
tlsConfig := &tls.Config{
ServerName: cfg.Host,
InsecureSkipVerify: cfg.SkipCertVerify,
@@ -39,15 +38,10 @@ func StreamTLSConn(conn net.Conn, cfg *TLSConfig) (net.Conn, error) {
if cfg.Reality == nil {
utlsConn, valid := GetUTLSConn(conn, cfg.ClientFingerprint, tlsConfig)
if valid {
- ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout)
- defer cancel()
-
err := utlsConn.(*tlsC.UConn).HandshakeContext(ctx)
return utlsConn, err
}
} else {
- ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout)
- defer cancel()
return tlsC.GetRealityConn(ctx, conn, cfg.ClientFingerprint, tlsConfig, cfg.Reality)
}
}
@@ -57,9 +51,6 @@ func StreamTLSConn(conn net.Conn, cfg *TLSConfig) (net.Conn, error) {
tlsConn := tls.Client(conn, tlsConfig)
- ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout)
- defer cancel()
-
err := tlsConn.HandshakeContext(ctx)
return tlsConn, err
}
diff --git a/transport/vmess/websocket.go b/transport/vmess/websocket.go
index e7335d84..a4ce99a9 100644
--- a/transport/vmess/websocket.go
+++ b/transport/vmess/websocket.go
@@ -194,17 +194,17 @@ func (wsedc *websocketWithEarlyDataConn) Dial(earlyData []byte) error {
earlyDataBuf := bytes.NewBuffer(earlyData)
if _, err := base64EarlyDataEncoder.Write(earlyDataBuf.Next(wsedc.config.MaxEarlyData)); err != nil {
- return errors.New("failed to encode early data: " + err.Error())
+ return fmt.Errorf("failed to encode early data: %w", err)
}
if errc := base64EarlyDataEncoder.Close(); errc != nil {
- return errors.New("failed to encode early data tail: " + errc.Error())
+ return fmt.Errorf("failed to encode early data tail: %w", errc)
}
var err error
- if wsedc.Conn, err = streamWebsocketConn(wsedc.underlay, wsedc.config, base64DataBuf); err != nil {
+ if wsedc.Conn, err = streamWebsocketConn(wsedc.ctx, wsedc.underlay, wsedc.config, base64DataBuf); err != nil {
wsedc.Close()
- return errors.New("failed to dial WebSocket: " + err.Error())
+ return fmt.Errorf("failed to dial WebSocket: %w", err)
}
wsedc.dialed <- true
@@ -340,7 +340,7 @@ func streamWebsocketWithEarlyDataConn(conn net.Conn, c *WebsocketConfig) (net.Co
return N.NewDeadlineConn(conn), nil
}
-func streamWebsocketConn(conn net.Conn, c *WebsocketConfig, earlyData *bytes.Buffer) (net.Conn, error) {
+func streamWebsocketConn(ctx context.Context, conn net.Conn, c *WebsocketConfig, earlyData *bytes.Buffer) (net.Conn, error) {
dialer := &websocket.Dialer{
NetDial: func(network, addr string) (net.Conn, error) {
@@ -396,13 +396,13 @@ func streamWebsocketConn(conn net.Conn, c *WebsocketConfig, earlyData *bytes.Buf
}
}
- wsConn, resp, err := dialer.Dial(uri.String(), headers)
+ wsConn, resp, err := dialer.DialContext(ctx, uri.String(), headers)
if err != nil {
- reason := err.Error()
+ reason := err
if resp != nil {
- reason = resp.Status
+ reason = errors.New(resp.Status)
}
- return nil, fmt.Errorf("dial %s error: %s", uri.Host, reason)
+ return nil, fmt.Errorf("dial %s error: %w", uri.Host, reason)
}
conn = &websocketConn{
@@ -417,7 +417,7 @@ func streamWebsocketConn(conn net.Conn, c *WebsocketConfig, earlyData *bytes.Buf
return N.NewDeadlineConn(conn), nil
}
-func StreamWebsocketConn(conn net.Conn, c *WebsocketConfig) (net.Conn, error) {
+func StreamWebsocketConn(ctx context.Context, conn net.Conn, c *WebsocketConfig) (net.Conn, error) {
if u, err := url.Parse(c.Path); err == nil {
if q := u.Query(); q.Get("ed") != "" {
if ed, err := strconv.Atoi(q.Get("ed")); err == nil {
@@ -434,5 +434,5 @@ func StreamWebsocketConn(conn net.Conn, c *WebsocketConfig) (net.Conn, error) {
return streamWebsocketWithEarlyDataConn(conn, c)
}
- return streamWebsocketConn(conn, c, nil)
+ return streamWebsocketConn(ctx, conn, c, nil)
}
diff --git a/tunnel/connection.go b/tunnel/connection.go
index c64a5266..b130f79a 100644
--- a/tunnel/connection.go
+++ b/tunnel/connection.go
@@ -7,7 +7,6 @@ import (
"time"
N "github.com/Dreamacro/clash/common/net"
- "github.com/Dreamacro/clash/common/pool"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/log"
)
@@ -27,34 +26,43 @@ func handleUDPToRemote(packet C.UDPPacket, pc C.PacketConn, metadata *C.Metadata
return nil
}
-func handleUDPToLocal(packet C.UDPPacket, pc net.PacketConn, key string, oAddr, fAddr netip.Addr) {
- buf := pool.Get(pool.UDPBufferSize)
+func handleUDPToLocal(packet C.UDPPacket, pc N.EnhancePacketConn, key string, oAddrPort netip.AddrPort, fAddr netip.Addr) {
defer func() {
_ = pc.Close()
closeAllLocalCoon(key)
natTable.Delete(key)
- _ = pool.Put(buf)
}()
for {
_ = pc.SetReadDeadline(time.Now().Add(udpTimeout))
- n, from, err := pc.ReadFrom(buf)
+ data, put, from, err := pc.WaitReadFrom()
if err != nil {
return
}
- fromUDPAddr := from.(*net.UDPAddr)
- _fromUDPAddr := *fromUDPAddr
- fromUDPAddr = &_fromUDPAddr // make a copy
- if fromAddr, ok := netip.AddrFromSlice(fromUDPAddr.IP); ok {
- if fAddr.IsValid() && (oAddr.Unmap() == fromAddr.Unmap()) {
- fromUDPAddr.IP = fAddr.Unmap().AsSlice()
- } else {
- fromUDPAddr.IP = fromAddr.Unmap().AsSlice()
+ fromUDPAddr, isUDPAddr := from.(*net.UDPAddr)
+ if isUDPAddr {
+ _fromUDPAddr := *fromUDPAddr
+ fromUDPAddr = &_fromUDPAddr // make a copy
+ if fromAddr, ok := netip.AddrFromSlice(fromUDPAddr.IP); ok {
+ fromAddr = fromAddr.Unmap()
+ if fAddr.IsValid() && (oAddrPort.Addr() == fromAddr) { // oAddrPort was Unmapped
+ fromAddr = fAddr.Unmap()
+ }
+ fromUDPAddr.IP = fromAddr.AsSlice()
+ if fromAddr.Is4() {
+ fromUDPAddr.Zone = "" // only ipv6 can have the zone
+ }
}
+ } else {
+ fromUDPAddr = net.UDPAddrFromAddrPort(oAddrPort) // oAddrPort was Unmapped
+ log.Warnln("server return a [%T](%s) which isn't a *net.UDPAddr, force replace to (%s), this may be caused by a wrongly implemented server", from, from, oAddrPort)
}
- _, err = packet.WriteBack(buf[:n], fromUDPAddr)
+ _, err = packet.WriteBack(data, fromUDPAddr)
+ if put != nil {
+ put()
+ }
if err != nil {
return
}
diff --git a/tunnel/statistic/tracker.go b/tunnel/statistic/tracker.go
index 170cbc99..a2a921ac 100644
--- a/tunnel/statistic/tracker.go
+++ b/tunnel/statistic/tracker.go
@@ -186,6 +186,16 @@ func (ut *udpTracker) ReadFrom(b []byte) (int, net.Addr, error) {
return n, addr, err
}
+func (ut *udpTracker) WaitReadFrom() (data []byte, put func(), addr net.Addr, err error) {
+ data, put, addr, err = ut.PacketConn.WaitReadFrom()
+ download := int64(len(data))
+ if ut.pushToManager {
+ ut.manager.PushDownloaded(download)
+ }
+ ut.DownloadTotal.Add(download)
+ return
+}
+
func (ut *udpTracker) WriteTo(b []byte, addr net.Addr) (int, error) {
n, err := ut.PacketConn.WriteTo(b, addr)
upload := int64(n)
@@ -201,6 +211,10 @@ func (ut *udpTracker) Close() error {
return ut.PacketConn.Close()
}
+func (ut *udpTracker) Upstream() any {
+ return ut.PacketConn
+}
+
func NewUDPTracker(conn C.PacketConn, manager *Manager, metadata *C.Metadata, rule C.Rule, uploadTotal int64, downloadTotal int64, pushToManager bool) *udpTracker {
metadata.RemoteDst = parseRemoteDestination(nil, conn)
diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go
index a4a473e9..4e00aca2 100644
--- a/tunnel/tunnel.go
+++ b/tunnel/tunnel.go
@@ -383,10 +383,10 @@ func handleUDPConn(packet C.PacketAdapter) {
log.Infoln("[UDP] %s --> %s doesn't match any rule using DIRECT", metadata.SourceDetail(), metadata.RemoteAddress())
}
- oAddr := metadata.DstIP
+ oAddrPort := metadata.AddrPort()
natTable.Set(key, pc)
- go handleUDPToLocal(packet, pc, key, oAddr, fAddr)
+ go handleUDPToLocal(packet, pc, key, oAddrPort, fAddr)
handle()
}()