Update On Tue Feb 18 19:34:31 CET 2025

This commit is contained in:
github-action[bot]
2025-02-18 19:34:31 +01:00
parent cdebca750b
commit 5cd1918265
216 changed files with 3614 additions and 4597 deletions

View File

@@ -54,7 +54,6 @@ jobs:
- { goos: windows, goarch: '386', output: '386' }
- { goos: windows, goarch: amd64, goamd64: v1, output: amd64-compatible }
- { goos: windows, goarch: amd64, goamd64: v3, output: amd64 }
- { goos: windows, goarch: arm, goarm: '7', output: armv7 }
- { goos: windows, goarch: arm64, output: arm64 }
- { goos: freebsd, goarch: '386', output: '386' }
@@ -67,6 +66,12 @@ jobs:
- { goos: android, goarch: arm, ndk: armv7a-linux-androideabi34, output: armv7 }
- { goos: android, goarch: arm64, ndk: aarch64-linux-android34, output: arm64-v8 }
# Go 1.23 with special patch can work on Windows 7
# https://github.com/MetaCubeX/go/commits/release-branch.go1.23/
- { goos: windows, goarch: '386', output: '386-go123', goversion: '1.23' }
- { goos: windows, goarch: amd64, goamd64: v1, output: amd64-compatible-go123, goversion: '1.23' }
- { goos: windows, goarch: amd64, goamd64: v3, output: amd64-go123, goversion: '1.23' }
# Go 1.22 with special patch can work on Windows 7
# https://github.com/MetaCubeX/go/commits/release-branch.go1.22/
- { goos: windows, goarch: '386', output: '386-go122', goversion: '1.22' }
@@ -95,6 +100,11 @@ jobs:
- { goos: darwin, goarch: amd64, goamd64: v1, output: amd64-compatible-go120, goversion: '1.20' }
- { goos: darwin, goarch: amd64, goamd64: v3, output: amd64-go120, goversion: '1.20' }
# Go 1.23 is the last release that requires Linux kernel version 2.6.32 or later. Go 1.24 will require Linux kernel version 3.2 or later.
- { goos: linux, goarch: '386', output: '386-go123', goversion: '1.23' }
- { goos: linux, goarch: amd64, goamd64: v1, output: amd64-compatible-go123, goversion: '1.23', test: test }
- { goos: linux, goarch: amd64, goamd64: v3, output: amd64-go123, goversion: '1.23' }
# only for test
- { goos: linux, goarch: '386', output: '386-go120', goversion: '1.20' }
- { goos: linux, goarch: amd64, goamd64: v1, output: amd64-compatible-go120, goversion: '1.20', test: test }
@@ -107,7 +117,7 @@ jobs:
if: ${{ matrix.jobs.goversion == '' && matrix.jobs.abi != '1' }}
uses: actions/setup-go@v5
with:
go-version: '1.23'
go-version: '1.24'
- name: Set up Go
if: ${{ matrix.jobs.goversion != '' && matrix.jobs.abi != '1' }}
@@ -122,6 +132,24 @@ jobs:
sudo tar zxf go1.23.0.linux-amd64-abi1.tar.gz -C /usr/local
echo "/usr/local/go/bin" >> $GITHUB_PATH
# modify from https://github.com/restic/restic/issues/4636#issuecomment-1896455557
# this patch file only works on golang1.24.x
# that means after golang1.25 release it must be changed
# see: https://github.com/MetaCubeX/go/commits/release-branch.go1.24/
# revert:
# 693def151adff1af707d82d28f55dba81ceb08e1: "crypto/rand,runtime: switch RtlGenRandom for ProcessPrng"
# 7c1157f9544922e96945196b47b95664b1e39108: "net: remove sysSocket fallback for Windows 7"
# 48042aa09c2f878c4faa576948b07fe625c4707a: "syscall: remove Windows 7 console handle workaround"
# a17d959debdb04cd550016a3501dd09d50cd62e7: "runtime: always use LoadLibraryEx to load system libraries"
- name: Revert Golang1.24 commit for Windows7/8
if: ${{ matrix.jobs.goos == 'windows' && matrix.jobs.goversion == '' }}
run: |
cd $(go env GOROOT)
curl https://github.com/MetaCubeX/go/commit/2a406dc9f1ea7323d6ca9fccb2fe9ddebb6b1cc8.diff | patch --verbose -p 1
curl https://github.com/MetaCubeX/go/commit/7b1fd7d39c6be0185fbe1d929578ab372ac5c632.diff | patch --verbose -p 1
curl https://github.com/MetaCubeX/go/commit/979d6d8bab3823ff572ace26767fd2ce3cf351ae.diff | patch --verbose -p 1
curl https://github.com/MetaCubeX/go/commit/ac3e93c061779dfefc0dd13a5b6e6f764a25621e.diff | patch --verbose -p 1
# modify from https://github.com/restic/restic/issues/4636#issuecomment-1896455557
# this patch file only works on golang1.23.x
# that means after golang1.24 release it must be changed
@@ -132,7 +160,7 @@ jobs:
# 48042aa09c2f878c4faa576948b07fe625c4707a: "syscall: remove Windows 7 console handle workaround"
# a17d959debdb04cd550016a3501dd09d50cd62e7: "runtime: always use LoadLibraryEx to load system libraries"
- name: Revert Golang1.23 commit for Windows7/8
if: ${{ matrix.jobs.goos == 'windows' && matrix.jobs.goversion == '' }}
if: ${{ matrix.jobs.goos == 'windows' && matrix.jobs.goversion == '1.23' }}
run: |
cd $(go env GOROOT)
curl https://github.com/MetaCubeX/go/commit/9ac42137ef6730e8b7daca016ece831297a1d75b.diff | patch --verbose -p 1

View File

@@ -0,0 +1,138 @@
package outbound
import (
"context"
"errors"
"net"
"runtime"
"strconv"
"time"
CN "github.com/metacubex/mihomo/common/net"
"github.com/metacubex/mihomo/component/dialer"
"github.com/metacubex/mihomo/component/proxydialer"
"github.com/metacubex/mihomo/component/resolver"
tlsC "github.com/metacubex/mihomo/component/tls"
C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/transport/anytls"
"github.com/metacubex/mihomo/transport/vmess"
M "github.com/sagernet/sing/common/metadata"
"github.com/sagernet/sing/common/uot"
)
type AnyTLS struct {
*Base
client *anytls.Client
dialer proxydialer.SingDialer
option *AnyTLSOption
}
type AnyTLSOption struct {
BasicOption
Name string `proxy:"name"`
Server string `proxy:"server"`
Port int `proxy:"port"`
Password string `proxy:"password"`
ALPN []string `proxy:"alpn,omitempty"`
SNI string `proxy:"sni,omitempty"`
ClientFingerprint string `proxy:"client-fingerprint,omitempty"`
SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"`
Fingerprint string `proxy:"fingerprint,omitempty"`
UDP bool `proxy:"udp,omitempty"`
IdleSessionCheckInterval int `proxy:"idle-session-check-interval,omitempty"`
IdleSessionTimeout int `proxy:"idle-session-timeout,omitempty"`
}
func (t *AnyTLS) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) {
options := t.Base.DialOptions(opts...)
t.dialer.SetDialer(dialer.NewDialer(options...))
c, err := t.client.CreateProxy(ctx, M.ParseSocksaddrHostPort(metadata.String(), metadata.DstPort))
if err != nil {
return nil, err
}
return NewConn(CN.NewRefConn(c, t), t), nil
}
func (t *AnyTLS) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) {
// create tcp
options := t.Base.DialOptions(opts...)
t.dialer.SetDialer(dialer.NewDialer(options...))
c, err := t.client.CreateProxy(ctx, uot.RequestDestination(2))
if err != nil {
return nil, err
}
// create uot on tcp
if !metadata.Resolved() {
ip, err := resolver.ResolveIP(ctx, metadata.Host)
if err != nil {
return nil, errors.New("can't resolve ip")
}
metadata.DstIP = ip
}
destination := M.SocksaddrFromNet(metadata.UDPAddr())
return newPacketConn(CN.NewRefPacketConn(CN.NewThreadSafePacketConn(uot.NewLazyConn(c, uot.Request{Destination: destination})), t), t), nil
}
// SupportUOT implements C.ProxyAdapter
func (t *AnyTLS) SupportUOT() bool {
return true
}
// ProxyInfo implements C.ProxyAdapter
func (t *AnyTLS) ProxyInfo() C.ProxyInfo {
info := t.Base.ProxyInfo()
info.DialerProxy = t.option.DialerProxy
return info
}
func NewAnyTLS(option AnyTLSOption) (*AnyTLS, error) {
addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port))
singDialer := proxydialer.NewByNameSingDialer(option.DialerProxy, dialer.NewDialer())
tOption := anytls.ClientConfig{
Password: option.Password,
Server: M.ParseSocksaddrHostPort(option.Server, uint16(option.Port)),
Dialer: singDialer,
IdleSessionCheckInterval: time.Duration(option.IdleSessionCheckInterval) * time.Second,
IdleSessionTimeout: time.Duration(option.IdleSessionTimeout) * time.Second,
}
tlsConfig := &vmess.TLSConfig{
Host: option.SNI,
SkipCertVerify: option.SkipCertVerify,
NextProtos: option.ALPN,
FingerPrint: option.Fingerprint,
ClientFingerprint: option.ClientFingerprint,
}
if tlsConfig.Host == "" {
tlsConfig.Host = option.Server
}
if tlsC.HaveGlobalFingerprint() && len(option.ClientFingerprint) == 0 {
tlsConfig.ClientFingerprint = tlsC.GetGlobalFingerprint()
}
tOption.TLSConfig = tlsConfig
outbound := &AnyTLS{
Base: &Base{
name: option.Name,
addr: addr,
tp: C.AnyTLS,
udp: option.UDP,
tfo: option.TFO,
mpTcp: option.MPTCP,
iface: option.Interface,
rmark: option.RoutingMark,
prefer: C.NewDNSPrefer(option.IPVersion),
},
client: anytls.NewClient(context.TODO(), tOption),
option: &option,
dialer: singDialer,
}
runtime.SetFinalizer(outbound, func(o *AnyTLS) {
_ = o.client.Close()
})
return outbound, nil
}

View File

@@ -148,6 +148,13 @@ func ParseProxy(mapping map[string]any) (C.Proxy, error) {
break
}
proxy, err = outbound.NewMieru(*mieruOption)
case "anytls":
anytlsOption := &outbound.AnyTLSOption{}
err = decoder.Decode(mapping, anytlsOption)
if err != nil {
break
}
proxy, err = outbound.NewAnyTLS(*anytlsOption)
default:
return nil, fmt.Errorf("unsupport proxy type: %s", proxyType)
}

View File

@@ -43,6 +43,7 @@ const (
Tuic
Ssh
Mieru
AnyTLS
)
const (
@@ -229,6 +230,8 @@ func (at AdapterType) String() string {
return "Ssh"
case Mieru:
return "Mieru"
case AnyTLS:
return "AnyTLS"
case Relay:
return "Relay"
case Selector:

View File

@@ -32,6 +32,7 @@ const (
TUN
TUIC
HYSTERIA2
ANYTLS
INNER
)
@@ -84,6 +85,8 @@ func (t Type) String() string {
return "Tuic"
case HYSTERIA2:
return "Hysteria2"
case ANYTLS:
return "AnyTLS"
case INNER:
return "Inner"
default:
@@ -120,6 +123,8 @@ func ParseType(t string) (*Type, error) {
res = TUIC
case "HYSTERIA2":
res = HYSTERIA2
case "ANYTLS":
res = ANYTLS
case "INNER":
res = INNER
default:

View File

@@ -61,7 +61,7 @@ external-controller-tls: 0.0.0.0:9443 # RESTful API HTTPS 监听地址,需要
# RESTful API CORS标头配置
external-controller-cors:
allow-origins:
- *
- "*"
allow-private-network: true
# RESTful API Unix socket 监听地址( windows版本大于17063也可以使用即大于等于1803/RS4版本即可使用
@@ -78,6 +78,7 @@ external-controller-pipe: \\.\pipe\mihomo
# 配置 WEB UI 目录,使用 http://{{external-controller}}/ui 访问
external-ui: /path/to/ui/folder/
external-ui-name: xd
# 目前支持下载zip,tgz格式的压缩包
external-ui-url: "https://github.com/MetaCubeX/metacubexd/archive/refs/heads/gh-pages.zip"
# 在RESTful API端口上开启DOH服务器
@@ -863,6 +864,22 @@ proxies: # socks5
# 可以使用的值包括 MULTIPLEXING_OFF, MULTIPLEXING_LOW, MULTIPLEXING_MIDDLE, MULTIPLEXING_HIGH。其中 MULTIPLEXING_OFF 会关闭多路复用功能。默认值为 MULTIPLEXING_LOW。
# multiplexing: MULTIPLEXING_LOW
# anytls
- name: anytls
type: anytls
server: 1.2.3.4
port: 443
password: "<your password>"
# client-fingerprint: chrome
udp: true
# idle-session-check-interval: 30 # seconds
# idle-session-timeout: 30 # seconds
# sni: "example.com"
# alpn:
# - h2
# - http/1.1
# skip-cert-verify: true
# dns 出站会将请求劫持到内部 dns 模块,所有请求均在内部处理
- name: "dns-out"
type: dns
@@ -1208,6 +1225,18 @@ listeners:
- test.com
### 注意对于vless listener, 至少需要填写 “certificate和private-key” 或 “reality-config” 的其中一项 ###
- name: anytls-in-1
type: anytls
port: 10818
listen: 0.0.0.0
users:
username1: password1
username2: password2
# "certificate" and "private-key" are required
certificate: ./server.crt
private-key: ./server.key
# padding-scheme: "" # https://github.com/anytls/anytls-go/blob/main/docs/protocol.md#cmdupdatepaddingscheme
- name: tun-in-1
type: tun
# rule: sub-rule-name1 # 默认使用 rules如果未找到 sub-rule 则直接使用 rules

View File

@@ -6,12 +6,12 @@ require (
github.com/3andne/restls-client-go v0.1.6
github.com/bahlo/generic-list-go v0.2.0
github.com/coreos/go-iptables v0.8.0
github.com/dlclark/regexp2 v1.11.4
github.com/enfein/mieru/v3 v3.11.1
github.com/go-chi/chi/v5 v5.2.0
github.com/dlclark/regexp2 v1.11.5
github.com/enfein/mieru/v3 v3.11.2
github.com/go-chi/chi/v5 v5.2.1
github.com/go-chi/render v1.0.3
github.com/gobwas/ws v1.4.0
github.com/gofrs/uuid/v5 v5.3.0
github.com/gofrs/uuid/v5 v5.3.1
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905
github.com/klauspost/compress v1.17.9 // lastest version compatible with golang1.20
github.com/klauspost/cpuid/v2 v2.2.9
@@ -19,9 +19,9 @@ require (
github.com/mdlayher/netlink v1.7.2
github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab
github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399
github.com/metacubex/chacha v0.1.0
github.com/metacubex/chacha v0.1.1
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759
github.com/metacubex/quic-go v0.48.3-0.20241126053724-b69fea3888da
github.com/metacubex/quic-go v0.49.1-0.20250212162123-c135a4412996
github.com/metacubex/randv2 v0.2.0
github.com/metacubex/sing-quic v0.0.0-20250119013740-2a19cce83925
github.com/metacubex/sing-shadowsocks v0.2.8
@@ -53,10 +53,10 @@ require (
gitlab.com/go-extension/aes-ccm v0.0.0-20230221065045-e58665ef23c7
go.uber.org/automaxprocs v1.6.0
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
golang.org/x/crypto v0.32.0
golang.org/x/crypto v0.33.0
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e // lastest version compatible with golang1.20
golang.org/x/net v0.34.0
golang.org/x/sys v0.29.0
golang.org/x/net v0.35.0
golang.org/x/sys v0.30.0
google.golang.org/protobuf v1.34.2 // lastest version compatible with golang1.20
gopkg.in/yaml.v3 v3.0.1
lukechampine.com/blake3 v1.3.0
@@ -111,8 +111,8 @@ require (
gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect
go.uber.org/mock v0.4.0 // indirect
golang.org/x/mod v0.20.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/sync v0.11.0 // indirect
golang.org/x/text v0.22.0 // indirect
golang.org/x/time v0.7.0 // indirect
golang.org/x/tools v0.24.0 // indirect
)

View File

@@ -24,12 +24,12 @@ github.com/coreos/go-iptables v0.8.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFE
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.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo=
github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=
github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/enfein/mieru/v3 v3.11.1 h1:G6PjSWPvzvgWUoMqnD4sBe1OGkDgrpNVT2UqCQhE6SE=
github.com/enfein/mieru/v3 v3.11.1/go.mod h1:XvVfNsM78lUMSlJJKXJZ0Hn3lAB2o/ETXTbb84x5egw=
github.com/enfein/mieru/v3 v3.11.2 h1:06KyGbXiiGz2nSHLJDOOkztAVY3cRr3wBMOpYxPotTo=
github.com/enfein/mieru/v3 v3.11.2/go.mod h1:XvVfNsM78lUMSlJJKXJZ0Hn3lAB2o/ETXTbb84x5egw=
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=
@@ -43,8 +43,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/gaukas/godicttls v0.0.4 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXbk=
github.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI=
github.com/go-chi/chi/v5 v5.2.0 h1:Aj1EtB0qR2Rdo2dG4O94RIU35w2lvQSj6BRA4+qwFL0=
github.com/go-chi/chi/v5 v5.2.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8=
github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
@@ -59,8 +59,8 @@ github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.4.0 h1:CTaoG1tojrh4ucGPcoJFiAQUAsEWekEWvLy7GsVNqGs=
github.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakrc=
github.com/gofrs/uuid/v5 v5.3.0 h1:m0mUMr+oVYUdxpMLgSYCZiXe7PuVPnI94+OMeVBNedk=
github.com/gofrs/uuid/v5 v5.3.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
github.com/gofrs/uuid/v5 v5.3.1 h1:aPx49MwJbekCzOyhZDjJVb0hx3A0KLjlbLx6p2gY0p0=
github.com/gofrs/uuid/v5 v5.3.1/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
@@ -99,14 +99,14 @@ github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab h1:Chbw+/31
github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab/go.mod h1:xVKK8jC5Sd3hfh7WjmCq+HorehIbrBijaUWmcuKjPcI=
github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399 h1:oBowHVKZycNtAFbZ6avaCSZJYeme2Nrj+4RpV2cNJig=
github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399/go.mod h1:4xcieuIK+M4bGQmQYZVqEaIYqjS1ahO4kXG7EmDgEro=
github.com/metacubex/chacha v0.1.0 h1:tg9RSJ18NvL38cCWNyYH1eiG6qDCyyXIaTLQthon0sc=
github.com/metacubex/chacha v0.1.0/go.mod h1:Djn9bPZxLTXbJFSeyo0/qzEzQI+gUSSzttuzZM75GH8=
github.com/metacubex/chacha v0.1.1 h1:OHIv11Nd9CISAIzegpjfupIoZp9DYm6uQw41RxvmU/c=
github.com/metacubex/chacha v0.1.1/go.mod h1:Djn9bPZxLTXbJFSeyo0/qzEzQI+gUSSzttuzZM75GH8=
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 h1:cjd4biTvOzK9ubNCCkQ+ldc4YSH/rILn53l/xGBFHHI=
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759/go.mod h1:UHOv2xu+RIgLwpXca7TLrXleEd4oR3sPatW6IF8wU88=
github.com/metacubex/gvisor v0.0.0-20241126021258-5b028898cc5a h1:cZ6oNVrsmsi3SNlnSnRio4zOgtQq+/XidwsaNgKICcg=
github.com/metacubex/gvisor v0.0.0-20241126021258-5b028898cc5a/go.mod h1:xBw/SYJPgUMPQ1tklV/brGn2nxhfr3BnvBzNlyi4Nic=
github.com/metacubex/quic-go v0.48.3-0.20241126053724-b69fea3888da h1:Mq6cbHbPTLLTUfA9scrwBmOGkvl6y99E3WmtMIMqo30=
github.com/metacubex/quic-go v0.48.3-0.20241126053724-b69fea3888da/go.mod h1:AiZ+UPgrkO1DTnmiAX4b+kRoV1Vfc65UkYD7RbFlIZA=
github.com/metacubex/quic-go v0.49.1-0.20250212162123-c135a4412996 h1:B+AP/Pj2/jBDS/kCYjz/x+0BCOKfd2VODYevyeIt+Ds=
github.com/metacubex/quic-go v0.49.1-0.20250212162123-c135a4412996/go.mod h1:ExVjGyEwTUjCFqx+5uxgV7MOoA3fZI+th4D40H35xmY=
github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs=
github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY=
github.com/metacubex/sing v0.0.0-20241121030428-33b6ebc52000 h1:gUbMXcQXhXGj0vCpCVFTUyIH7TMpD1dpTcNv/MCS+ok=
@@ -232,8 +232,8 @@ go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBs
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
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.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e h1:I88y4caeGeuDQxgdoFPUq097j7kNfw6uvuiNxUBfcBk=
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
@@ -242,11 +242,11 @@ golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
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.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
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-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -262,12 +262,12 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=

View File

@@ -0,0 +1,182 @@
package anytls
import (
"context"
"crypto/sha256"
"crypto/tls"
"encoding/binary"
"errors"
"net"
"strings"
"github.com/metacubex/mihomo/adapter/inbound"
"github.com/metacubex/mihomo/common/atomic"
"github.com/metacubex/mihomo/common/buf"
N "github.com/metacubex/mihomo/common/net"
C "github.com/metacubex/mihomo/constant"
LC "github.com/metacubex/mihomo/listener/config"
"github.com/metacubex/mihomo/listener/sing"
"github.com/metacubex/mihomo/transport/anytls/padding"
"github.com/metacubex/mihomo/transport/anytls/session"
"github.com/sagernet/sing/common/auth"
"github.com/sagernet/sing/common/bufio"
M "github.com/sagernet/sing/common/metadata"
)
type Listener struct {
closed bool
config LC.AnyTLSServer
listeners []net.Listener
tlsConfig *tls.Config
userMap map[[32]byte]string
padding atomic.TypedValue[*padding.PaddingFactory]
}
func New(config LC.AnyTLSServer, tunnel C.Tunnel, additions ...inbound.Addition) (sl *Listener, err error) {
if len(additions) == 0 {
additions = []inbound.Addition{
inbound.WithInName("DEFAULT-ANYTLS"),
inbound.WithSpecialRules(""),
}
}
tlsConfig := &tls.Config{}
if config.Certificate != "" && config.PrivateKey != "" {
cert, err := N.ParseCert(config.Certificate, config.PrivateKey, C.Path)
if err != nil {
return nil, err
}
tlsConfig.Certificates = []tls.Certificate{cert}
}
sl = &Listener{
config: config,
tlsConfig: tlsConfig,
userMap: make(map[[32]byte]string),
}
for user, password := range config.Users {
sl.userMap[sha256.Sum256([]byte(password))] = user
}
if len(config.PaddingScheme) > 0 {
if !padding.UpdatePaddingScheme([]byte(config.PaddingScheme), &sl.padding) {
return nil, errors.New("incorrect padding scheme format")
}
} else {
padding.UpdatePaddingScheme(padding.DefaultPaddingScheme, &sl.padding)
}
// Using sing handler can automatically handle UoT
h, err := sing.NewListenerHandler(sing.ListenerConfig{
Tunnel: tunnel,
Type: C.ANYTLS,
Additions: additions,
})
if err != nil {
return nil, err
}
for _, addr := range strings.Split(config.Listen, ",") {
addr := addr
//TCP
l, err := inbound.Listen("tcp", addr)
if err != nil {
return nil, err
}
sl.listeners = append(sl.listeners, l)
go func() {
for {
c, err := l.Accept()
if err != nil {
if sl.closed {
break
}
continue
}
go sl.HandleConn(c, h)
}
}()
}
return sl, nil
}
func (l *Listener) Close() error {
l.closed = true
var retErr error
for _, lis := range l.listeners {
err := lis.Close()
if err != nil {
retErr = err
}
}
return retErr
}
func (l *Listener) Config() string {
return l.config.String()
}
func (l *Listener) AddrList() (addrList []net.Addr) {
for _, lis := range l.listeners {
addrList = append(addrList, lis.Addr())
}
return
}
func (l *Listener) HandleConn(conn net.Conn, h *sing.ListenerHandler) {
ctx := context.TODO()
conn = tls.Server(conn, l.tlsConfig)
defer conn.Close()
b := buf.NewPacket()
_, err := b.ReadOnceFrom(conn)
if err != nil {
return
}
conn = bufio.NewCachedConn(conn, b)
by, err := b.ReadBytes(32)
if err != nil {
return
}
var passwordSha256 [32]byte
copy(passwordSha256[:], by)
if user, ok := l.userMap[passwordSha256]; ok {
ctx = auth.ContextWithUser(ctx, user)
} else {
return
}
by, err = b.ReadBytes(2)
if err != nil {
return
}
paddingLen := binary.BigEndian.Uint16(by)
if paddingLen > 0 {
_, err = b.ReadBytes(int(paddingLen))
if err != nil {
return
}
}
session := session.NewServerSession(conn, func(stream *session.Stream) {
defer stream.Close()
destination, err := M.SocksaddrSerializer.ReadAddrPort(stream)
if err != nil {
return
}
h.NewConnection(ctx, stream, M.Metadata{
Source: M.SocksaddrFromNet(conn.RemoteAddr()),
Destination: destination,
})
}, &l.padding)
session.Run(true)
session.Close()
}

View File

@@ -0,0 +1,19 @@
package config
import (
"encoding/json"
)
type AnyTLSServer struct {
Enable bool `yaml:"enable" json:"enable"`
Listen string `yaml:"listen" json:"listen"`
Users map[string]string `yaml:"users" json:"users,omitempty"`
Certificate string `yaml:"certificate" json:"certificate"`
PrivateKey string `yaml:"private-key" json:"private-key"`
PaddingScheme string `yaml:"padding-scheme" json:"padding-scheme,omitempty"`
}
func (t AnyTLSServer) String() string {
b, _ := json.Marshal(t)
return string(b)
}

View File

@@ -0,0 +1,79 @@
package inbound
import (
C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/listener/anytls"
LC "github.com/metacubex/mihomo/listener/config"
"github.com/metacubex/mihomo/log"
)
type AnyTLSOption struct {
BaseOption
Users map[string]string `inbound:"users,omitempty"`
Certificate string `inbound:"certificate"`
PrivateKey string `inbound:"private-key"`
PaddingScheme string `inbound:"padding-scheme,omitempty"`
}
func (o AnyTLSOption) Equal(config C.InboundConfig) bool {
return optionToString(o) == optionToString(config)
}
type AnyTLS struct {
*Base
config *AnyTLSOption
l C.MultiAddrListener
vs LC.AnyTLSServer
}
func NewAnyTLS(options *AnyTLSOption) (*AnyTLS, error) {
base, err := NewBase(&options.BaseOption)
if err != nil {
return nil, err
}
return &AnyTLS{
Base: base,
config: options,
vs: LC.AnyTLSServer{
Enable: true,
Listen: base.RawAddress(),
Users: options.Users,
Certificate: options.Certificate,
PrivateKey: options.PrivateKey,
PaddingScheme: options.PaddingScheme,
},
}, nil
}
// Config implements constant.InboundListener
func (v *AnyTLS) Config() C.InboundConfig {
return v.config
}
// Address implements constant.InboundListener
func (v *AnyTLS) Address() string {
if v.l != nil {
for _, addr := range v.l.AddrList() {
return addr.String()
}
}
return ""
}
// Listen implements constant.InboundListener
func (v *AnyTLS) Listen(tunnel C.Tunnel) error {
var err error
v.l, err = anytls.New(v.vs, tunnel, v.Additions()...)
if err != nil {
return err
}
log.Infoln("AnyTLS[%s] proxy listening at: %s", v.Name(), v.Address())
return nil
}
// Close implements constant.InboundListener
func (v *AnyTLS) Close() error {
return v.l.Close()
}
var _ C.InboundListener = (*AnyTLS)(nil)

View File

@@ -113,6 +113,13 @@ func ParseListener(mapping map[string]any) (C.InboundListener, error) {
return nil, err
}
listener, err = IN.NewTuic(tuicOption)
case "anytls":
anytlsOption := &IN.AnyTLSOption{}
err = decoder.Decode(mapping, anytlsOption)
if err != nil {
return nil, err
}
listener, err = IN.NewAnyTLS(anytlsOption)
default:
return nil, fmt.Errorf("unsupport proxy type: %s", proxyType)
}

View File

@@ -81,8 +81,10 @@ func ParseRule(tp, payload, target string, params []string, subRules map[string]
case "MATCH":
parsed = RC.NewMatch(target)
parseErr = nil
case "":
parseErr = fmt.Errorf("missing subsequent parameters: %s", payload)
default:
parseErr = fmt.Errorf("unsupported rule type %s", tp)
parseErr = fmt.Errorf("unsupported rule type: %s", tp)
}
if parseErr != nil {

View File

@@ -59,7 +59,7 @@ func (c *classicalStrategy) Insert(rule string) {
r, err := c.parse(ruleType, rule, "", params)
if err != nil {
log.Warnln("parse rule error:[%s]", err.Error())
log.Warnln("parse classical rule error: %s", err.Error())
} else {
if r.ShouldResolveIP() {
c.shouldResolveIP = true
@@ -83,7 +83,7 @@ func ruleParse(ruleRaw string) (string, string, []string) {
return item[0], item[1], nil
} else if len(item) > 2 {
if item[0] == "NOT" || item[0] == "OR" || item[0] == "AND" || item[0] == "SUB-RULE" || item[0] == "DOMAIN-REGEX" || item[0] == "PROCESS-NAME-REGEX" || item[0] == "PROCESS-PATH-REGEX" {
return item[0], strings.Join(item[1:len(item)], ","), nil
return item[0], strings.Join(item[1:], ","), nil
} else {
return item[0], item[1], item[2:]
}
@@ -95,8 +95,8 @@ func ruleParse(ruleRaw string) (string, string, []string) {
func NewClassicalStrategy(parse func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error)) *classicalStrategy {
return &classicalStrategy{rules: []C.Rule{}, parse: func(tp, payload, target string, params []string) (parsed C.Rule, parseErr error) {
switch tp {
case "MATCH":
return nil, fmt.Errorf("unsupported rule type on rule-set")
case "MATCH", "RULE-SET", "SUB-RULE":
return nil, fmt.Errorf("unsupported rule type on classical rule-set: %s", tp)
default:
return parse(tp, payload, target, params, nil)
}

View File

@@ -0,0 +1,105 @@
package anytls
import (
"context"
"crypto/sha256"
"encoding/binary"
"net"
"time"
"github.com/metacubex/mihomo/common/atomic"
"github.com/metacubex/mihomo/common/buf"
C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/transport/anytls/padding"
"github.com/metacubex/mihomo/transport/anytls/session"
"github.com/metacubex/mihomo/transport/vmess"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
)
type ClientConfig struct {
Password string
IdleSessionCheckInterval time.Duration
IdleSessionTimeout time.Duration
Server M.Socksaddr
Dialer N.Dialer
TLSConfig *vmess.TLSConfig
}
type Client struct {
passwordSha256 []byte
tlsConfig *vmess.TLSConfig
clientFingerprint string
dialer N.Dialer
server M.Socksaddr
sessionClient *session.Client
padding atomic.TypedValue[*padding.PaddingFactory]
}
func NewClient(ctx context.Context, config ClientConfig) *Client {
pw := sha256.Sum256([]byte(config.Password))
c := &Client{
passwordSha256: pw[:],
tlsConfig: config.TLSConfig,
dialer: config.Dialer,
server: config.Server,
}
// Initialize the padding state of this client
padding.UpdatePaddingScheme(padding.DefaultPaddingScheme, &c.padding)
c.sessionClient = session.NewClient(ctx, c.CreateOutboundTLSConnection, &c.padding, config.IdleSessionCheckInterval, config.IdleSessionTimeout)
return c
}
func (c *Client) CreateProxy(ctx context.Context, destination M.Socksaddr) (net.Conn, error) {
conn, err := c.sessionClient.CreateStream(ctx)
if err != nil {
return nil, err
}
err = M.SocksaddrSerializer.WriteAddrPort(conn, destination)
if err != nil {
conn.Close()
return nil, err
}
return conn, nil
}
func (c *Client) CreateOutboundTLSConnection(ctx context.Context) (net.Conn, error) {
conn, err := c.dialer.DialContext(ctx, N.NetworkTCP, c.server)
if err != nil {
return nil, err
}
b := buf.NewPacket()
b.Write(c.passwordSha256)
var paddingLen int
if pad := c.padding.Load().GenerateRecordPayloadSizes(0); len(pad) > 0 {
paddingLen = pad[0]
}
binary.BigEndian.PutUint16(b.Extend(2), uint16(paddingLen))
if paddingLen > 0 {
b.WriteZeroN(paddingLen)
}
getTlsConn := func() (net.Conn, error) {
ctx, cancel := context.WithTimeout(ctx, C.DefaultTLSTimeout)
defer cancel()
return vmess.StreamTLSConn(ctx, conn, c.tlsConfig)
}
tlsConn, err := getTlsConn()
if err != nil {
conn.Close()
return nil, err
}
_, err = b.WriteTo(tlsConn)
if err != nil {
tlsConn.Close()
return nil, err
}
return tlsConn, nil
}
func (h *Client) Close() error {
return h.sessionClient.Close()
}

View File

@@ -0,0 +1,92 @@
package padding
import (
"crypto/md5"
"crypto/rand"
"fmt"
"math/big"
"strconv"
"strings"
"github.com/metacubex/mihomo/common/atomic"
"github.com/metacubex/mihomo/transport/anytls/util"
)
const CheckMark = -1
var DefaultPaddingScheme = []byte(`stop=8
0=34-120
1=100-400
2=400-500,c,500-1000,c,400-500,c,500-1000,c,500-1000,c,400-500
3=500-1000
4=500-1000
5=500-1000
6=500-1000
7=500-1000`)
type PaddingFactory struct {
scheme util.StringMap
RawScheme []byte
Stop uint32
Md5 string
}
func UpdatePaddingScheme(rawScheme []byte, to *atomic.TypedValue[*PaddingFactory]) bool {
if p := NewPaddingFactory(rawScheme); p != nil {
to.Store(p)
return true
}
return false
}
func NewPaddingFactory(rawScheme []byte) *PaddingFactory {
p := &PaddingFactory{
RawScheme: rawScheme,
Md5: fmt.Sprintf("%x", md5.Sum(rawScheme)),
}
scheme := util.StringMapFromBytes(rawScheme)
if len(scheme) == 0 {
return nil
}
if stop, err := strconv.Atoi(scheme["stop"]); err == nil {
p.Stop = uint32(stop)
} else {
return nil
}
p.scheme = scheme
return p
}
func (p *PaddingFactory) GenerateRecordPayloadSizes(pkt uint32) (pktSizes []int) {
if s, ok := p.scheme[strconv.Itoa(int(pkt))]; ok {
sRanges := strings.Split(s, ",")
for _, sRange := range sRanges {
sRangeMinMax := strings.Split(sRange, "-")
if len(sRangeMinMax) == 2 {
_min, err := strconv.ParseInt(sRangeMinMax[0], 10, 64)
if err != nil {
continue
}
_max, err := strconv.ParseInt(sRangeMinMax[1], 10, 64)
if err != nil {
continue
}
if _min > _max {
_min, _max = _max, _min
}
if _min <= 0 || _max <= 0 {
continue
}
if _min == _max {
pktSizes = append(pktSizes, int(_min))
} else {
i, _ := rand.Int(rand.Reader, big.NewInt(_max-_min))
pktSizes = append(pktSizes, int(i.Int64()+_min))
}
} else if sRange == "c" {
pktSizes = append(pktSizes, CheckMark)
}
}
}
return
}

View File

@@ -0,0 +1,158 @@
package session
import (
"context"
"fmt"
"io"
"math"
"net"
"sync"
"time"
"github.com/metacubex/mihomo/common/atomic"
"github.com/metacubex/mihomo/transport/anytls/padding"
"github.com/metacubex/mihomo/transport/anytls/skiplist"
"github.com/metacubex/mihomo/transport/anytls/util"
)
type Client struct {
die context.Context
dieCancel context.CancelFunc
dialOut func(ctx context.Context) (net.Conn, error)
sessionCounter atomic.Uint64
idleSession *skiplist.SkipList[uint64, *Session]
idleSessionLock sync.Mutex
padding *atomic.TypedValue[*padding.PaddingFactory]
idleSessionTimeout time.Duration
}
func NewClient(ctx context.Context, dialOut func(ctx context.Context) (net.Conn, error), _padding *atomic.TypedValue[*padding.PaddingFactory], idleSessionCheckInterval, idleSessionTimeout time.Duration) *Client {
c := &Client{
dialOut: dialOut,
padding: _padding,
idleSessionTimeout: idleSessionTimeout,
}
if idleSessionCheckInterval <= time.Second*5 {
idleSessionCheckInterval = time.Second * 30
}
if c.idleSessionTimeout <= time.Second*5 {
c.idleSessionTimeout = time.Second * 30
}
c.die, c.dieCancel = context.WithCancel(ctx)
c.idleSession = skiplist.NewSkipList[uint64, *Session]()
util.StartRoutine(c.die, idleSessionCheckInterval, c.idleCleanup)
return c
}
func (c *Client) CreateStream(ctx context.Context) (net.Conn, error) {
select {
case <-c.die.Done():
return nil, io.ErrClosedPipe
default:
}
var session *Session
var stream *Stream
var err error
for i := 0; i < 3; i++ {
session, err = c.findSession(ctx)
if session == nil {
return nil, fmt.Errorf("failed to create session: %w", err)
}
stream, err = session.OpenStream()
if err != nil {
_ = session.Close()
continue
}
break
}
if session == nil || stream == nil {
return nil, fmt.Errorf("too many closed session: %w", err)
}
stream.dieHook = func() {
if session.IsClosed() {
if session.dieHook != nil {
session.dieHook()
}
} else {
c.idleSessionLock.Lock()
session.idleSince = time.Now()
c.idleSession.Insert(math.MaxUint64-session.seq, session)
c.idleSessionLock.Unlock()
}
}
return stream, nil
}
func (c *Client) findSession(ctx context.Context) (*Session, error) {
var idle *Session
c.idleSessionLock.Lock()
if !c.idleSession.IsEmpty() {
it := c.idleSession.Iterate()
idle = it.Value()
c.idleSession.Remove(it.Key())
}
c.idleSessionLock.Unlock()
if idle == nil {
s, err := c.createSession(ctx)
return s, err
}
return idle, nil
}
func (c *Client) createSession(ctx context.Context) (*Session, error) {
underlying, err := c.dialOut(ctx)
if err != nil {
return nil, err
}
session := NewClientSession(underlying, c.padding)
session.seq = c.sessionCounter.Add(1)
session.dieHook = func() {
//logrus.Debugln("session died", session)
c.idleSessionLock.Lock()
c.idleSession.Remove(math.MaxUint64 - session.seq)
c.idleSessionLock.Unlock()
}
session.Run(false)
return session, nil
}
func (c *Client) Close() error {
c.dieCancel()
go c.idleCleanupExpTime(time.Now())
return nil
}
func (c *Client) idleCleanup() {
c.idleCleanupExpTime(time.Now().Add(-c.idleSessionTimeout))
}
func (c *Client) idleCleanupExpTime(expTime time.Time) {
var sessionToRemove = make([]*Session, 0)
c.idleSessionLock.Lock()
it := c.idleSession.Iterate()
for it.IsNotEnd() {
session := it.Value()
if session.idleSince.Before(expTime) {
sessionToRemove = append(sessionToRemove, session)
c.idleSession.Remove(it.Key())
}
it.MoveToNext()
}
c.idleSessionLock.Unlock()
for _, session := range sessionToRemove {
session.Close()
}
}

View File

@@ -0,0 +1,44 @@
package session
import (
"encoding/binary"
)
const ( // cmds
cmdWaste = 0 // Paddings
cmdSYN = 1 // stream open
cmdPSH = 2 // data push
cmdFIN = 3 // stream close, a.k.a EOF mark
cmdSettings = 4 // Settings
cmdAlert = 5 // Alert
cmdUpdatePaddingScheme = 6 // update padding scheme
)
const (
headerOverHeadSize = 1 + 4 + 2
)
// frame defines a packet from or to be multiplexed into a single connection
type frame struct {
cmd byte // 1
sid uint32 // 4
data []byte // 2 + len(data)
}
func newFrame(cmd byte, sid uint32) frame {
return frame{cmd: cmd, sid: sid}
}
type rawHeader [headerOverHeadSize]byte
func (h rawHeader) Cmd() byte {
return h[0]
}
func (h rawHeader) StreamID() uint32 {
return binary.BigEndian.Uint32(h[1:])
}
func (h rawHeader) Length() uint16 {
return binary.BigEndian.Uint16(h[5:])
}

View File

@@ -0,0 +1,379 @@
package session
import (
"crypto/md5"
"encoding/binary"
"io"
"net"
"runtime/debug"
"sync"
"time"
"github.com/metacubex/mihomo/common/atomic"
"github.com/metacubex/mihomo/common/buf"
"github.com/metacubex/mihomo/common/pool"
"github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/log"
"github.com/metacubex/mihomo/transport/anytls/padding"
"github.com/metacubex/mihomo/transport/anytls/util"
)
type Session struct {
conn net.Conn
connLock sync.Mutex
streams map[uint32]*Stream
streamId atomic.Uint32
streamLock sync.RWMutex
dieOnce sync.Once
die chan struct{}
dieHook func()
// pool
seq uint64
idleSince time.Time
padding *atomic.TypedValue[*padding.PaddingFactory]
// client
isClient bool
buffering bool
buffer []byte
pktCounter atomic.Uint32
// server
onNewStream func(stream *Stream)
}
func NewClientSession(conn net.Conn, _padding *atomic.TypedValue[*padding.PaddingFactory]) *Session {
s := &Session{
conn: conn,
isClient: true,
padding: _padding,
}
s.die = make(chan struct{})
s.streams = make(map[uint32]*Stream)
return s
}
func NewServerSession(conn net.Conn, onNewStream func(stream *Stream), _padding *atomic.TypedValue[*padding.PaddingFactory]) *Session {
s := &Session{
conn: conn,
onNewStream: onNewStream,
isClient: false,
padding: _padding,
}
s.die = make(chan struct{})
s.streams = make(map[uint32]*Stream)
return s
}
func (s *Session) Run(isServer bool) {
if isServer {
s.recvLoop()
return
}
settings := util.StringMap{
"v": "1",
"client": "mihomo/" + constant.Version,
"padding-md5": s.padding.Load().Md5,
}
f := newFrame(cmdSettings, 0)
f.data = settings.ToBytes()
s.buffering = true
s.writeFrame(f)
go s.recvLoop()
}
// IsClosed does a safe check to see if we have shutdown
func (s *Session) IsClosed() bool {
select {
case <-s.die:
return true
default:
return false
}
}
// Close is used to close the session and all streams.
func (s *Session) Close() error {
var once bool
s.dieOnce.Do(func() {
close(s.die)
once = true
})
if once {
if s.dieHook != nil {
s.dieHook()
}
s.streamLock.Lock()
for k := range s.streams {
s.streams[k].sessionClose()
}
s.streamLock.Unlock()
return s.conn.Close()
} else {
return io.ErrClosedPipe
}
}
// OpenStream is used to create a new stream for CLIENT
func (s *Session) OpenStream() (*Stream, error) {
if s.IsClosed() {
return nil, io.ErrClosedPipe
}
sid := s.streamId.Add(1)
stream := newStream(sid, s)
//logrus.Debugln("stream open", sid, s.streams)
if _, err := s.writeFrame(newFrame(cmdSYN, sid)); err != nil {
return nil, err
}
s.buffering = false // proxy Write it's SocksAddr to flush the buffer
s.streamLock.Lock()
defer s.streamLock.Unlock()
select {
case <-s.die:
return nil, io.ErrClosedPipe
default:
s.streams[sid] = stream
return stream, nil
}
}
func (s *Session) recvLoop() error {
defer func() {
if r := recover(); r != nil {
log.Errorln("[BUG] %v %s", r, string(debug.Stack()))
}
}()
defer s.Close()
var receivedSettingsFromClient bool
var hdr rawHeader
for {
if s.IsClosed() {
return io.ErrClosedPipe
}
// read header first
if _, err := io.ReadFull(s.conn, hdr[:]); err == nil {
sid := hdr.StreamID()
switch hdr.Cmd() {
case cmdPSH:
if hdr.Length() > 0 {
buffer := pool.Get(int(hdr.Length()))
if _, err := io.ReadFull(s.conn, buffer); err == nil {
s.streamLock.RLock()
stream, ok := s.streams[sid]
s.streamLock.RUnlock()
if ok {
stream.pipeW.Write(buffer)
}
pool.Put(buffer)
} else {
pool.Put(buffer)
return err
}
}
case cmdSYN: // should be server only
if !s.isClient && !receivedSettingsFromClient {
f := newFrame(cmdAlert, 0)
f.data = []byte("client did not send its settings")
s.writeFrame(f)
return nil
}
s.streamLock.Lock()
if _, ok := s.streams[sid]; !ok {
stream := newStream(sid, s)
s.streams[sid] = stream
if s.onNewStream != nil {
go s.onNewStream(stream)
} else {
go s.Close()
}
}
s.streamLock.Unlock()
case cmdFIN:
s.streamLock.RLock()
stream, ok := s.streams[sid]
s.streamLock.RUnlock()
if ok {
stream.Close()
}
//logrus.Debugln("stream fin", sid, s.streams)
case cmdWaste:
if hdr.Length() > 0 {
buffer := pool.Get(int(hdr.Length()))
if _, err := io.ReadFull(s.conn, buffer); err != nil {
pool.Put(buffer)
return err
}
pool.Put(buffer)
}
case cmdSettings:
if hdr.Length() > 0 {
buffer := pool.Get(int(hdr.Length()))
if _, err := io.ReadFull(s.conn, buffer); err != nil {
pool.Put(buffer)
return err
}
if !s.isClient {
receivedSettingsFromClient = true
m := util.StringMapFromBytes(buffer)
paddingF := s.padding.Load()
if m["padding-md5"] != paddingF.Md5 {
// logrus.Debugln("remote md5 is", m["padding-md5"])
f := newFrame(cmdUpdatePaddingScheme, 0)
f.data = paddingF.RawScheme
_, err = s.writeFrame(f)
if err != nil {
pool.Put(buffer)
return err
}
}
}
pool.Put(buffer)
}
case cmdAlert:
if hdr.Length() > 0 {
buffer := pool.Get(int(hdr.Length()))
if _, err := io.ReadFull(s.conn, buffer); err != nil {
pool.Put(buffer)
return err
}
if s.isClient {
log.Errorln("[Alert from server] %s", string(buffer))
}
pool.Put(buffer)
return nil
}
case cmdUpdatePaddingScheme:
if hdr.Length() > 0 {
buffer := pool.Get(int(hdr.Length()))
if _, err := io.ReadFull(s.conn, buffer); err != nil {
pool.Put(buffer)
return err
}
if s.isClient {
if padding.UpdatePaddingScheme(buffer, s.padding) {
log.Infoln("[Update padding succeed] %x\n", md5.Sum(buffer))
} else {
log.Warnln("[Update padding failed] %x\n", md5.Sum(buffer))
}
}
pool.Put(buffer)
}
default:
// I don't know what command it is (can't have data)
}
} else {
return err
}
}
}
// notify the session that a stream has closed
func (s *Session) streamClosed(sid uint32) error {
_, err := s.writeFrame(newFrame(cmdFIN, sid))
s.streamLock.Lock()
delete(s.streams, sid)
s.streamLock.Unlock()
return err
}
func (s *Session) writeFrame(frame frame) (int, error) {
dataLen := len(frame.data)
buffer := buf.NewSize(dataLen + headerOverHeadSize)
buffer.WriteByte(frame.cmd)
binary.BigEndian.PutUint32(buffer.Extend(4), frame.sid)
binary.BigEndian.PutUint16(buffer.Extend(2), uint16(dataLen))
buffer.Write(frame.data)
_, err := s.writeConn(buffer.Bytes())
buffer.Release()
if err != nil {
return 0, err
}
return dataLen, nil
}
func (s *Session) writeConn(b []byte) (n int, err error) {
s.connLock.Lock()
defer s.connLock.Unlock()
if s.buffering {
s.buffer = append(s.buffer, b...)
return len(b), nil
} else if len(s.buffer) > 0 {
b = append(s.buffer, b...)
s.buffer = nil
}
// calulate & send padding
if s.isClient {
pkt := s.pktCounter.Add(1)
paddingF := s.padding.Load()
if pkt < paddingF.Stop {
pktSizes := paddingF.GenerateRecordPayloadSizes(pkt)
for _, l := range pktSizes {
remainPayloadLen := len(b)
if l == padding.CheckMark {
if remainPayloadLen == 0 {
break
} else {
continue
}
}
// logrus.Debugln(pkt, "write", l, "len", remainPayloadLen, "remain", remainPayloadLen-l)
if remainPayloadLen > l { // this packet is all payload
_, err = s.conn.Write(b[:l])
if err != nil {
return 0, err
}
n += l
b = b[l:]
} else if remainPayloadLen > 0 { // this packet contains padding and the last part of payload
paddingLen := l - remainPayloadLen - headerOverHeadSize
if paddingLen > 0 {
padding := make([]byte, headerOverHeadSize+paddingLen)
padding[0] = cmdWaste
binary.BigEndian.PutUint32(padding[1:5], 0)
binary.BigEndian.PutUint16(padding[5:7], uint16(paddingLen))
b = append(b, padding...)
}
_, err = s.conn.Write(b)
if err != nil {
return 0, err
}
n += remainPayloadLen
b = nil
} else { // this packet is all padding
padding := make([]byte, headerOverHeadSize+l)
padding[0] = cmdWaste
binary.BigEndian.PutUint32(padding[1:5], 0)
binary.BigEndian.PutUint16(padding[5:7], uint16(l))
_, err = s.conn.Write(padding)
if err != nil {
return 0, err
}
b = nil
}
}
// maybe still remain payload to write
if len(b) == 0 {
return
}
}
}
return s.conn.Write(b)
}

View File

@@ -0,0 +1,99 @@
package session
import (
"io"
"net"
"os"
"sync"
"time"
)
// Stream implements net.Conn
type Stream struct {
id uint32
sess *Session
pipeR *io.PipeReader
pipeW *io.PipeWriter
dieOnce sync.Once
dieHook func()
}
// newStream initiates a Stream struct
func newStream(id uint32, sess *Session) *Stream {
s := new(Stream)
s.id = id
s.sess = sess
s.pipeR, s.pipeW = io.Pipe()
return s
}
// Read implements net.Conn
func (s *Stream) Read(b []byte) (n int, err error) {
return s.pipeR.Read(b)
}
// Write implements net.Conn
func (s *Stream) Write(b []byte) (n int, err error) {
f := newFrame(cmdPSH, s.id)
f.data = b
n, err = s.sess.writeFrame(f)
return
}
// Close implements net.Conn
func (s *Stream) Close() error {
if s.sessionClose() {
// notify remote
return s.sess.streamClosed(s.id)
} else {
return io.ErrClosedPipe
}
}
// sessionClose close stream from session side, do not notify remote
func (s *Stream) sessionClose() (once bool) {
s.dieOnce.Do(func() {
s.pipeR.Close()
once = true
if s.dieHook != nil {
s.dieHook()
s.dieHook = nil
}
})
return
}
func (s *Stream) SetReadDeadline(t time.Time) error {
return os.ErrNotExist
}
func (s *Stream) SetWriteDeadline(t time.Time) error {
return os.ErrNotExist
}
func (s *Stream) SetDeadline(t time.Time) error {
return os.ErrNotExist
}
// LocalAddr satisfies net.Conn interface
func (s *Stream) LocalAddr() net.Addr {
if ts, ok := s.sess.conn.(interface {
LocalAddr() net.Addr
}); ok {
return ts.LocalAddr()
}
return nil
}
// RemoteAddr satisfies net.Conn interface
func (s *Stream) RemoteAddr() net.Addr {
if ts, ok := s.sess.conn.(interface {
RemoteAddr() net.Addr
}); ok {
return ts.RemoteAddr()
}
return nil
}

View File

@@ -0,0 +1,46 @@
package skiplist
// Container is a holder object that stores a collection of other objects.
type Container interface {
IsEmpty() bool // IsEmpty checks if the container has no elements.
Len() int // Len returns the number of elements in the container.
Clear() // Clear erases all elements from the container. After this call, Len() returns zero.
}
// Map is a associative container that contains key-value pairs with unique keys.
type Map[K any, V any] interface {
Container
Has(K) bool // Checks whether the container contains element with specific key.
Find(K) *V // Finds element with specific key.
Insert(K, V) // Inserts a key-value pair in to the container or replace existing value.
Remove(K) bool // Remove element with specific key.
ForEach(func(K, V)) // Iterate the container.
ForEachIf(func(K, V) bool) // Iterate the container, stops when the callback returns false.
ForEachMutable(func(K, *V)) // Iterate the container, *V is mutable.
ForEachMutableIf(func(K, *V) bool) // Iterate the container, *V is mutable, stops when the callback returns false.
}
// Set is a containers that store unique elements.
type Set[K any] interface {
Container
Has(K) bool // Checks whether the container contains element with specific key.
Insert(K) // Inserts a key-value pair in to the container or replace existing value.
InsertN(...K) // Inserts multiple key-value pairs in to the container or replace existing value.
Remove(K) bool // Remove element with specific key.
RemoveN(...K) // Remove multiple elements with specific keys.
ForEach(func(K)) // Iterate the container.
ForEachIf(func(K) bool) // Iterate the container, stops when the callback returns false.
}
// Iterator is the interface for container's iterator.
type Iterator[T any] interface {
IsNotEnd() bool // Whether it is point to the end of the range.
MoveToNext() // Let it point to the next element.
Value() T // Return the value of current element.
}
// MapIterator is the interface for map's iterator.
type MapIterator[K any, V any] interface {
Iterator[V]
Key() K // The key of the element
}

View File

@@ -0,0 +1,457 @@
package skiplist
// This implementation is based on https://github.com/liyue201/gostl/tree/master/ds/skiplist
// (many thanks), added many optimizations, such as:
//
// - adaptive level
// - lesser search for prevs when key already exists.
// - reduce memory allocations
// - richer interface.
//
// etc.
import (
"math/bits"
"math/rand"
"time"
)
const (
skipListMaxLevel = 40
)
// SkipList is a probabilistic data structure that seem likely to supplant balanced trees as the
// implementation method of choice for many applications. Skip list algorithms have the same
// asymptotic expected time bounds as balanced trees and are simpler, faster and use less space.
//
// See https://en.wikipedia.org/wiki/Skip_list for more details.
type SkipList[K any, V any] struct {
level int // Current level, may increase dynamically during insertion
len int // Total elements numner in the skiplist.
head skipListNode[K, V] // head.next[level] is the head of each level.
// This cache is used to save the previous nodes when modifying the skip list to avoid
// allocating memory each time it is called.
prevsCache []*skipListNode[K, V]
rander *rand.Rand
impl skipListImpl[K, V]
}
// NewSkipList creates a new SkipList for Ordered key type.
func NewSkipList[K Ordered, V any]() *SkipList[K, V] {
sl := skipListOrdered[K, V]{}
sl.init()
sl.impl = (skipListImpl[K, V])(&sl)
return &sl.SkipList
}
// NewSkipListFromMap creates a new SkipList from a map.
func NewSkipListFromMap[K Ordered, V any](m map[K]V) *SkipList[K, V] {
sl := NewSkipList[K, V]()
for k, v := range m {
sl.Insert(k, v)
}
return sl
}
// NewSkipListFunc creates a new SkipList with specified compare function keyCmp.
func NewSkipListFunc[K any, V any](keyCmp CompareFn[K]) *SkipList[K, V] {
sl := skipListFunc[K, V]{}
sl.init()
sl.keyCmp = keyCmp
sl.impl = skipListImpl[K, V](&sl)
return &sl.SkipList
}
// IsEmpty implements the Container interface.
func (sl *SkipList[K, V]) IsEmpty() bool {
return sl.len == 0
}
// Len implements the Container interface.
func (sl *SkipList[K, V]) Len() int {
return sl.len
}
// Clear implements the Container interface.
func (sl *SkipList[K, V]) Clear() {
for i := range sl.head.next {
sl.head.next[i] = nil
}
sl.level = 1
sl.len = 0
}
// Iterate return an iterator to the skiplist.
func (sl *SkipList[K, V]) Iterate() MapIterator[K, V] {
return &skipListIterator[K, V]{sl.head.next[0], nil}
}
// Insert inserts a key-value pair into the skiplist.
// If the key is already in the skip list, it's value will be updated.
func (sl *SkipList[K, V]) Insert(key K, value V) {
node, prevs := sl.impl.findInsertPoint(key)
if node != nil {
// Already exist, update the value
node.value = value
return
}
level := sl.randomLevel()
node = newSkipListNode(level, key, value)
minLevel := level
if sl.level < level {
minLevel = sl.level
}
for i := 0; i < minLevel; i++ {
node.next[i] = prevs[i].next[i]
prevs[i].next[i] = node
}
if level > sl.level {
for i := sl.level; i < level; i++ {
sl.head.next[i] = node
}
sl.level = level
}
sl.len++
}
// Find returns the value associated with the passed key if the key is in the skiplist, otherwise
// returns nil.
func (sl *SkipList[K, V]) Find(key K) *V {
node := sl.impl.findNode(key)
if node != nil {
return &node.value
}
return nil
}
// Has implement the Map interface.
func (sl *SkipList[K, V]) Has(key K) bool {
return sl.impl.findNode(key) != nil
}
// LowerBound returns an iterator to the first element in the skiplist that
// does not satisfy element < value (i.e. greater or equal to),
// or a end itetator if no such element is found.
func (sl *SkipList[K, V]) LowerBound(key K) MapIterator[K, V] {
return &skipListIterator[K, V]{sl.impl.lowerBound(key), nil}
}
// UpperBound returns an iterator to the first element in the skiplist that
// does not satisfy value < element (i.e. strictly greater),
// or a end itetator if no such element is found.
func (sl *SkipList[K, V]) UpperBound(key K) MapIterator[K, V] {
return &skipListIterator[K, V]{sl.impl.upperBound(key), nil}
}
// FindRange returns an iterator in range [first, last) (last is not includeed).
func (sl *SkipList[K, V]) FindRange(first, last K) MapIterator[K, V] {
return &skipListIterator[K, V]{sl.impl.lowerBound(first), sl.impl.upperBound(last)}
}
// Remove removes the key-value pair associated with the passed key and returns true if the key is
// in the skiplist, otherwise returns false.
func (sl *SkipList[K, V]) Remove(key K) bool {
node, prevs := sl.impl.findRemovePoint(key)
if node == nil {
return false
}
for i, v := range node.next {
prevs[i].next[i] = v
}
for sl.level > 1 && sl.head.next[sl.level-1] == nil {
sl.level--
}
sl.len--
return true
}
// ForEach implements the Map interface.
func (sl *SkipList[K, V]) ForEach(op func(K, V)) {
for e := sl.head.next[0]; e != nil; e = e.next[0] {
op(e.key, e.value)
}
}
// ForEachMutable implements the Map interface.
func (sl *SkipList[K, V]) ForEachMutable(op func(K, *V)) {
for e := sl.head.next[0]; e != nil; e = e.next[0] {
op(e.key, &e.value)
}
}
// ForEachIf implements the Map interface.
func (sl *SkipList[K, V]) ForEachIf(op func(K, V) bool) {
for e := sl.head.next[0]; e != nil; e = e.next[0] {
if !op(e.key, e.value) {
return
}
}
}
// ForEachMutableIf implements the Map interface.
func (sl *SkipList[K, V]) ForEachMutableIf(op func(K, *V) bool) {
for e := sl.head.next[0]; e != nil; e = e.next[0] {
if !op(e.key, &e.value) {
return
}
}
}
/// SkipList implementation part.
type skipListNode[K any, V any] struct {
key K
value V
next []*skipListNode[K, V]
}
//go:generate bash ./skiplist_newnode_generate.sh skipListMaxLevel skiplist_newnode.go
// func newSkipListNode[K Ordered, V any](level int, key K, value V) *skipListNode[K, V]
type skipListIterator[K any, V any] struct {
node, end *skipListNode[K, V]
}
func (it *skipListIterator[K, V]) IsNotEnd() bool {
return it.node != it.end
}
func (it *skipListIterator[K, V]) MoveToNext() {
it.node = it.node.next[0]
}
func (it *skipListIterator[K, V]) Key() K {
return it.node.key
}
func (it *skipListIterator[K, V]) Value() V {
return it.node.value
}
// skipListImpl is an interface to provide different implementation for Ordered key or CompareFn.
//
// We can use CompareFn to cumpare Ordered keys, but a separated implementation is much faster.
// We don't make the whole skip list an interface, in order to share the type independented method.
// And because these methods are called directly without going through the interface, they are also
// much faster.
type skipListImpl[K any, V any] interface {
findNode(key K) *skipListNode[K, V]
lowerBound(key K) *skipListNode[K, V]
upperBound(key K) *skipListNode[K, V]
findInsertPoint(key K) (*skipListNode[K, V], []*skipListNode[K, V])
findRemovePoint(key K) (*skipListNode[K, V], []*skipListNode[K, V])
}
func (sl *SkipList[K, V]) init() {
sl.level = 1
// #nosec G404 -- This is not a security condition
sl.rander = rand.New(rand.NewSource(time.Now().Unix()))
sl.prevsCache = make([]*skipListNode[K, V], skipListMaxLevel)
sl.head.next = make([]*skipListNode[K, V], skipListMaxLevel)
}
func (sl *SkipList[K, V]) randomLevel() int {
total := uint64(1)<<uint64(skipListMaxLevel) - 1 // 2^n-1
k := sl.rander.Uint64() % total
level := skipListMaxLevel - bits.Len64(k) + 1
// Since levels are randomly generated, most should be less than log2(s.len).
// Then make a limit according to sl.len to avoid unexpectedly large value.
for level > 3 && 1<<(level-3) > sl.len {
level--
}
return level
}
/// skipListOrdered part
// skipListOrdered is the skip list implementation for Ordered types.
type skipListOrdered[K Ordered, V any] struct {
SkipList[K, V]
}
func (sl *skipListOrdered[K, V]) findNode(key K) *skipListNode[K, V] {
return sl.doFindNode(key, true)
}
func (sl *skipListOrdered[K, V]) doFindNode(key K, eq bool) *skipListNode[K, V] {
// This function execute the job of findNode if eq is true, otherwise lowBound.
// Passing the control variable eq is ugly but it's faster than testing node
// again outside the function in findNode.
prev := &sl.head
for i := sl.level - 1; i >= 0; i-- {
for cur := prev.next[i]; cur != nil; cur = cur.next[i] {
if cur.key == key {
return cur
}
if cur.key > key {
// All other node in this level must be greater than the key,
// search the next level.
break
}
prev = cur
}
}
if eq {
return nil
}
return prev.next[0]
}
func (sl *skipListOrdered[K, V]) lowerBound(key K) *skipListNode[K, V] {
return sl.doFindNode(key, false)
}
func (sl *skipListOrdered[K, V]) upperBound(key K) *skipListNode[K, V] {
node := sl.lowerBound(key)
if node != nil && node.key == key {
return node.next[0]
}
return node
}
// findInsertPoint returns (*node, nil) to the existed node if the key exists,
// or (nil, []*node) to the previous nodes if the key doesn't exist
func (sl *skipListOrdered[K, V]) findInsertPoint(key K) (*skipListNode[K, V], []*skipListNode[K, V]) {
prevs := sl.prevsCache[0:sl.level]
prev := &sl.head
for i := sl.level - 1; i >= 0; i-- {
for next := prev.next[i]; next != nil; next = next.next[i] {
if next.key == key {
// The key is already existed, prevs are useless because no new node insertion.
// stop searching.
return next, nil
}
if next.key > key {
// All other node in this level must be greater than the key,
// search the next level.
break
}
prev = next
}
prevs[i] = prev
}
return nil, prevs
}
// findRemovePoint finds the node which match the key and it's previous nodes.
func (sl *skipListOrdered[K, V]) findRemovePoint(key K) (*skipListNode[K, V], []*skipListNode[K, V]) {
prevs := sl.findPrevNodes(key)
node := prevs[0].next[0]
if node == nil || node.key != key {
return nil, nil
}
return node, prevs
}
func (sl *skipListOrdered[K, V]) findPrevNodes(key K) []*skipListNode[K, V] {
prevs := sl.prevsCache[0:sl.level]
prev := &sl.head
for i := sl.level - 1; i >= 0; i-- {
for next := prev.next[i]; next != nil; next = next.next[i] {
if next.key >= key {
break
}
prev = next
}
prevs[i] = prev
}
return prevs
}
/// skipListFunc part
// skipListFunc is the skip list implementation which compare keys with func.
type skipListFunc[K any, V any] struct {
SkipList[K, V]
keyCmp CompareFn[K]
}
func (sl *skipListFunc[K, V]) findNode(key K) *skipListNode[K, V] {
node := sl.lowerBound(key)
if node != nil && sl.keyCmp(node.key, key) == 0 {
return node
}
return nil
}
func (sl *skipListFunc[K, V]) lowerBound(key K) *skipListNode[K, V] {
var prev = &sl.head
for i := sl.level - 1; i >= 0; i-- {
cur := prev.next[i]
for ; cur != nil; cur = cur.next[i] {
cmpRet := sl.keyCmp(cur.key, key)
if cmpRet == 0 {
return cur
}
if cmpRet > 0 {
break
}
prev = cur
}
}
return prev.next[0]
}
func (sl *skipListFunc[K, V]) upperBound(key K) *skipListNode[K, V] {
node := sl.lowerBound(key)
if node != nil && sl.keyCmp(node.key, key) == 0 {
return node.next[0]
}
return node
}
// findInsertPoint returns (*node, nil) to the existed node if the key exists,
// or (nil, []*node) to the previous nodes if the key doesn't exist
func (sl *skipListFunc[K, V]) findInsertPoint(key K) (*skipListNode[K, V], []*skipListNode[K, V]) {
prevs := sl.prevsCache[0:sl.level]
prev := &sl.head
for i := sl.level - 1; i >= 0; i-- {
for cur := prev.next[i]; cur != nil; cur = cur.next[i] {
r := sl.keyCmp(cur.key, key)
if r == 0 {
// The key is already existed, prevs are useless because no new node insertion.
// stop searching.
return cur, nil
}
if r > 0 {
// All other node in this level must be greater than the key,
// search the next level.
break
}
prev = cur
}
prevs[i] = prev
}
return nil, prevs
}
// findRemovePoint finds the node which match the key and it's previous nodes.
func (sl *skipListFunc[K, V]) findRemovePoint(key K) (*skipListNode[K, V], []*skipListNode[K, V]) {
prevs := sl.findPrevNodes(key)
node := prevs[0].next[0]
if node == nil || sl.keyCmp(node.key, key) != 0 {
return nil, nil
}
return node, prevs
}
func (sl *skipListFunc[K, V]) findPrevNodes(key K) []*skipListNode[K, V] {
prevs := sl.prevsCache[0:sl.level]
prev := &sl.head
for i := sl.level - 1; i >= 0; i-- {
for next := prev.next[i]; next != nil; next = next.next[i] {
if sl.keyCmp(next.key, key) >= 0 {
break
}
prev = next
}
prevs[i] = prev
}
return prevs
}

View File

@@ -0,0 +1,297 @@
// AUTO GENERATED CODE, DON'T EDIT!!!
// EDIT skiplist_newnode_generate.sh accordingly.
package skiplist
// newSkipListNode creates a new node initialized with specified key, value and next slice.
func newSkipListNode[K any, V any](level int, key K, value V) *skipListNode[K, V] {
// For nodes with each levels, point their next slice to the nexts array allocated together,
// which can reduce 1 memory allocation and improve performance.
//
// The generics of the golang doesn't support non-type parameters like in C++,
// so we have to generate it manually.
switch level {
case 1:
n := struct {
head skipListNode[K, V]
nexts [1]*skipListNode[K, V]
}{head: skipListNode[K, V]{key, value, nil}}
n.head.next = n.nexts[:]
return &n.head
case 2:
n := struct {
head skipListNode[K, V]
nexts [2]*skipListNode[K, V]
}{head: skipListNode[K, V]{key, value, nil}}
n.head.next = n.nexts[:]
return &n.head
case 3:
n := struct {
head skipListNode[K, V]
nexts [3]*skipListNode[K, V]
}{head: skipListNode[K, V]{key, value, nil}}
n.head.next = n.nexts[:]
return &n.head
case 4:
n := struct {
head skipListNode[K, V]
nexts [4]*skipListNode[K, V]
}{head: skipListNode[K, V]{key, value, nil}}
n.head.next = n.nexts[:]
return &n.head
case 5:
n := struct {
head skipListNode[K, V]
nexts [5]*skipListNode[K, V]
}{head: skipListNode[K, V]{key, value, nil}}
n.head.next = n.nexts[:]
return &n.head
case 6:
n := struct {
head skipListNode[K, V]
nexts [6]*skipListNode[K, V]
}{head: skipListNode[K, V]{key, value, nil}}
n.head.next = n.nexts[:]
return &n.head
case 7:
n := struct {
head skipListNode[K, V]
nexts [7]*skipListNode[K, V]
}{head: skipListNode[K, V]{key, value, nil}}
n.head.next = n.nexts[:]
return &n.head
case 8:
n := struct {
head skipListNode[K, V]
nexts [8]*skipListNode[K, V]
}{head: skipListNode[K, V]{key, value, nil}}
n.head.next = n.nexts[:]
return &n.head
case 9:
n := struct {
head skipListNode[K, V]
nexts [9]*skipListNode[K, V]
}{head: skipListNode[K, V]{key, value, nil}}
n.head.next = n.nexts[:]
return &n.head
case 10:
n := struct {
head skipListNode[K, V]
nexts [10]*skipListNode[K, V]
}{head: skipListNode[K, V]{key, value, nil}}
n.head.next = n.nexts[:]
return &n.head
case 11:
n := struct {
head skipListNode[K, V]
nexts [11]*skipListNode[K, V]
}{head: skipListNode[K, V]{key, value, nil}}
n.head.next = n.nexts[:]
return &n.head
case 12:
n := struct {
head skipListNode[K, V]
nexts [12]*skipListNode[K, V]
}{head: skipListNode[K, V]{key, value, nil}}
n.head.next = n.nexts[:]
return &n.head
case 13:
n := struct {
head skipListNode[K, V]
nexts [13]*skipListNode[K, V]
}{head: skipListNode[K, V]{key, value, nil}}
n.head.next = n.nexts[:]
return &n.head
case 14:
n := struct {
head skipListNode[K, V]
nexts [14]*skipListNode[K, V]
}{head: skipListNode[K, V]{key, value, nil}}
n.head.next = n.nexts[:]
return &n.head
case 15:
n := struct {
head skipListNode[K, V]
nexts [15]*skipListNode[K, V]
}{head: skipListNode[K, V]{key, value, nil}}
n.head.next = n.nexts[:]
return &n.head
case 16:
n := struct {
head skipListNode[K, V]
nexts [16]*skipListNode[K, V]
}{head: skipListNode[K, V]{key, value, nil}}
n.head.next = n.nexts[:]
return &n.head
case 17:
n := struct {
head skipListNode[K, V]
nexts [17]*skipListNode[K, V]
}{head: skipListNode[K, V]{key, value, nil}}
n.head.next = n.nexts[:]
return &n.head
case 18:
n := struct {
head skipListNode[K, V]
nexts [18]*skipListNode[K, V]
}{head: skipListNode[K, V]{key, value, nil}}
n.head.next = n.nexts[:]
return &n.head
case 19:
n := struct {
head skipListNode[K, V]
nexts [19]*skipListNode[K, V]
}{head: skipListNode[K, V]{key, value, nil}}
n.head.next = n.nexts[:]
return &n.head
case 20:
n := struct {
head skipListNode[K, V]
nexts [20]*skipListNode[K, V]
}{head: skipListNode[K, V]{key, value, nil}}
n.head.next = n.nexts[:]
return &n.head
case 21:
n := struct {
head skipListNode[K, V]
nexts [21]*skipListNode[K, V]
}{head: skipListNode[K, V]{key, value, nil}}
n.head.next = n.nexts[:]
return &n.head
case 22:
n := struct {
head skipListNode[K, V]
nexts [22]*skipListNode[K, V]
}{head: skipListNode[K, V]{key, value, nil}}
n.head.next = n.nexts[:]
return &n.head
case 23:
n := struct {
head skipListNode[K, V]
nexts [23]*skipListNode[K, V]
}{head: skipListNode[K, V]{key, value, nil}}
n.head.next = n.nexts[:]
return &n.head
case 24:
n := struct {
head skipListNode[K, V]
nexts [24]*skipListNode[K, V]
}{head: skipListNode[K, V]{key, value, nil}}
n.head.next = n.nexts[:]
return &n.head
case 25:
n := struct {
head skipListNode[K, V]
nexts [25]*skipListNode[K, V]
}{head: skipListNode[K, V]{key, value, nil}}
n.head.next = n.nexts[:]
return &n.head
case 26:
n := struct {
head skipListNode[K, V]
nexts [26]*skipListNode[K, V]
}{head: skipListNode[K, V]{key, value, nil}}
n.head.next = n.nexts[:]
return &n.head
case 27:
n := struct {
head skipListNode[K, V]
nexts [27]*skipListNode[K, V]
}{head: skipListNode[K, V]{key, value, nil}}
n.head.next = n.nexts[:]
return &n.head
case 28:
n := struct {
head skipListNode[K, V]
nexts [28]*skipListNode[K, V]
}{head: skipListNode[K, V]{key, value, nil}}
n.head.next = n.nexts[:]
return &n.head
case 29:
n := struct {
head skipListNode[K, V]
nexts [29]*skipListNode[K, V]
}{head: skipListNode[K, V]{key, value, nil}}
n.head.next = n.nexts[:]
return &n.head
case 30:
n := struct {
head skipListNode[K, V]
nexts [30]*skipListNode[K, V]
}{head: skipListNode[K, V]{key, value, nil}}
n.head.next = n.nexts[:]
return &n.head
case 31:
n := struct {
head skipListNode[K, V]
nexts [31]*skipListNode[K, V]
}{head: skipListNode[K, V]{key, value, nil}}
n.head.next = n.nexts[:]
return &n.head
case 32:
n := struct {
head skipListNode[K, V]
nexts [32]*skipListNode[K, V]
}{head: skipListNode[K, V]{key, value, nil}}
n.head.next = n.nexts[:]
return &n.head
case 33:
n := struct {
head skipListNode[K, V]
nexts [33]*skipListNode[K, V]
}{head: skipListNode[K, V]{key, value, nil}}
n.head.next = n.nexts[:]
return &n.head
case 34:
n := struct {
head skipListNode[K, V]
nexts [34]*skipListNode[K, V]
}{head: skipListNode[K, V]{key, value, nil}}
n.head.next = n.nexts[:]
return &n.head
case 35:
n := struct {
head skipListNode[K, V]
nexts [35]*skipListNode[K, V]
}{head: skipListNode[K, V]{key, value, nil}}
n.head.next = n.nexts[:]
return &n.head
case 36:
n := struct {
head skipListNode[K, V]
nexts [36]*skipListNode[K, V]
}{head: skipListNode[K, V]{key, value, nil}}
n.head.next = n.nexts[:]
return &n.head
case 37:
n := struct {
head skipListNode[K, V]
nexts [37]*skipListNode[K, V]
}{head: skipListNode[K, V]{key, value, nil}}
n.head.next = n.nexts[:]
return &n.head
case 38:
n := struct {
head skipListNode[K, V]
nexts [38]*skipListNode[K, V]
}{head: skipListNode[K, V]{key, value, nil}}
n.head.next = n.nexts[:]
return &n.head
case 39:
n := struct {
head skipListNode[K, V]
nexts [39]*skipListNode[K, V]
}{head: skipListNode[K, V]{key, value, nil}}
n.head.next = n.nexts[:]
return &n.head
case 40:
n := struct {
head skipListNode[K, V]
nexts [40]*skipListNode[K, V]
}{head: skipListNode[K, V]{key, value, nil}}
n.head.next = n.nexts[:]
return &n.head
}
panic("should not reach here")
}

View File

@@ -0,0 +1,75 @@
package skiplist
// Signed is a constraint that permits any signed integer type.
// If future releases of Go add new predeclared signed integer types,
// this constraint will be modified to include them.
type Signed interface {
~int | ~int8 | ~int16 | ~int32 | ~int64
}
// Unsigned is a constraint that permits any unsigned integer type.
// If future releases of Go add new predeclared unsigned integer types,
// this constraint will be modified to include them.
type Unsigned interface {
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
}
// Integer is a constraint that permits any integer type.
// If future releases of Go add new predeclared integer types,
// this constraint will be modified to include them.
type Integer interface {
Signed | Unsigned
}
// Float is a constraint that permits any floating-point type.
// If future releases of Go add new predeclared floating-point types,
// this constraint will be modified to include them.
type Float interface {
~float32 | ~float64
}
// Ordered is a constraint that permits any ordered type: any type
// that supports the operators < <= >= >.
// If future releases of Go add new ordered types,
// this constraint will be modified to include them.
type Ordered interface {
Integer | Float | ~string
}
// Numeric is a constraint that permits any numeric type.
type Numeric interface {
Integer | Float
}
// LessFn is a function that returns whether 'a' is less than 'b'.
type LessFn[T any] func(a, b T) bool
// CompareFn is a 3 way compare function that
// returns 1 if a > b,
// returns 0 if a == b,
// returns -1 if a < b.
type CompareFn[T any] func(a, b T) int
// HashFn is a function that returns the hash of 't'.
type HashFn[T any] func(t T) uint64
// Equals wraps the '==' operator for comparable types.
func Equals[T comparable](a, b T) bool {
return a == b
}
// Less wraps the '<' operator for ordered types.
func Less[T Ordered](a, b T) bool {
return a < b
}
// OrderedCompare provide default CompareFn for ordered types.
func OrderedCompare[T Ordered](a, b T) int {
if a < b {
return -1
}
if a > b {
return 1
}
return 0
}

View File

@@ -0,0 +1,28 @@
package util
import (
"context"
"runtime/debug"
"time"
"github.com/metacubex/mihomo/log"
)
func StartRoutine(ctx context.Context, d time.Duration, f func()) {
go func() {
defer func() {
if r := recover(); r != nil {
log.Errorln("[BUG] %v %s", r, string(debug.Stack()))
}
}()
for {
time.Sleep(d)
f()
select {
case <-ctx.Done():
return
default:
}
}
}()
}

View File

@@ -0,0 +1,27 @@
package util
import (
"strings"
)
type StringMap map[string]string
func (s StringMap) ToBytes() []byte {
var lines []string
for k, v := range s {
lines = append(lines, k+"="+v)
}
return []byte(strings.Join(lines, "\n"))
}
func StringMapFromBytes(b []byte) StringMap {
var m = make(StringMap)
var lines = strings.Split(string(b), "\n")
for _, line := range lines {
v := strings.SplitN(line, "=", 2)
if len(v) == 2 {
m[v[0]] = v[1]
}
}
return m
}

View File

@@ -93,7 +93,7 @@ func (t *Trojan) StreamConn(ctx context.Context, conn net.Conn) (net.Conn, error
ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout)
defer cancel()
err := utlsConn.(*tlsC.UConn).HandshakeContext(ctx)
err := utlsConn.HandshakeContext(ctx)
return utlsConn, err
}
} else {

View File

@@ -36,7 +36,7 @@ func StreamTLSConn(ctx context.Context, conn net.Conn, cfg *TLSConfig) (net.Conn
if cfg.Reality == nil {
utlsConn, valid := GetUTLSConn(conn, cfg.ClientFingerprint, tlsConfig)
if valid {
err := utlsConn.(*tlsC.UConn).HandshakeContext(ctx)
err = utlsConn.HandshakeContext(ctx)
return utlsConn, err
}
} else {
@@ -53,7 +53,7 @@ func StreamTLSConn(ctx context.Context, conn net.Conn, cfg *TLSConfig) (net.Conn
return tlsConn, err
}
func GetUTLSConn(conn net.Conn, ClientFingerprint string, tlsConfig *tls.Config) (net.Conn, bool) {
func GetUTLSConn(conn net.Conn, ClientFingerprint string, tlsConfig *tls.Config) (*tlsC.UConn, bool) {
if fingerprint, exists := tlsC.GetFingerprint(ClientFingerprint); exists {
utlsConn := tlsC.UClient(conn, tlsConfig, fingerprint)

View File

@@ -14,23 +14,23 @@ require (
github.com/buger/jsonparser v1.1.1 // indirect
github.com/cloudflare/circl v1.3.7 // indirect
github.com/coreos/go-iptables v0.8.0 // indirect
github.com/dlclark/regexp2 v1.11.4 // indirect
github.com/dlclark/regexp2 v1.11.5 // indirect
github.com/ebitengine/purego v0.8.2 // indirect
github.com/enfein/mieru/v3 v3.11.1 // indirect
github.com/enfein/mieru/v3 v3.11.2 // 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.7.0 // indirect
github.com/gaukas/godicttls v0.0.4 // indirect
github.com/go-chi/chi/v5 v5.2.0 // indirect
github.com/go-chi/chi/v5 v5.2.1 // indirect
github.com/go-chi/render v1.0.3 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/gobwas/httphead v0.1.0 // indirect
github.com/gobwas/pool v0.2.1 // indirect
github.com/gobwas/ws v1.4.0 // indirect
github.com/gofrs/uuid/v5 v5.3.0 // indirect
github.com/gofrs/uuid/v5 v5.3.1 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
@@ -46,11 +46,11 @@ require (
github.com/mdlayher/socket v0.4.1 // indirect
github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab // indirect
github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399 // indirect
github.com/metacubex/chacha v0.1.0 // indirect
github.com/metacubex/chacha v0.1.1 // indirect
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 // indirect
github.com/metacubex/gvisor v0.0.0-20241126021258-5b028898cc5a // indirect
github.com/metacubex/mihomo v1.7.0 // indirect
github.com/metacubex/quic-go v0.48.3-0.20241126053724-b69fea3888da // indirect
github.com/metacubex/quic-go v0.49.1-0.20250212162123-c135a4412996 // indirect
github.com/metacubex/randv2 v0.2.0 // indirect
github.com/metacubex/sing-quic v0.0.0-20250119013740-2a19cce83925 // indirect
github.com/metacubex/sing-shadowsocks v0.2.8 // indirect
@@ -99,13 +99,13 @@ require (
gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect
go.uber.org/mock v0.4.0 // indirect
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
golang.org/x/crypto v0.32.0 // indirect
golang.org/x/crypto v0.33.0 // indirect
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e // indirect
golang.org/x/mod v0.20.0 // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/net v0.35.0 // indirect
golang.org/x/sync v0.11.0 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/text v0.22.0 // indirect
golang.org/x/time v0.7.0 // indirect
golang.org/x/tools v0.24.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect

View File

@@ -24,12 +24,12 @@ github.com/coreos/go-iptables v0.8.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFE
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.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo=
github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=
github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/enfein/mieru/v3 v3.11.1 h1:G6PjSWPvzvgWUoMqnD4sBe1OGkDgrpNVT2UqCQhE6SE=
github.com/enfein/mieru/v3 v3.11.1/go.mod h1:XvVfNsM78lUMSlJJKXJZ0Hn3lAB2o/ETXTbb84x5egw=
github.com/enfein/mieru/v3 v3.11.2 h1:06KyGbXiiGz2nSHLJDOOkztAVY3cRr3wBMOpYxPotTo=
github.com/enfein/mieru/v3 v3.11.2/go.mod h1:XvVfNsM78lUMSlJJKXJZ0Hn3lAB2o/ETXTbb84x5egw=
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=
@@ -43,8 +43,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/gaukas/godicttls v0.0.4 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXbk=
github.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI=
github.com/go-chi/chi/v5 v5.2.0 h1:Aj1EtB0qR2Rdo2dG4O94RIU35w2lvQSj6BRA4+qwFL0=
github.com/go-chi/chi/v5 v5.2.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8=
github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
@@ -59,8 +59,8 @@ github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.4.0 h1:CTaoG1tojrh4ucGPcoJFiAQUAsEWekEWvLy7GsVNqGs=
github.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakrc=
github.com/gofrs/uuid/v5 v5.3.0 h1:m0mUMr+oVYUdxpMLgSYCZiXe7PuVPnI94+OMeVBNedk=
github.com/gofrs/uuid/v5 v5.3.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
github.com/gofrs/uuid/v5 v5.3.1 h1:aPx49MwJbekCzOyhZDjJVb0hx3A0KLjlbLx6p2gY0p0=
github.com/gofrs/uuid/v5 v5.3.1/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
@@ -99,14 +99,14 @@ github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab h1:Chbw+/31
github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab/go.mod h1:xVKK8jC5Sd3hfh7WjmCq+HorehIbrBijaUWmcuKjPcI=
github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399 h1:oBowHVKZycNtAFbZ6avaCSZJYeme2Nrj+4RpV2cNJig=
github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399/go.mod h1:4xcieuIK+M4bGQmQYZVqEaIYqjS1ahO4kXG7EmDgEro=
github.com/metacubex/chacha v0.1.0 h1:tg9RSJ18NvL38cCWNyYH1eiG6qDCyyXIaTLQthon0sc=
github.com/metacubex/chacha v0.1.0/go.mod h1:Djn9bPZxLTXbJFSeyo0/qzEzQI+gUSSzttuzZM75GH8=
github.com/metacubex/chacha v0.1.1 h1:OHIv11Nd9CISAIzegpjfupIoZp9DYm6uQw41RxvmU/c=
github.com/metacubex/chacha v0.1.1/go.mod h1:Djn9bPZxLTXbJFSeyo0/qzEzQI+gUSSzttuzZM75GH8=
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 h1:cjd4biTvOzK9ubNCCkQ+ldc4YSH/rILn53l/xGBFHHI=
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759/go.mod h1:UHOv2xu+RIgLwpXca7TLrXleEd4oR3sPatW6IF8wU88=
github.com/metacubex/gvisor v0.0.0-20241126021258-5b028898cc5a h1:cZ6oNVrsmsi3SNlnSnRio4zOgtQq+/XidwsaNgKICcg=
github.com/metacubex/gvisor v0.0.0-20241126021258-5b028898cc5a/go.mod h1:xBw/SYJPgUMPQ1tklV/brGn2nxhfr3BnvBzNlyi4Nic=
github.com/metacubex/quic-go v0.48.3-0.20241126053724-b69fea3888da h1:Mq6cbHbPTLLTUfA9scrwBmOGkvl6y99E3WmtMIMqo30=
github.com/metacubex/quic-go v0.48.3-0.20241126053724-b69fea3888da/go.mod h1:AiZ+UPgrkO1DTnmiAX4b+kRoV1Vfc65UkYD7RbFlIZA=
github.com/metacubex/quic-go v0.49.1-0.20250212162123-c135a4412996 h1:B+AP/Pj2/jBDS/kCYjz/x+0BCOKfd2VODYevyeIt+Ds=
github.com/metacubex/quic-go v0.49.1-0.20250212162123-c135a4412996/go.mod h1:ExVjGyEwTUjCFqx+5uxgV7MOoA3fZI+th4D40H35xmY=
github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs=
github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY=
github.com/metacubex/sing v0.0.0-20241121030428-33b6ebc52000 h1:gUbMXcQXhXGj0vCpCVFTUyIH7TMpD1dpTcNv/MCS+ok=
@@ -228,8 +228,8 @@ go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBs
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
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.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e h1:I88y4caeGeuDQxgdoFPUq097j7kNfw6uvuiNxUBfcBk=
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
@@ -238,11 +238,11 @@ golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
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.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
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-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -258,12 +258,12 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=

View File

@@ -3,9 +3,9 @@ module cfa
go 1.20
require (
github.com/dlclark/regexp2 v1.11.4
github.com/dlclark/regexp2 v1.11.5
github.com/metacubex/mihomo v1.7.0
golang.org/x/sync v0.10.0
golang.org/x/sync v0.11.0
gopkg.in/yaml.v3 v3.0.1
)
@@ -24,21 +24,21 @@ require (
github.com/cloudflare/circl v1.3.7 // indirect
github.com/coreos/go-iptables v0.8.0 // indirect
github.com/ebitengine/purego v0.8.2 // indirect
github.com/enfein/mieru/v3 v3.11.1 // indirect
github.com/enfein/mieru/v3 v3.11.2 // 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.7.0 // indirect
github.com/gaukas/godicttls v0.0.4 // indirect
github.com/go-chi/chi/v5 v5.2.0 // indirect
github.com/go-chi/chi/v5 v5.2.1 // indirect
github.com/go-chi/render v1.0.3 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/gobwas/httphead v0.1.0 // indirect
github.com/gobwas/pool v0.2.1 // indirect
github.com/gobwas/ws v1.4.0 // indirect
github.com/gofrs/uuid/v5 v5.3.0 // indirect
github.com/gofrs/uuid/v5 v5.3.1 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/go-cmp v0.6.0 // indirect
@@ -55,10 +55,10 @@ require (
github.com/mdlayher/socket v0.4.1 // indirect
github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab // indirect
github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399 // indirect
github.com/metacubex/chacha v0.1.0 // indirect
github.com/metacubex/chacha v0.1.1 // indirect
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 // indirect
github.com/metacubex/gvisor v0.0.0-20241126021258-5b028898cc5a // indirect
github.com/metacubex/quic-go v0.48.3-0.20241126053724-b69fea3888da // indirect
github.com/metacubex/quic-go v0.49.1-0.20250212162123-c135a4412996 // indirect
github.com/metacubex/randv2 v0.2.0 // indirect
github.com/metacubex/sing-quic v0.0.0-20250119013740-2a19cce83925 // indirect
github.com/metacubex/sing-shadowsocks v0.2.8 // indirect
@@ -107,12 +107,12 @@ require (
gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect
go.uber.org/mock v0.4.0 // indirect
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
golang.org/x/crypto v0.32.0 // indirect
golang.org/x/crypto v0.33.0 // indirect
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e // indirect
golang.org/x/mod v0.20.0 // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/net v0.35.0 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/text v0.22.0 // indirect
golang.org/x/time v0.7.0 // indirect
golang.org/x/tools v0.24.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect

View File

@@ -24,12 +24,12 @@ github.com/coreos/go-iptables v0.8.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFE
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.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo=
github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=
github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/enfein/mieru/v3 v3.11.1 h1:G6PjSWPvzvgWUoMqnD4sBe1OGkDgrpNVT2UqCQhE6SE=
github.com/enfein/mieru/v3 v3.11.1/go.mod h1:XvVfNsM78lUMSlJJKXJZ0Hn3lAB2o/ETXTbb84x5egw=
github.com/enfein/mieru/v3 v3.11.2 h1:06KyGbXiiGz2nSHLJDOOkztAVY3cRr3wBMOpYxPotTo=
github.com/enfein/mieru/v3 v3.11.2/go.mod h1:XvVfNsM78lUMSlJJKXJZ0Hn3lAB2o/ETXTbb84x5egw=
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=
@@ -43,8 +43,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/gaukas/godicttls v0.0.4 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXbk=
github.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI=
github.com/go-chi/chi/v5 v5.2.0 h1:Aj1EtB0qR2Rdo2dG4O94RIU35w2lvQSj6BRA4+qwFL0=
github.com/go-chi/chi/v5 v5.2.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8=
github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
@@ -59,8 +59,8 @@ github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.4.0 h1:CTaoG1tojrh4ucGPcoJFiAQUAsEWekEWvLy7GsVNqGs=
github.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakrc=
github.com/gofrs/uuid/v5 v5.3.0 h1:m0mUMr+oVYUdxpMLgSYCZiXe7PuVPnI94+OMeVBNedk=
github.com/gofrs/uuid/v5 v5.3.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
github.com/gofrs/uuid/v5 v5.3.1 h1:aPx49MwJbekCzOyhZDjJVb0hx3A0KLjlbLx6p2gY0p0=
github.com/gofrs/uuid/v5 v5.3.1/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
@@ -100,14 +100,14 @@ github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab h1:Chbw+/31
github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab/go.mod h1:xVKK8jC5Sd3hfh7WjmCq+HorehIbrBijaUWmcuKjPcI=
github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399 h1:oBowHVKZycNtAFbZ6avaCSZJYeme2Nrj+4RpV2cNJig=
github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399/go.mod h1:4xcieuIK+M4bGQmQYZVqEaIYqjS1ahO4kXG7EmDgEro=
github.com/metacubex/chacha v0.1.0 h1:tg9RSJ18NvL38cCWNyYH1eiG6qDCyyXIaTLQthon0sc=
github.com/metacubex/chacha v0.1.0/go.mod h1:Djn9bPZxLTXbJFSeyo0/qzEzQI+gUSSzttuzZM75GH8=
github.com/metacubex/chacha v0.1.1 h1:OHIv11Nd9CISAIzegpjfupIoZp9DYm6uQw41RxvmU/c=
github.com/metacubex/chacha v0.1.1/go.mod h1:Djn9bPZxLTXbJFSeyo0/qzEzQI+gUSSzttuzZM75GH8=
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 h1:cjd4biTvOzK9ubNCCkQ+ldc4YSH/rILn53l/xGBFHHI=
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759/go.mod h1:UHOv2xu+RIgLwpXca7TLrXleEd4oR3sPatW6IF8wU88=
github.com/metacubex/gvisor v0.0.0-20241126021258-5b028898cc5a h1:cZ6oNVrsmsi3SNlnSnRio4zOgtQq+/XidwsaNgKICcg=
github.com/metacubex/gvisor v0.0.0-20241126021258-5b028898cc5a/go.mod h1:xBw/SYJPgUMPQ1tklV/brGn2nxhfr3BnvBzNlyi4Nic=
github.com/metacubex/quic-go v0.48.3-0.20241126053724-b69fea3888da h1:Mq6cbHbPTLLTUfA9scrwBmOGkvl6y99E3WmtMIMqo30=
github.com/metacubex/quic-go v0.48.3-0.20241126053724-b69fea3888da/go.mod h1:AiZ+UPgrkO1DTnmiAX4b+kRoV1Vfc65UkYD7RbFlIZA=
github.com/metacubex/quic-go v0.49.1-0.20250212162123-c135a4412996 h1:B+AP/Pj2/jBDS/kCYjz/x+0BCOKfd2VODYevyeIt+Ds=
github.com/metacubex/quic-go v0.49.1-0.20250212162123-c135a4412996/go.mod h1:ExVjGyEwTUjCFqx+5uxgV7MOoA3fZI+th4D40H35xmY=
github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs=
github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY=
github.com/metacubex/sing v0.0.0-20241121030428-33b6ebc52000 h1:gUbMXcQXhXGj0vCpCVFTUyIH7TMpD1dpTcNv/MCS+ok=
@@ -229,8 +229,8 @@ go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBs
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
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.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e h1:I88y4caeGeuDQxgdoFPUq097j7kNfw6uvuiNxUBfcBk=
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
@@ -239,11 +239,11 @@ golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
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.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
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-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -259,12 +259,12 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=

View File

@@ -36,6 +36,7 @@ data class Proxy(
Dns(false),
Ssh(false),
Mieru(false),
AnyTLS(false),
Relay(true),