Compare commits

..

44 Commits

Author SHA1 Message Date
Skyxim
47ad8e08be chore: Random only if the certificate and private-key are empty 2023-06-03 10:02:31 +00:00
H1JK
e1af4ddda3 chore: Reject packet conn implement wait read 2023-06-03 10:01:50 +00:00
Larvan2
58e05c42c9 fix: handle manually select in url-test 2023-06-03 09:55:29 +00:00
Larvan2
880cc90e10 Merge branch 'Alpha' into Meta 2023-06-03 09:54:25 +00:00
Skyxim
a4334e1d52 fix: wildcard matching problem (#536) 2023-04-29 01:10:55 +08:00
Larvan2
3ba94842cc chore: update genReleaseNote.sh 2023-04-28 07:29:58 +00:00
Larvan2
a266589faf Merge branch 'Alpha' into Meta 2023-04-28 07:05:21 +00:00
Skyxim
d9319ec09a chore: update bug_report 2023-04-27 12:19:59 +00:00
Larvan2
070f8f8949 Merge branch 'Alpha' into Meta 2023-04-26 14:14:46 +00:00
Larvan2
bf3c6a044c chore: update templates 2023-04-14 05:26:44 +00:00
Larvan2
d6d2d90502 add issue templates 2023-04-04 06:45:21 +00:00
Larvan2
a1d0f4c6ee chore: rename delete.yml 2023-03-31 06:34:23 +00:00
Larvan2
d569d8186d chore: add genReleaseNote.sh 2023-03-30 16:43:05 +00:00
Larvan2
9b7aab1fc7 chore: rename delete.yml 2023-03-30 16:04:09 +00:00
Larvan2
3c717097cb Merge branch 'Alpha' into Meta 2023-03-30 16:03:47 +00:00
wwqgtxx
8293b7fdae Merge branch 'Beta' into Meta
# Conflicts:
#	.github/workflows/docker.yaml
#	.github/workflows/prerelease.yml
2023-02-19 01:25:34 +08:00
wwqgtxx
0ba415866e Merge branch 'Alpha' into Beta 2023-02-19 01:25:01 +08:00
Larvan2
53b41ca166 Chore: Add action for deleting old workflow 2023-01-30 18:17:22 +08:00
metacubex
8a75f78e63 chore: adjust Dockerfile 2023-01-12 02:24:12 +08:00
metacubex
d9692c6366 Merge branch 'Beta' into Meta 2023-01-12 02:15:14 +08:00
metacubex
f4b0062dfc Merge branch 'Alpha' into Beta 2023-01-12 02:14:49 +08:00
metacubex
b9ffc82e53 Merge branch 'Beta' into Meta 2023-01-12 01:33:56 +08:00
metacubex
78aaea6a45 Merge branch 'Alpha' into Beta 2023-01-12 01:33:16 +08:00
cubemaze
3645fbf161 Merge pull request #327 from Rasphino/Meta
Update flake.nix hash
2023-01-08 00:29:29 +08:00
Rasphino
a1d0f22132 fix: update flake.nix hash 2023-01-07 23:38:32 +08:00
metacubex
fa73b0f4bf Merge remote-tracking branch 'origin/Beta' into Meta 2023-01-01 19:41:36 +08:00
metacubex
3b76a8b839 Merge remote-tracking branch 'origin/Alpha' into Beta 2023-01-01 19:40:36 +08:00
cubemaze
667f42dcdc Merge pull request #282 from tdjnodj/Meta
Update README.md
2022-12-03 17:23:51 +08:00
tdjnodj
dfbe09860f Update README.md 2022-12-03 17:17:08 +08:00
metacubex
9e20f9c26a chore: update dependencies 2022-11-28 20:33:10 +08:00
Skimmle
f968d0cb82 chore: update github action 2022-11-26 20:16:12 +08:00
metacubex
2ad84f4379 Merge branch 'Beta' into Meta 2022-11-02 18:08:22 +08:00
metacubex
c7aa16426f Merge branch 'Alpha' into Beta 2022-11-02 18:07:29 +08:00
Skyxim
5987f8e3b5 Merge branch 'Beta' into Meta 2022-08-29 13:08:29 +08:00
Skyxim
3a8eb72de2 Merge branch 'Alpha' into Beta 2022-08-29 13:08:22 +08:00
zhudan
33abbdfd24 Merge pull request #174 from MetaCubeX/Alpha
Alpha
2022-08-29 11:24:07 +08:00
zhudan
0703d6cbff Merge pull request #173 from MetaCubeX/Alpha
Alpha
2022-08-29 11:22:14 +08:00
Skyxim
10d2d14938 Merge branch 'Beta' into Meta
# Conflicts:
#	rules/provider/classical_strategy.go
2022-07-02 10:41:41 +08:00
wwqgtxx
691cf1d8d6 Merge pull request #94 from bash99/Meta
Update README.md
2022-06-15 19:15:51 +08:00
bash99
d1decb8e58 Update README.md
add permissions for systemctl services
clash-dashboard change to updated one
2022-06-15 14:00:05 +08:00
Skyxim
7d04904109 fix: leak dns when domain in hosts list 2022-06-11 18:51:26 +08:00
Skyxim
a5acd3aa97 refactor: clear linkname,reduce cycle dependencies,transport init geosite function 2022-06-11 18:51:22 +08:00
Skyxim
eea9a12560 fix: 规则匹配默认策略组返回错误 2022-06-09 14:18:35 +08:00
adlyq
0a4570b55c fix: group filter touch provider 2022-06-09 14:18:29 +08:00
164 changed files with 2776 additions and 5099 deletions

82
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@@ -0,0 +1,82 @@
name: Bug report
description: Create a report to help us improve
title: "[Bug] "
body:
- type: checkboxes
id: ensure
attributes:
label: Verify steps
description: "
在提交之前,请确认
Please verify that you've followed these steps
"
options:
- label: "
确保你使用的是**本仓库**最新的的 clash 或 clash Alpha 版本
Ensure you are using the latest version of Clash or Clash Premium from **this repository**.
"
required: true
- label: "
如果你可以自己 debug 并解决的话,提交 PR 吧
Is this something you can **debug and fix**? Send a pull request! Bug fixes and documentation fixes are welcome.
"
required: false
- label: "
我已经在 [Issue Tracker](……/) 中找过我要提出的问题
I have searched on the [issue tracker](……/) for a related issue.
"
required: true
- label: "
我已经使用 Alpha 分支版本测试过,问题依旧存在
I have tested using the dev branch, and the issue still exists.
"
required: true
- label: "
我已经仔细看过 [Documentation](https://wiki.metacubex.one/) 并无法自行解决问题
I have read the [documentation](https://wiki.metacubex.one/) and was unable to solve the issue.
"
required: true
- label: "
这是 Clash 核心的问题,并非我所使用的 Clash 衍生版本(如 OpenClash、KoolClash 等)的特定问题
This is an issue of the Clash core *per se*, not to the derivatives of Clash, like OpenClash or KoolClash.
"
required: true
- type: input
attributes:
label: Clash version
description: "use `clash -v`"
validations:
required: true
- type: dropdown
id: os
attributes:
label: What OS are you seeing the problem on?
multiple: true
options:
- macOS
- Windows
- Linux
- OpenBSD/FreeBSD
- type: textarea
attributes:
render: yaml
label: "Clash config"
description: "
在下方附上 Clash core 配置文件,请确保配置文件中没有敏感信息(比如:服务器地址,密码,端口等)
Paste the Clash core configuration file below, please make sure that there is no sensitive information in the configuration file (e.g., server address/url, password, port)
"
validations:
required: true
- type: textarea
attributes:
render: shell
label: Clash log
description: "
在下方附上 Clash Core 的日志log level 使用 DEBUG
Paste the Clash core log below with the log level set to `DEBUG`.
"
- type: textarea
attributes:
label: Description
validations:
required: true

View File

@@ -0,0 +1,36 @@
name: Feature request
description: Suggest an idea for this project
title: "[Feature] "
body:
- type: checkboxes
id: ensure
attributes:
label: Verify steps
description: "
在提交之前,请确认
Please verify that you've followed these steps
"
options:
- label: "
我已经在 [Issue Tracker](……/) 中找过我要提出的请求
I have searched on the [issue tracker](……/) for a related feature request.
"
required: true
- label: "
我已经仔细看过 [Documentation](https://wiki.metacubex.one/) 并无法找到这个功能
I have read the [documentation](https://wiki.metacubex.one/) and was unable to solve the issue.
"
required: true
- type: textarea
attributes:
label: Description
description: 请详细、清晰地表达你要提出的论述,例如这个问题如何影响到你?你想实现什么功能?目前 Clash Core 的行为是什麽?
validations:
required: true
- type: textarea
attributes:
label: Possible Solution
description: "
此项非必须,但是如果你有想法的话欢迎提出。
Not obligatory, but suggest a fix/reason for the bug, or ideas how to implement the addition or change
"

1
.github/genReleaseNote.sh vendored Executable file
View File

@@ -0,0 +1 @@
git log --pretty=format:"* %s by @%an" v1.14.x..v1.14.y | sort -f | uniq > release.md

View File

@@ -128,7 +128,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v4
with:
go-version: "1.21"
go-version: "1.20"
check-latest: true
- name: Test
@@ -285,7 +285,6 @@ jobs:
generate_release_notes: true
Docker:
if: ${{ github.event_name != 'pull_request' }}
permissions: write-all
needs: [Build]
runs-on: ubuntu-latest

15
.github/workflows/delete.yml vendored Normal file
View File

@@ -0,0 +1,15 @@
name: Delete old workflow runs
on:
schedule:
- cron: "0 0 * * SUN"
jobs:
del_runs:
runs-on: ubuntu-latest
steps:
- name: Delete workflow runs
uses: GitRML/delete-workflow-runs@main
with:
token: ${{ secrets.AUTH_PAT }}
repository: ${{ github.repository }}
retain_days: 30

View File

@@ -4,9 +4,9 @@ RUN echo "I'm building for $TARGETPLATFORM"
RUN apk add --no-cache gzip && \
mkdir /clash-config && \
wget -O /clash-config/geoip.metadb https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geoip.metadb && \
wget -O /clash-config/geosite.dat https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geosite.dat && \
wget -O /clash-config/geoip.dat https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geoip.dat
wget -O /clash-config/Country.mmdb https://raw.githubusercontent.com/Loyalsoldier/geoip/release/Country.mmdb && \
wget -O /clash-config/geosite.dat https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat && \
wget -O /clash-config/geoip.dat https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat
COPY docker/file-name.sh /clash/file-name.sh
WORKDIR /clash

View File

@@ -31,8 +31,6 @@ PLATFORM_LIST = \
linux-mips-hardfloat \
linux-mipsle-softfloat \
linux-mipsle-hardfloat \
linux-riscv64 \
linux-loong64 \
android-arm64 \
freebsd-386 \
freebsd-amd64 \
@@ -105,9 +103,6 @@ linux-mips64le:
linux-riscv64:
GOARCH=riscv64 GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@
linux-loong64:
GOARCH=loong64 GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@
android-arm64:
GOARCH=arm64 GOOS=android $(GOBUILD) -o $(BINDIR)/$(NAME)-$@

View File

@@ -3,41 +3,25 @@ package adapter
import (
"context"
"encoding/json"
"errors"
"fmt"
"net"
"net/http"
"net/netip"
"net/url"
"strconv"
"sync"
"time"
"github.com/Dreamacro/clash/common/atomic"
"github.com/Dreamacro/clash/common/queue"
"github.com/Dreamacro/clash/common/utils"
"github.com/Dreamacro/clash/component/dialer"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/log"
)
var UnifiedDelay = atomic.NewBool(false)
const (
defaultHistoriesNum = 10
)
type extraProxyState struct {
history *queue.Queue[C.DelayHistory]
alive *atomic.Bool
}
type Proxy struct {
C.ProxyAdapter
history *queue.Queue[C.DelayHistory]
alive *atomic.Bool
url string
extra sync.Map
}
// Alive implements C.Proxy
@@ -45,15 +29,6 @@ func (p *Proxy) Alive() bool {
return p.alive.Load()
}
// AliveForTestUrl implements C.Proxy
func (p *Proxy) AliveForTestUrl(url string) bool {
if state, ok := p.extra.Load(url); ok {
return state.(*extraProxyState).alive.Load()
}
return p.alive.Load()
}
// Dial implements C.Proxy
func (p *Proxy) Dial(metadata *C.Metadata) (C.Conn, error) {
ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTCPTimeout)
@@ -87,51 +62,9 @@ func (p *Proxy) DelayHistory() []C.DelayHistory {
for _, item := range queueM {
histories = append(histories, item)
}
return histories
}
// DelayHistoryForTestUrl implements C.Proxy
func (p *Proxy) DelayHistoryForTestUrl(url string) []C.DelayHistory {
var queueM []C.DelayHistory
if state, ok := p.extra.Load(url); ok {
queueM = state.(*extraProxyState).history.Copy()
}
if queueM == nil {
queueM = p.history.Copy()
}
histories := []C.DelayHistory{}
for _, item := range queueM {
histories = append(histories, item)
}
return histories
}
func (p *Proxy) ExtraDelayHistory() map[string][]C.DelayHistory {
extraHistory := map[string][]C.DelayHistory{}
p.extra.Range(func(k, v interface{}) bool {
testUrl := k.(string)
state := v.(*extraProxyState)
histories := []C.DelayHistory{}
queueM := state.history.Copy()
for _, item := range queueM {
histories = append(histories, item)
}
extraHistory[testUrl] = histories
return true
})
return extraHistory
}
// LastDelay return last history record. if proxy is not alive, return the max value of uint16.
// implements C.Proxy
func (p *Proxy) LastDelay() (delay uint16) {
@@ -147,28 +80,6 @@ func (p *Proxy) LastDelay() (delay uint16) {
return history.Delay
}
// LastDelayForTestUrl implements C.Proxy
func (p *Proxy) LastDelayForTestUrl(url string) (delay uint16) {
var max uint16 = 0xffff
alive := p.alive.Load()
history := p.history.Last()
if state, ok := p.extra.Load(url); ok {
alive = state.(*extraProxyState).alive.Load()
history = state.(*extraProxyState).history.Last()
}
if !alive {
return max
}
if history.Delay == 0 {
return max
}
return history.Delay
}
// MarshalJSON implements C.ProxyAdapter
func (p *Proxy) MarshalJSON() ([]byte, error) {
inner, err := p.ProxyAdapter.MarshalJSON()
@@ -179,8 +90,6 @@ func (p *Proxy) MarshalJSON() ([]byte, error) {
mapping := map[string]any{}
_ = json.Unmarshal(inner, &mapping)
mapping["history"] = p.DelayHistory()
mapping["extra"] = p.ExtraDelayHistory()
mapping["alive"] = p.Alive()
mapping["name"] = p.Name()
mapping["udp"] = p.SupportUDP()
mapping["xudp"] = p.SupportXUDP()
@@ -190,49 +99,16 @@ func (p *Proxy) MarshalJSON() ([]byte, error) {
// URLTest get the delay for the specified URL
// implements C.Proxy
func (p *Proxy) URLTest(ctx context.Context, url string, expectedStatus utils.IntRanges[uint16], store C.DelayHistoryStoreType) (t uint16, err error) {
func (p *Proxy) URLTest(ctx context.Context, url string) (t uint16, err error) {
defer func() {
alive := err == nil
store = p.determineFinalStoreType(store, url)
switch store {
case C.OriginalHistory:
p.alive.Store(alive)
record := C.DelayHistory{Time: time.Now()}
if alive {
record.Delay = t
}
p.history.Put(record)
if p.history.Len() > defaultHistoriesNum {
p.history.Pop()
}
// test URL configured by the proxy provider
if len(p.url) == 0 {
p.url = url
}
case C.ExtraHistory:
record := C.DelayHistory{Time: time.Now()}
if alive {
record.Delay = t
}
state, ok := p.extra.Load(url)
if !ok {
state = &extraProxyState{
history: queue.New[C.DelayHistory](defaultHistoriesNum),
alive: atomic.NewBool(true),
}
p.extra.Store(url, state)
}
state.(*extraProxyState).alive.Store(alive)
state.(*extraProxyState).history.Put(record)
if state.(*extraProxyState).history.Len() > defaultHistoriesNum {
state.(*extraProxyState).history.Pop()
}
default:
log.Debugln("health check result will be discarded, url: %s alive: %t, delay: %d", url, alive, t)
p.alive.Store(err == nil)
record := C.DelayHistory{Time: time.Now()}
if err == nil {
record.Delay = t
}
p.history.Put(record)
if p.history.Len() > 10 {
p.history.Pop()
}
}()
@@ -296,22 +172,12 @@ func (p *Proxy) URLTest(ctx context.Context, url string, expectedStatus utils.In
}
}
if expectedStatus != nil && !expectedStatus.Check(uint16(resp.StatusCode)) {
// maybe another value should be returned for differentiation
err = errors.New("response status is inconsistent with the expected status")
}
t = uint16(time.Since(start) / time.Millisecond)
return
}
func NewProxy(adapter C.ProxyAdapter) *Proxy {
return &Proxy{
ProxyAdapter: adapter,
history: queue.New[C.DelayHistory](defaultHistoriesNum),
alive: atomic.NewBool(true),
url: "",
extra: sync.Map{}}
return &Proxy{adapter, queue.New[C.DelayHistory](10), atomic.NewBool(true)}
}
func urlToMetadata(rawURL string) (addr C.Metadata, err error) {
@@ -332,46 +198,11 @@ func urlToMetadata(rawURL string) (addr C.Metadata, err error) {
return
}
}
uintPort, err := strconv.ParseUint(port, 10, 16)
if err != nil {
return
}
addr = C.Metadata{
Host: u.Hostname(),
DstIP: netip.Addr{},
DstPort: uint16(uintPort),
DstPort: port,
}
return
}
func (p *Proxy) determineFinalStoreType(store C.DelayHistoryStoreType, url string) C.DelayHistoryStoreType {
if store != C.DropHistory {
return store
}
if len(p.url) == 0 || url == p.url {
return C.OriginalHistory
}
length := 0
p.extra.Range(func(_, _ interface{}) bool {
length++
return length < 2*C.DefaultMaxHealthCheckUrlNum
})
if length == 0 {
return C.ExtraHistory
}
_, ok := p.extra.Load(url)
if ok {
return C.ExtraHistory
}
if length < 2*C.DefaultMaxHealthCheckUrlNum {
return C.ExtraHistory
}
return store
}

View File

@@ -17,10 +17,6 @@ func SetTfo(open bool) {
lc.DisableTFO = !open
}
func SetMPTCP(open bool) {
setMultiPathTCP(&lc.ListenConfig, open)
}
func ListenContext(ctx context.Context, network, address string) (net.Listener, error) {
return lc.Listen(ctx, network, address)
}

View File

@@ -1,10 +0,0 @@
//go:build !go1.21
package inbound
import "net"
const multipathTCPAvailable = false
func setMultiPathTCP(listenConfig *net.ListenConfig, open bool) {
}

View File

@@ -1,11 +0,0 @@
//go:build go1.21
package inbound
import "net"
const multipathTCPAvailable = true
func setMultiPathTCP(listenConfig *net.ListenConfig, open bool) {
listenConfig.SetMultipathTCP(open)
}

View File

@@ -3,7 +3,6 @@ package inbound
import (
"net"
"net/netip"
"strconv"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/context"
@@ -38,9 +37,7 @@ func NewInner(conn net.Conn, address string) *context.ConnContext {
metadata.DNSMode = C.DNSNormal
metadata.Process = C.ClashName
if h, port, err := net.SplitHostPort(address); err == nil {
if port, err := strconv.ParseUint(port, 10, 16); err == nil {
metadata.DstPort = uint16(port)
}
metadata.DstPort = port
if ip, err := netip.ParseAddr(h); err == nil {
metadata.DstIP = ip
} else {

View File

@@ -20,14 +20,14 @@ func parseSocksAddr(target socks5.Addr) *C.Metadata {
case socks5.AtypDomainName:
// trim for FQDN
metadata.Host = strings.TrimRight(string(target[2:2+target[1]]), ".")
metadata.DstPort = uint16((int(target[2+target[1]]) << 8) | int(target[2+target[1]+1]))
metadata.DstPort = strconv.Itoa((int(target[2+target[1]]) << 8) | int(target[2+target[1]+1]))
case socks5.AtypIPv4:
metadata.DstIP = nnip.IpToAddr(net.IP(target[1 : 1+net.IPv4len]))
metadata.DstPort = uint16((int(target[1+net.IPv4len]) << 8) | int(target[1+net.IPv4len+1]))
metadata.DstPort = strconv.Itoa((int(target[1+net.IPv4len]) << 8) | int(target[1+net.IPv4len+1]))
case socks5.AtypIPv6:
ip6, _ := netip.AddrFromSlice(target[1 : 1+net.IPv6len])
metadata.DstIP = ip6.Unmap()
metadata.DstPort = uint16((int(target[1+net.IPv6len]) << 8) | int(target[1+net.IPv6len+1]))
metadata.DstPort = strconv.Itoa((int(target[1+net.IPv6len]) << 8) | int(target[1+net.IPv6len+1]))
}
return metadata
@@ -43,16 +43,11 @@ func parseHTTPAddr(request *http.Request) *C.Metadata {
// trim FQDN (#737)
host = strings.TrimRight(host, ".")
var uint16Port uint16
if port, err := strconv.ParseUint(port, 10, 16); err == nil {
uint16Port = uint16(port)
}
metadata := &C.Metadata{
NetWork: C.TCP,
Host: host,
DstIP: netip.Addr{},
DstPort: uint16Port,
DstPort: port,
}
ip, err := netip.ParseAddr(host)
@@ -63,10 +58,10 @@ func parseHTTPAddr(request *http.Request) *C.Metadata {
return metadata
}
func parseAddr(addr net.Addr) (netip.Addr, uint16, error) {
func parseAddr(addr net.Addr) (netip.Addr, string, error) {
// Filter when net.Addr interface is nil
if addr == nil {
return netip.Addr{}, 0, errors.New("nil addr")
return netip.Addr{}, "", errors.New("nil addr")
}
if rawAddr, ok := addr.(interface{ RawAddr() net.Addr }); ok {
ip, port, err := parseAddr(rawAddr.RawAddr())
@@ -77,14 +72,9 @@ func parseAddr(addr net.Addr) (netip.Addr, uint16, error) {
addrStr := addr.String()
host, port, err := net.SplitHostPort(addrStr)
if err != nil {
return netip.Addr{}, 0, err
}
var uint16Port uint16
if port, err := strconv.ParseUint(port, 10, 16); err == nil {
uint16Port = uint16(port)
return netip.Addr{}, "", err
}
ip, err := netip.ParseAddr(host)
return ip, uint16Port, err
return ip, port, err
}

View File

@@ -21,7 +21,6 @@ type Base struct {
udp bool
xudp bool
tfo bool
mpTcp bool
rmark int
id string
prefer C.DNSPrefer
@@ -144,16 +143,11 @@ func (b *Base) DialOptions(opts ...dialer.Option) []dialer.Option {
opts = append(opts, dialer.WithTFO(true))
}
if b.mpTcp {
opts = append(opts, dialer.WithMPTCP(true))
}
return opts
}
type BasicOption struct {
TFO bool `proxy:"tfo,omitempty" group:"tfo,omitempty"`
MPTCP bool `proxy:"mptcp,omitempty" group:"mptcp,omitempty"`
Interface string `proxy:"interface-name,omitempty" group:"interface-name,omitempty"`
RoutingMark int `proxy:"routing-mark,omitempty" group:"routing-mark,omitempty"`
IPVersion string `proxy:"ip-version,omitempty" group:"ip-version,omitempty"`
@@ -167,7 +161,6 @@ type BaseOption struct {
UDP bool
XUDP bool
TFO bool
MPTCP bool
Interface string
RoutingMark int
Prefer C.DNSPrefer
@@ -181,7 +174,6 @@ func NewBase(opt BaseOption) *Base {
udp: opt.UDP,
xudp: opt.XUDP,
tfo: opt.TFO,
mpTcp: opt.MPTCP,
iface: opt.Interface,
rmark: opt.RoutingMark,
prefer: opt.Prefer,

View File

@@ -12,11 +12,6 @@ type Direct struct {
*Base
}
type DirectOption struct {
BasicOption
Name string `proxy:"name"`
}
// DialContext implements C.ProxyAdapter
func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) {
opts = append(opts, dialer.WithResolver(resolver.DefaultResolver))
@@ -45,21 +40,6 @@ func (d *Direct) ListenPacketContext(ctx context.Context, metadata *C.Metadata,
return newPacketConn(pc, d), nil
}
func NewDirectWithOption(option DirectOption) *Direct {
return &Direct{
Base: &Base{
name: option.Name,
tp: C.Direct,
udp: true,
tfo: option.TFO,
mpTcp: option.MPTCP,
iface: option.Interface,
rmark: option.RoutingMark,
prefer: C.NewDNSPrefer(option.IPVersion),
},
}
}
func NewDirect() *Direct {
return &Direct{
Base: &Base{

View File

@@ -177,7 +177,6 @@ func NewHttp(option HttpOption) (*Http, error) {
addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)),
tp: C.Http,
tfo: option.TFO,
mpTcp: option.MPTCP,
iface: option.Interface,
rmark: option.RoutingMark,
prefer: C.NewDNSPrefer(option.IPVersion),

View File

@@ -19,7 +19,7 @@ import (
v2rayObfs "github.com/Dreamacro/clash/transport/v2ray-plugin"
restlsC "github.com/3andne/restls-client-go"
shadowsocks "github.com/metacubex/sing-shadowsocks2"
"github.com/metacubex/sing-shadowsocks2"
M "github.com/sagernet/sing/common/metadata"
"github.com/sagernet/sing/common/uot"
)
@@ -294,6 +294,7 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
}
restlsConfig, err = restlsC.NewRestlsConfig(restlsOpt.Host, restlsOpt.Password, restlsOpt.VersionHint, restlsOpt.RestlsScript, option.ClientFingerprint)
restlsConfig.SessionTicketsDisabled = true
if err != nil {
return nil, fmt.Errorf("ss %s initialize restls-plugin error: %w", addr, err)
}
@@ -314,7 +315,6 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
tp: C.Shadowsocks,
udp: option.UDP,
tfo: option.TFO,
mpTcp: option.MPTCP,
iface: option.Interface,
rmark: option.RoutingMark,
prefer: C.NewDNSPrefer(option.IPVersion),

View File

@@ -181,7 +181,6 @@ func NewShadowSocksR(option ShadowSocksROption) (*ShadowSocksR, error) {
tp: C.ShadowsocksR,
udp: option.UDP,
tfo: option.TFO,
mpTcp: option.MPTCP,
iface: option.Interface,
rmark: option.RoutingMark,
prefer: C.NewDNSPrefer(option.IPVersion),

View File

@@ -97,7 +97,7 @@ func (s *SingMux) ListenPacketContext(ctx context.Context, metadata *C.Metadata,
func (s *SingMux) SupportUDP() bool {
if s.onlyTcp {
return s.ProxyAdapter.SupportUDP()
return s.ProxyAdapter.SupportUOT()
}
return true
}

View File

@@ -59,7 +59,8 @@ func (s *Snell) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.M
err := snell.WriteUDPHeader(c, s.version)
return c, err
}
err := snell.WriteHeader(c, metadata.String(), uint(metadata.DstPort), s.version)
port, _ := strconv.ParseUint(metadata.DstPort, 10, 16)
err := snell.WriteHeader(c, metadata.String(), uint(port), s.version)
return c, err
}
@@ -71,7 +72,8 @@ func (s *Snell) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d
return nil, err
}
if err = snell.WriteHeader(c, metadata.String(), uint(metadata.DstPort), s.version); err != nil {
port, _ := strconv.ParseUint(metadata.DstPort, 10, 16)
if err = snell.WriteHeader(c, metadata.String(), uint(port), s.version); err != nil {
c.Close()
return nil, err
}
@@ -181,7 +183,6 @@ func NewSnell(option SnellOption) (*Snell, error) {
tp: C.Snell,
udp: option.UDP,
tfo: option.TFO,
mpTcp: option.MPTCP,
iface: option.Interface,
rmark: option.RoutingMark,
prefer: C.NewDNSPrefer(option.IPVersion),

View File

@@ -196,7 +196,6 @@ func NewSocks5(option Socks5Option) (*Socks5, error) {
tp: C.Socks5,
udp: option.UDP,
tfo: option.TFO,
mpTcp: option.MPTCP,
iface: option.Interface,
rmark: option.RoutingMark,
prefer: C.NewDNSPrefer(option.IPVersion),

View File

@@ -14,6 +14,7 @@ import (
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/transport/gun"
"github.com/Dreamacro/clash/transport/trojan"
"github.com/Dreamacro/clash/transport/vless"
)
type Trojan struct {
@@ -44,6 +45,8 @@ type TrojanOption struct {
RealityOpts RealityOptions `proxy:"reality-opts,omitempty"`
GrpcOpts GrpcOptions `proxy:"grpc-opts,omitempty"`
WSOpts WSOptions `proxy:"ws-opts,omitempty"`
Flow string `proxy:"flow,omitempty"`
FlowShow bool `proxy:"flow-show,omitempty"`
ClientFingerprint string `proxy:"client-fingerprint,omitempty"`
}
@@ -92,6 +95,11 @@ func (t *Trojan) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.
return nil, fmt.Errorf("%s connect error: %w", t.addr, err)
}
c, err = t.instance.PresetXTLSConn(c)
if err != nil {
return nil, err
}
if metadata.NetWork == C.UDP {
err = t.instance.WriteHeader(c, trojan.CommandUDP, serializesSocksAddr(metadata))
return c, err
@@ -109,6 +117,12 @@ func (t *Trojan) DialContext(ctx context.Context, metadata *C.Metadata, opts ...
return nil, err
}
c, err = t.instance.PresetXTLSConn(c)
if err != nil {
c.Close()
return nil, err
}
if err = t.instance.WriteHeader(c, trojan.CommandTCP, serializesSocksAddr(metadata)); err != nil {
c.Close()
return nil, err
@@ -223,10 +237,24 @@ func NewTrojan(option TrojanOption) (*Trojan, error) {
ALPN: option.ALPN,
ServerName: option.Server,
SkipCertVerify: option.SkipCertVerify,
FlowShow: option.FlowShow,
Fingerprint: option.Fingerprint,
ClientFingerprint: option.ClientFingerprint,
}
switch option.Network {
case "", "tcp":
if len(option.Flow) >= 16 {
option.Flow = option.Flow[:16]
switch option.Flow {
case vless.XRO, vless.XRD, vless.XRS:
tOption.Flow = option.Flow
default:
return nil, fmt.Errorf("unsupported xtls flow type: %s", option.Flow)
}
}
}
if option.SNI != "" {
tOption.ServerName = option.SNI
}
@@ -238,7 +266,6 @@ func NewTrojan(option TrojanOption) (*Trojan, error) {
tp: C.Trojan,
udp: option.UDP,
tfo: option.TFO,
mpTcp: option.MPTCP,
iface: option.Interface,
rmark: option.RoutingMark,
prefer: C.NewDNSPrefer(option.IPVersion),

View File

@@ -6,7 +6,6 @@ import (
"crypto/tls"
"encoding/hex"
"encoding/pem"
"errors"
"fmt"
"math"
"net"
@@ -14,17 +13,13 @@ import (
"strconv"
"time"
"github.com/metacubex/quic-go"
"github.com/Dreamacro/clash/component/dialer"
"github.com/Dreamacro/clash/component/proxydialer"
"github.com/Dreamacro/clash/component/resolver"
tlsC "github.com/Dreamacro/clash/component/tls"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/transport/tuic"
"github.com/gofrs/uuid/v5"
"github.com/metacubex/quic-go"
M "github.com/sagernet/sing/common/metadata"
"github.com/sagernet/sing/common/uot"
)
type Tuic struct {
@@ -38,9 +33,7 @@ type TuicOption struct {
Name string `proxy:"name"`
Server string `proxy:"server"`
Port int `proxy:"port"`
Token string `proxy:"token,omitempty"`
UUID string `proxy:"uuid,omitempty"`
Password string `proxy:"password,omitempty"`
Token string `proxy:"token"`
Ip string `proxy:"ip,omitempty"`
HeartbeatInterval int `proxy:"heartbeat-interval,omitempty"`
ALPN []string `proxy:"alpn,omitempty"`
@@ -53,7 +46,6 @@ type TuicOption struct {
FastOpen bool `proxy:"fast-open,omitempty"`
MaxOpenStreams int `proxy:"max-open-streams,omitempty"`
CWND int `proxy:"cwnd,omitempty"`
SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"`
Fingerprint string `proxy:"fingerprint,omitempty"`
CustomCA string `proxy:"ca,omitempty"`
@@ -63,9 +55,6 @@ type TuicOption struct {
DisableMTUDiscovery bool `proxy:"disable-mtu-discovery,omitempty"`
MaxDatagramFrameSize int `proxy:"max-datagram-frame-size,omitempty"`
SNI string `proxy:"sni,omitempty"`
UDPOverStream bool `proxy:"udp-over-stream,omitempty"`
UDPOverStreamVersion int `proxy:"udp-over-stream-version,omitempty"`
}
// DialContext implements C.ProxyAdapter
@@ -89,32 +78,6 @@ func (t *Tuic) ListenPacketContext(ctx context.Context, metadata *C.Metadata, op
// ListenPacketWithDialer implements C.ProxyAdapter
func (t *Tuic) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) {
if t.option.UDPOverStream {
uotDestination := uot.RequestDestination(uint8(t.option.UDPOverStreamVersion))
uotMetadata := *metadata
uotMetadata.Host = uotDestination.Fqdn
uotMetadata.DstPort = uotDestination.Port
c, err := t.DialContextWithDialer(ctx, dialer, &uotMetadata)
if err != nil {
return nil, err
}
// tuic uos use stream-oriented udp with a special address, so we need a net.UDPAddr
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())
if t.option.UDPOverStreamVersion == uot.LegacyVersion {
return newPacketConn(uot.NewConn(c, uot.Request{Destination: destination}), t), nil
} else {
return newPacketConn(uot.NewLazyConn(c, uot.Request{Destination: destination}), t), nil
}
}
pc, err := t.client.ListenPacketWithDialer(ctx, metadata, dialer, t.dialWithDialer)
if err != nil {
return nil, err
@@ -127,7 +90,11 @@ func (t *Tuic) SupportWithDialer() C.NetWork {
return C.ALLNet
}
func (t *Tuic) dialWithDialer(ctx context.Context, dialer C.Dialer) (transport *quic.Transport, addr net.Addr, err error) {
func (t *Tuic) dial(ctx context.Context, opts ...dialer.Option) (pc net.PacketConn, addr net.Addr, err error) {
return t.dialWithDialer(ctx, dialer.NewDialer(opts...))
}
func (t *Tuic) dialWithDialer(ctx context.Context, dialer C.Dialer) (pc net.PacketConn, addr net.Addr, err error) {
if len(t.option.DialerProxy) > 0 {
dialer, err = proxydialer.NewByName(t.option.DialerProxy, dialer)
if err != nil {
@@ -139,14 +106,10 @@ func (t *Tuic) dialWithDialer(ctx context.Context, dialer C.Dialer) (transport *
return nil, nil, err
}
addr = udpAddr
var pc net.PacketConn
pc, err = dialer.ListenPacket(ctx, "udp", "", udpAddr.AddrPort())
if err != nil {
return nil, nil, err
}
transport = &quic.Transport{Conn: pc}
transport.SetCreatedConn(true) // auto close conn
transport.SetSingleUse(true) // auto close transport
return
}
@@ -195,7 +158,7 @@ func NewTuic(option TuicOption) (*Tuic, error) {
tlsConfig = tlsC.GetGlobalTLSConfig(tlsConfig)
}
if option.ALPN != nil { // structure's Decode will ensure value not nil when input has value even it was set an empty array
if len(option.ALPN) > 0 {
tlsConfig.NextProtos = option.ALPN
} else {
tlsConfig.NextProtos = []string{"h3"}
@@ -209,9 +172,8 @@ func NewTuic(option TuicOption) (*Tuic, error) {
option.HeartbeatInterval = 10000
}
udpRelayMode := tuic.QUIC
if option.UdpRelayMode != "quic" {
udpRelayMode = tuic.NATIVE
option.UdpRelayMode = "native"
}
if option.MaxUdpRelayPacketSize == 0 {
@@ -222,23 +184,14 @@ func NewTuic(option TuicOption) (*Tuic, error) {
option.MaxOpenStreams = 100
}
if option.CWND == 0 {
option.CWND = 32
}
packetOverHead := tuic.PacketOverHeadV4
if len(option.Token) == 0 {
packetOverHead = tuic.PacketOverHeadV5
}
if option.MaxDatagramFrameSize == 0 {
option.MaxDatagramFrameSize = option.MaxUdpRelayPacketSize + packetOverHead
option.MaxDatagramFrameSize = option.MaxUdpRelayPacketSize + tuic.PacketOverHead
}
if option.MaxDatagramFrameSize > 1400 {
option.MaxDatagramFrameSize = 1400
}
option.MaxUdpRelayPacketSize = option.MaxDatagramFrameSize - packetOverHead
option.MaxUdpRelayPacketSize = option.MaxDatagramFrameSize - tuic.PacketOverHead
// ensure server's incoming stream can handle correctly, increase to 1.1x
quicMaxOpenStreams := int64(option.MaxOpenStreams)
@@ -267,18 +220,12 @@ func NewTuic(option TuicOption) (*Tuic, error) {
if len(option.Ip) > 0 {
addr = net.JoinHostPort(option.Ip, strconv.Itoa(option.Port))
}
host := option.Server
if option.DisableSni {
host = ""
tlsConfig.ServerName = ""
tlsConfig.InsecureSkipVerify = true // tls: either ServerName or InsecureSkipVerify must be specified in the tls.Config
}
switch option.UDPOverStreamVersion {
case uot.Version, uot.LegacyVersion:
case 0:
option.UDPOverStreamVersion = uot.LegacyVersion
default:
return nil, fmt.Errorf("tuic %s unknown udp over stream protocol version: %d", addr, option.UDPOverStreamVersion)
}
tkn := tuic.GenTKN(option.Token)
t := &Tuic{
Base: &Base{
@@ -304,40 +251,21 @@ func NewTuic(option TuicOption) (*Tuic, error) {
if clientMaxOpenStreams < 1 {
clientMaxOpenStreams = 1
}
if len(option.Token) > 0 {
tkn := tuic.GenTKN(option.Token)
clientOption := &tuic.ClientOptionV4{
TlsConfig: tlsConfig,
QuicConfig: quicConfig,
Token: tkn,
UdpRelayMode: udpRelayMode,
CongestionController: option.CongestionController,
ReduceRtt: option.ReduceRtt,
RequestTimeout: time.Duration(option.RequestTimeout) * time.Millisecond,
MaxUdpRelayPacketSize: option.MaxUdpRelayPacketSize,
FastOpen: option.FastOpen,
MaxOpenStreams: clientMaxOpenStreams,
CWND: option.CWND,
}
t.client = tuic.NewPoolClientV4(clientOption)
} else {
clientOption := &tuic.ClientOptionV5{
TlsConfig: tlsConfig,
QuicConfig: quicConfig,
Uuid: uuid.FromStringOrNil(option.UUID),
Password: option.Password,
UdpRelayMode: udpRelayMode,
CongestionController: option.CongestionController,
ReduceRtt: option.ReduceRtt,
MaxUdpRelayPacketSize: option.MaxUdpRelayPacketSize,
MaxOpenStreams: clientMaxOpenStreams,
CWND: option.CWND,
}
t.client = tuic.NewPoolClientV5(clientOption)
clientOption := &tuic.ClientOption{
TlsConfig: tlsConfig,
QuicConfig: quicConfig,
Host: host,
Token: tkn,
UdpRelayMode: option.UdpRelayMode,
CongestionController: option.CongestionController,
ReduceRtt: option.ReduceRtt,
RequestTimeout: time.Duration(option.RequestTimeout) * time.Millisecond,
MaxUdpRelayPacketSize: option.MaxUdpRelayPacketSize,
FastOpen: option.FastOpen,
MaxOpenStreams: clientMaxOpenStreams,
}
t.client = tuic.NewPoolClient(clientOption)
return t, nil
}

View File

@@ -4,8 +4,10 @@ import (
"bytes"
"context"
"crypto/tls"
xtls "github.com/xtls/go"
"net"
"net/netip"
"strconv"
"sync"
"time"
@@ -15,8 +17,9 @@ import (
)
var (
globalClientSessionCache tls.ClientSessionCache
once sync.Once
globalClientSessionCache tls.ClientSessionCache
globalClientXSessionCache xtls.ClientSessionCache
once sync.Once
)
func tcpKeepAlive(c net.Conn) {
@@ -33,11 +36,18 @@ func getClientSessionCache() tls.ClientSessionCache {
return globalClientSessionCache
}
func getClientXSessionCache() xtls.ClientSessionCache {
once.Do(func() {
globalClientXSessionCache = xtls.NewLRUClientSessionCache(128)
})
return globalClientXSessionCache
}
func serializesSocksAddr(metadata *C.Metadata) []byte {
var buf [][]byte
addrType := metadata.AddrType()
aType := uint8(addrType)
p := uint(metadata.DstPort)
p, _ := strconv.ParseUint(metadata.DstPort, 10, 16)
port := []byte{uint8(p >> 8), uint8(p & 0xff)}
switch addrType {
case socks5.AtypDomainName:

View File

@@ -14,7 +14,6 @@ import (
"github.com/Dreamacro/clash/common/convert"
N "github.com/Dreamacro/clash/common/net"
"github.com/Dreamacro/clash/common/utils"
"github.com/Dreamacro/clash/component/dialer"
"github.com/Dreamacro/clash/component/proxydialer"
"github.com/Dreamacro/clash/component/resolver"
@@ -26,8 +25,8 @@ import (
"github.com/Dreamacro/clash/transport/vless"
"github.com/Dreamacro/clash/transport/vmess"
vmessSing "github.com/metacubex/sing-vmess"
"github.com/metacubex/sing-vmess/packetaddr"
vmessSing "github.com/sagernet/sing-vmess"
"github.com/sagernet/sing-vmess/packetaddr"
M "github.com/sagernet/sing/common/metadata"
)
@@ -56,8 +55,8 @@ type VlessOption struct {
Port int `proxy:"port"`
UUID string `proxy:"uuid"`
Flow string `proxy:"flow,omitempty"`
FlowShow bool `proxy:"flow-show,omitempty"`
TLS bool `proxy:"tls,omitempty"`
ALPN []string `proxy:"alpn,omitempty"`
UDP bool `proxy:"udp,omitempty"`
PacketAddr bool `proxy:"packet-addr,omitempty"`
XUDP bool `proxy:"xudp,omitempty"`
@@ -133,7 +132,7 @@ func (v *Vless) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.M
c, err = vmess.StreamWebsocketConn(ctx, c, wsOpts)
case "http":
// readability first, so just copy default TLS logic
c, err = v.streamTLSConn(ctx, c, false)
c, err = v.streamTLSOrXTLSConn(ctx, c, false)
if err != nil {
return nil, err
}
@@ -148,7 +147,7 @@ func (v *Vless) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.M
c = vmess.StreamHTTPConn(c, httpOpts)
case "h2":
c, err = v.streamTLSConn(ctx, c, true)
c, err = v.streamTLSOrXTLSConn(ctx, c, true)
if err != nil {
return nil, err
}
@@ -163,8 +162,8 @@ func (v *Vless) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.M
c, err = gun.StreamGunWithConn(c, v.gunTLSConfig, v.gunConfig, v.realityConfig)
default:
// default tcp network
// handle TLS
c, err = v.streamTLSConn(ctx, c, false)
// handle TLS And XTLS
c, err = v.streamTLSOrXTLSConn(ctx, c, false)
}
if err != nil {
@@ -180,7 +179,7 @@ func (v *Vless) streamConn(c net.Conn, metadata *C.Metadata) (conn net.Conn, err
metadata = &C.Metadata{
NetWork: C.UDP,
Host: packetaddr.SeqPacketMagicAddress,
DstPort: 443,
DstPort: "443",
}
} else {
metadata = &C.Metadata{ // a clear metadata only contains ip
@@ -202,17 +201,29 @@ func (v *Vless) streamConn(c net.Conn, metadata *C.Metadata) (conn net.Conn, err
return
}
func (v *Vless) streamTLSConn(ctx context.Context, conn net.Conn, isH2 bool) (net.Conn, error) {
if v.option.TLS {
host, _, _ := net.SplitHostPort(v.addr)
func (v *Vless) streamTLSOrXTLSConn(ctx context.Context, conn net.Conn, isH2 bool) (net.Conn, error) {
host, _, _ := net.SplitHostPort(v.addr)
if v.isLegacyXTLSEnabled() && !isH2 {
xtlsOpts := vless.XTLSConfig{
Host: host,
SkipCertVerify: v.option.SkipCertVerify,
Fingerprint: v.option.Fingerprint,
}
if v.option.ServerName != "" {
xtlsOpts.Host = v.option.ServerName
}
return vless.StreamXTLSConn(ctx, conn, &xtlsOpts)
} else if v.option.TLS {
tlsOpts := vmess.TLSConfig{
Host: host,
SkipCertVerify: v.option.SkipCertVerify,
FingerPrint: v.option.Fingerprint,
ClientFingerprint: v.option.ClientFingerprint,
Reality: v.realityConfig,
NextProtos: v.option.ALPN,
}
if isH2 {
@@ -229,6 +240,10 @@ func (v *Vless) streamTLSConn(ctx context.Context, conn net.Conn, isH2 bool) (ne
return conn, nil
}
func (v *Vless) isLegacyXTLSEnabled() bool {
return v.client.Addons != nil && v.client.Addons.Flow != vless.XRV
}
// DialContext implements C.ProxyAdapter
func (v *Vless) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) {
// gun transport
@@ -358,14 +373,8 @@ func (v *Vless) ListenPacketOnStreamConn(ctx context.Context, c net.Conn, metada
}
if v.option.XUDP {
var globalID [8]byte
if metadata.SourceValid() {
globalID = utils.GlobalID(metadata.SourceAddress())
}
return newPacketConn(N.NewThreadSafePacketConn(
vmessSing.NewXUDPConn(c,
globalID,
M.SocksaddrFromNet(metadata.UDPAddr())),
vmessSing.NewXUDPConn(c, M.SocksaddrFromNet(metadata.UDPAddr())),
), v), nil
} else if v.option.PacketAddr {
return newPacketConn(N.NewThreadSafePacketConn(
@@ -401,11 +410,12 @@ func parseVlessAddr(metadata *C.Metadata, xudp bool) *vless.DstAddr {
copy(addr[1:], metadata.Host)
}
port, _ := strconv.ParseUint(metadata.DstPort, 10, 16)
return &vless.DstAddr{
UDP: metadata.NetWork == C.UDP,
AddrType: addrType,
Addr: addr,
Port: metadata.DstPort,
Port: uint16(port),
Mux: metadata.NetWork == C.UDP && xudp,
}
}
@@ -509,11 +519,11 @@ func NewVless(option VlessOption) (*Vless, error) {
switch option.Flow {
case vless.XRV:
log.Warnln("To use %s, ensure your server is upgrade to Xray-core v1.8.0+", vless.XRV)
fallthrough
case vless.XRO, vless.XRD, vless.XRS:
addons = &vless.Addons{
Flow: option.Flow,
}
case vless.XRO, vless.XRD, vless.XRS:
log.Fatalln("Legacy XTLS protocol %s is deprecated and no longer supported", option.Flow)
default:
return nil, fmt.Errorf("unsupported xtls flow type: %s", option.Flow)
}
@@ -532,7 +542,7 @@ func NewVless(option VlessOption) (*Vless, error) {
option.PacketAddr = false
}
client, err := vless.NewClient(option.UUID, addons)
client, err := vless.NewClient(option.UUID, addons, option.FlowShow)
if err != nil {
return nil, err
}
@@ -545,7 +555,6 @@ func NewVless(option VlessOption) (*Vless, error) {
udp: option.UDP,
xudp: option.XUDP,
tfo: option.TFO,
mpTcp: option.MPTCP,
iface: option.Interface,
rmark: option.RoutingMark,
prefer: C.NewDNSPrefer(option.IPVersion),

View File

@@ -12,18 +12,16 @@ import (
"sync"
N "github.com/Dreamacro/clash/common/net"
"github.com/Dreamacro/clash/common/utils"
"github.com/Dreamacro/clash/component/dialer"
"github.com/Dreamacro/clash/component/proxydialer"
"github.com/Dreamacro/clash/component/resolver"
tlsC "github.com/Dreamacro/clash/component/tls"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/ntp"
"github.com/Dreamacro/clash/transport/gun"
clashVMess "github.com/Dreamacro/clash/transport/vmess"
vmess "github.com/metacubex/sing-vmess"
"github.com/metacubex/sing-vmess/packetaddr"
vmess "github.com/sagernet/sing-vmess"
"github.com/sagernet/sing-vmess/packetaddr"
M "github.com/sagernet/sing/common/metadata"
)
@@ -53,7 +51,6 @@ type VmessOption struct {
UDP bool `proxy:"udp,omitempty"`
Network string `proxy:"network,omitempty"`
TLS bool `proxy:"tls,omitempty"`
ALPN []string `proxy:"alpn,omitempty"`
SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"`
Fingerprint string `proxy:"fingerprint,omitempty"`
ServerName string `proxy:"servername,omitempty"`
@@ -151,7 +148,6 @@ func (v *Vmess) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.M
SkipCertVerify: v.option.SkipCertVerify,
ClientFingerprint: v.option.ClientFingerprint,
Reality: v.realityConfig,
NextProtos: v.option.ALPN,
}
if v.option.ServerName != "" {
@@ -208,7 +204,6 @@ func (v *Vmess) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.M
SkipCertVerify: v.option.SkipCertVerify,
ClientFingerprint: v.option.ClientFingerprint,
Reality: v.realityConfig,
NextProtos: v.option.ALPN,
}
if v.option.ServerName != "" {
@@ -228,44 +223,30 @@ func (v *Vmess) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.M
func (v *Vmess) streamConn(c net.Conn, metadata *C.Metadata) (conn net.Conn, err error) {
if metadata.NetWork == C.UDP {
if v.option.XUDP {
var globalID [8]byte
if metadata.SourceValid() {
globalID = utils.GlobalID(metadata.SourceAddress())
}
if N.NeedHandshake(c) {
conn = v.client.DialEarlyXUDPPacketConn(c,
globalID,
M.SocksaddrFromNet(metadata.UDPAddr()))
conn = v.client.DialEarlyXUDPPacketConn(c, M.SocksaddrFromNet(metadata.UDPAddr()))
} else {
conn, err = v.client.DialXUDPPacketConn(c,
globalID,
M.SocksaddrFromNet(metadata.UDPAddr()))
conn, err = v.client.DialXUDPPacketConn(c, M.SocksaddrFromNet(metadata.UDPAddr()))
}
} else if v.option.PacketAddr {
if N.NeedHandshake(c) {
conn = v.client.DialEarlyPacketConn(c,
M.ParseSocksaddrHostPort(packetaddr.SeqPacketMagicAddress, 443))
conn = v.client.DialEarlyPacketConn(c, M.ParseSocksaddrHostPort(packetaddr.SeqPacketMagicAddress, 443))
} else {
conn, err = v.client.DialPacketConn(c,
M.ParseSocksaddrHostPort(packetaddr.SeqPacketMagicAddress, 443))
conn, err = v.client.DialPacketConn(c, M.ParseSocksaddrHostPort(packetaddr.SeqPacketMagicAddress, 443))
}
conn = packetaddr.NewBindConn(conn)
} else {
if N.NeedHandshake(c) {
conn = v.client.DialEarlyPacketConn(c,
M.SocksaddrFromNet(metadata.UDPAddr()))
conn = v.client.DialEarlyPacketConn(c, M.SocksaddrFromNet(metadata.UDPAddr()))
} else {
conn, err = v.client.DialPacketConn(c,
M.SocksaddrFromNet(metadata.UDPAddr()))
conn, err = v.client.DialPacketConn(c, M.SocksaddrFromNet(metadata.UDPAddr()))
}
}
} else {
if N.NeedHandshake(c) {
conn = v.client.DialEarlyConn(c,
M.ParseSocksaddr(metadata.RemoteAddress()))
conn = v.client.DialEarlyConn(c, M.ParseSocksaddr(metadata.RemoteAddress()))
} else {
conn, err = v.client.DialConn(c,
M.ParseSocksaddr(metadata.RemoteAddress()))
conn, err = v.client.DialConn(c, M.ParseSocksaddr(metadata.RemoteAddress()))
}
}
if err != nil {
@@ -417,7 +398,6 @@ func NewVmess(option VmessOption) (*Vmess, error) {
if option.AuthenticatedLength {
options = append(options, vmess.ClientWithAuthenticatedLength())
}
options = append(options, vmess.ClientWithTimeFunc(ntp.Now))
client, err := vmess.NewClient(option.UUID, security, option.AlterID, options...)
if err != nil {
return nil, err
@@ -441,7 +421,6 @@ func NewVmess(option VmessOption) (*Vmess, error) {
udp: option.UDP,
xudp: option.XUDP,
tfo: option.TFO,
mpTcp: option.MPTCP,
iface: option.Interface,
rmark: option.RoutingMark,
prefer: C.NewDNSPrefer(option.IPVersion),

View File

@@ -302,7 +302,7 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) {
if err != nil {
return nil, E.Cause(err, "create WireGuard device")
}
outbound.device = device.NewDevice(context.Background(), outbound.tunDevice, outbound.bind, &device.Logger{
outbound.device = device.NewDevice(outbound.tunDevice, outbound.bind, &device.Logger{
Verbosef: func(format string, args ...interface{}) {
log.SingLogger.Debug(fmt.Sprintf("[WG](%s) %s", option.Name, fmt.Sprintf(format, args...)))
},
@@ -374,7 +374,8 @@ func (w *WireGuard) DialContext(ctx context.Context, metadata *C.Metadata, opts
options = append(options, dialer.WithNetDialer(wgNetDialer{tunDevice: w.tunDevice}))
conn, err = dialer.NewDialer(options...).DialContext(ctx, "tcp", metadata.RemoteAddress())
} else {
conn, err = w.tunDevice.DialContext(ctx, "tcp", M.SocksaddrFrom(metadata.DstIP, metadata.DstPort).Unwrap())
port, _ := strconv.Atoi(metadata.DstPort)
conn, err = w.tunDevice.DialContext(ctx, "tcp", M.SocksaddrFrom(metadata.DstIP, uint16(port)).Unwrap())
}
if err != nil {
return nil, err
@@ -411,7 +412,8 @@ func (w *WireGuard) ListenPacketContext(ctx context.Context, metadata *C.Metadat
}
metadata.DstIP = ip
}
pc, err = w.tunDevice.ListenPacket(ctx, M.SocksaddrFrom(metadata.DstIP, metadata.DstPort).Unwrap())
port, _ := strconv.Atoi(metadata.DstPort)
pc, err = w.tunDevice.ListenPacket(ctx, M.SocksaddrFrom(metadata.DstIP, uint16(port)).Unwrap())
if err != nil {
return nil, err
}

View File

@@ -9,7 +9,6 @@ import (
"github.com/Dreamacro/clash/adapter/outbound"
"github.com/Dreamacro/clash/common/callback"
N "github.com/Dreamacro/clash/common/net"
"github.com/Dreamacro/clash/common/utils"
"github.com/Dreamacro/clash/component/dialer"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/constant/provider"
@@ -17,10 +16,9 @@ import (
type Fallback struct {
*GroupBase
disableUDP bool
testUrl string
selected string
expectedStatus string
disableUDP bool
testUrl string
selected string
}
func (f *Fallback) Now() string {
@@ -84,11 +82,9 @@ func (f *Fallback) MarshalJSON() ([]byte, error) {
all = append(all, proxy.Name())
}
return json.Marshal(map[string]any{
"type": f.Type().String(),
"now": f.Now(),
"all": all,
"testUrl": f.testUrl,
"expected": f.expectedStatus,
"type": f.Type().String(),
"now": f.Now(),
"all": all,
})
}
@@ -102,14 +98,12 @@ func (f *Fallback) findAliveProxy(touch bool) C.Proxy {
proxies := f.GetProxies(touch)
for _, proxy := range proxies {
if len(f.selected) == 0 {
// if proxy.Alive() {
if proxy.AliveForTestUrl(f.testUrl) {
if proxy.Alive() {
return proxy
}
} else {
if proxy.Name() == f.selected {
// if proxy.Alive() {
if proxy.AliveForTestUrl(f.testUrl) {
if proxy.Alive() {
return proxy
} else {
f.selected = ""
@@ -135,12 +129,10 @@ func (f *Fallback) Set(name string) error {
}
f.selected = name
// if !p.Alive() {
if !p.AliveForTestUrl(f.testUrl) {
if !p.Alive() {
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*time.Duration(5000))
defer cancel()
expectedStatus, _ := utils.NewIntRanges[uint16](f.expectedStatus)
_, _ = p.URLTest(ctx, f.testUrl, expectedStatus, C.ExtraHistory)
_, _ = p.URLTest(ctx, f.testUrl)
}
return nil
@@ -164,8 +156,7 @@ func NewFallback(option *GroupCommonOption, providers []provider.ProxyProvider)
option.ExcludeType,
providers,
}),
disableUDP: option.DisableUDP,
testUrl: option.URL,
expectedStatus: option.ExpectedStatus,
disableUDP: option.DisableUDP,
testUrl: option.URL,
}
}

View File

@@ -9,7 +9,6 @@ import (
"github.com/Dreamacro/clash/adapter/outbound"
"github.com/Dreamacro/clash/common/atomic"
"github.com/Dreamacro/clash/common/utils"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/constant/provider"
types "github.com/Dreamacro/clash/constant/provider"
@@ -193,7 +192,7 @@ func (gb *GroupBase) GetProxies(touch bool) []C.Proxy {
return proxies
}
func (gb *GroupBase) URLTest(ctx context.Context, url string, expectedStatus utils.IntRanges[uint16]) (map[string]uint16, error) {
func (gb *GroupBase) URLTest(ctx context.Context, url string) (map[string]uint16, error) {
var wg sync.WaitGroup
var lock sync.Mutex
mp := map[string]uint16{}
@@ -202,7 +201,7 @@ func (gb *GroupBase) URLTest(ctx context.Context, url string, expectedStatus uti
proxy := proxy
wg.Add(1)
go func() {
delay, err := proxy.URLTest(ctx, url, expectedStatus, C.DropHistory)
delay, err := proxy.URLTest(ctx, url)
if err == nil {
lock.Lock()
mp[proxy.Name()] = delay

View File

@@ -12,8 +12,8 @@ import (
"github.com/Dreamacro/clash/adapter/outbound"
"github.com/Dreamacro/clash/common/cache"
"github.com/Dreamacro/clash/common/callback"
"github.com/Dreamacro/clash/common/murmur3"
N "github.com/Dreamacro/clash/common/net"
"github.com/Dreamacro/clash/common/utils"
"github.com/Dreamacro/clash/component/dialer"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/constant/provider"
@@ -25,10 +25,8 @@ type strategyFn = func(proxies []C.Proxy, metadata *C.Metadata, touch bool) C.Pr
type LoadBalance struct {
*GroupBase
disableUDP bool
strategyFn strategyFn
testUrl string
expectedStatus string
disableUDP bool
strategyFn strategyFn
}
var errStrategy = errors.New("unsupported strategy")
@@ -131,7 +129,7 @@ func (lb *LoadBalance) IsL3Protocol(metadata *C.Metadata) bool {
return lb.Unwrap(metadata, false).IsL3Protocol(metadata)
}
func strategyRoundRobin(url string) strategyFn {
func strategyRoundRobin() strategyFn {
idx := 0
idxMutex := sync.Mutex{}
return func(proxies []C.Proxy, metadata *C.Metadata, touch bool) C.Proxy {
@@ -150,8 +148,7 @@ func strategyRoundRobin(url string) strategyFn {
for ; i < length; i++ {
id := (idx + i) % length
proxy := proxies[id]
// if proxy.Alive() {
if proxy.AliveForTestUrl(url) {
if proxy.Alive() {
i++
return proxy
}
@@ -161,24 +158,22 @@ func strategyRoundRobin(url string) strategyFn {
}
}
func strategyConsistentHashing(url string) strategyFn {
func strategyConsistentHashing() strategyFn {
maxRetry := 5
return func(proxies []C.Proxy, metadata *C.Metadata, touch bool) C.Proxy {
key := utils.MapHash(getKey(metadata))
key := uint64(murmur3.Sum32([]byte(getKey(metadata))))
buckets := int32(len(proxies))
for i := 0; i < maxRetry; i, key = i+1, key+1 {
idx := jumpHash(key, buckets)
proxy := proxies[idx]
// if proxy.Alive() {
if proxy.AliveForTestUrl(url) {
if proxy.Alive() {
return proxy
}
}
// when availability is poor, traverse the entire list to get the available nodes
for _, proxy := range proxies {
// if proxy.Alive() {
if proxy.AliveForTestUrl(url) {
if proxy.Alive() {
return proxy
}
}
@@ -187,14 +182,14 @@ func strategyConsistentHashing(url string) strategyFn {
}
}
func strategyStickySessions(url string) strategyFn {
func strategyStickySessions() strategyFn {
ttl := time.Minute * 10
maxRetry := 5
lruCache := cache.New[uint64, int](
cache.WithAge[uint64, int](int64(ttl.Seconds())),
cache.WithSize[uint64, int](1000))
return func(proxies []C.Proxy, metadata *C.Metadata, touch bool) C.Proxy {
key := utils.MapHash(getKeyWithSrcAndDst(metadata))
key := uint64(murmur3.Sum32([]byte(getKeyWithSrcAndDst(metadata))))
length := len(proxies)
idx, has := lruCache.Get(key)
if !has {
@@ -204,8 +199,7 @@ func strategyStickySessions(url string) strategyFn {
nowIdx := idx
for i := 1; i < maxRetry; i++ {
proxy := proxies[nowIdx]
// if proxy.Alive() {
if proxy.AliveForTestUrl(url) {
if proxy.Alive() {
if nowIdx != idx {
lruCache.Delete(key)
lruCache.Set(key, nowIdx)
@@ -236,10 +230,8 @@ func (lb *LoadBalance) MarshalJSON() ([]byte, error) {
all = append(all, proxy.Name())
}
return json.Marshal(map[string]any{
"type": lb.Type().String(),
"all": all,
"testUrl": lb.testUrl,
"expectedStatus": lb.expectedStatus,
"type": lb.Type().String(),
"all": all,
})
}
@@ -247,11 +239,11 @@ func NewLoadBalance(option *GroupCommonOption, providers []provider.ProxyProvide
var strategyFn strategyFn
switch strategy {
case "consistent-hashing":
strategyFn = strategyConsistentHashing(option.URL)
strategyFn = strategyConsistentHashing()
case "round-robin":
strategyFn = strategyRoundRobin(option.URL)
strategyFn = strategyRoundRobin()
case "sticky-sessions":
strategyFn = strategyStickySessions(option.URL)
strategyFn = strategyStickySessions()
default:
return nil, fmt.Errorf("%w: %s", errStrategy, strategy)
}
@@ -268,9 +260,7 @@ func NewLoadBalance(option *GroupCommonOption, providers []provider.ProxyProvide
option.ExcludeType,
providers,
}),
strategyFn: strategyFn,
disableUDP: option.DisableUDP,
testUrl: option.URL,
expectedStatus: option.ExpectedStatus,
strategyFn: strategyFn,
disableUDP: option.DisableUDP,
}, nil
}

View File

@@ -3,37 +3,35 @@ package outboundgroup
import (
"errors"
"fmt"
"strings"
"github.com/Dreamacro/clash/adapter/outbound"
"github.com/Dreamacro/clash/adapter/provider"
"github.com/Dreamacro/clash/common/structure"
"github.com/Dreamacro/clash/common/utils"
C "github.com/Dreamacro/clash/constant"
types "github.com/Dreamacro/clash/constant/provider"
)
var (
errFormat = errors.New("format error")
errType = errors.New("unsupported type")
errType = errors.New("unsupport type")
errMissProxy = errors.New("`use` or `proxies` missing")
errMissHealthCheck = errors.New("`url` or `interval` missing")
errDuplicateProvider = errors.New("duplicate provider name")
)
type GroupCommonOption struct {
outbound.BasicOption
Name string `group:"name"`
Type string `group:"type"`
Proxies []string `group:"proxies,omitempty"`
Use []string `group:"use,omitempty"`
URL string `group:"url,omitempty"`
Interval int `group:"interval,omitempty"`
Lazy bool `group:"lazy,omitempty"`
DisableUDP bool `group:"disable-udp,omitempty"`
Filter string `group:"filter,omitempty"`
ExcludeFilter string `group:"exclude-filter,omitempty"`
ExcludeType string `group:"exclude-type,omitempty"`
ExpectedStatus string `group:"expected-status,omitempty"`
Name string `group:"name"`
Type string `group:"type"`
Proxies []string `group:"proxies,omitempty"`
Use []string `group:"use,omitempty"`
URL string `group:"url,omitempty"`
Interval int `group:"interval,omitempty"`
Lazy bool `group:"lazy,omitempty"`
DisableUDP bool `group:"disable-udp,omitempty"`
Filter string `group:"filter,omitempty"`
ExcludeFilter string `group:"exclude-filter,omitempty"`
ExcludeType string `group:"exclude-type,omitempty"`
}
func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, providersMap map[string]types.ProxyProvider) (C.ProxyAdapter, error) {
@@ -55,36 +53,30 @@ func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, provide
providers := []types.ProxyProvider{}
if len(groupOption.Proxies) == 0 && len(groupOption.Use) == 0 {
return nil, fmt.Errorf("%s: %w", groupName, errMissProxy)
return nil, errMissProxy
}
expectedStatus, err := utils.NewIntRanges[uint16](groupOption.ExpectedStatus)
if err != nil {
return nil, fmt.Errorf("%s: %w", groupName, err)
}
status := strings.TrimSpace(groupOption.ExpectedStatus)
if status == "" {
status = "*"
}
groupOption.ExpectedStatus = status
testUrl := groupOption.URL
if len(groupOption.Proxies) != 0 {
ps, err := getProxies(proxyMap, groupOption.Proxies)
if err != nil {
return nil, fmt.Errorf("%s: %w", groupName, err)
return nil, err
}
if _, ok := providersMap[groupName]; ok {
return nil, fmt.Errorf("%s: %w", groupName, errDuplicateProvider)
return nil, errDuplicateProvider
}
var url string
var interval uint
// select don't need health check
if groupOption.Type != "select" && groupOption.Type != "relay" {
if groupOption.Type == "select" || groupOption.Type == "relay" {
hc := provider.NewHealthCheck(ps, "", 0, true)
pd, err := provider.NewCompatibleProvider(groupName, ps, hc)
if err != nil {
return nil, err
}
providers = append(providers, pd)
providersMap[groupName] = pd
} else {
if groupOption.URL == "" {
groupOption.URL = "https://cp.cloudflare.com/generate_204"
}
@@ -93,29 +85,22 @@ func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, provide
groupOption.Interval = 300
}
url = groupOption.URL
interval = uint(groupOption.Interval)
}
hc := provider.NewHealthCheck(ps, groupOption.URL, uint(groupOption.Interval), groupOption.Lazy)
pd, err := provider.NewCompatibleProvider(groupName, ps, hc)
if err != nil {
return nil, err
}
hc := provider.NewHealthCheck(ps, url, interval, true, expectedStatus)
pd, err := provider.NewCompatibleProvider(groupName, ps, hc)
if err != nil {
return nil, fmt.Errorf("%s: %w", groupName, err)
providers = append(providers, pd)
providersMap[groupName] = pd
}
providers = append(providers, pd)
providersMap[groupName] = pd
}
if len(groupOption.Use) != 0 {
list, err := getProviders(providersMap, groupOption.Use)
if err != nil {
return nil, fmt.Errorf("%s: %w", groupName, err)
return nil, err
}
// different proxy groups use different test URL
addTestUrlToProviders(list, testUrl, expectedStatus, groupOption.Filter, uint(groupOption.Interval))
providers = append(providers, list...)
} else {
groupOption.Filter = ""
@@ -169,13 +154,3 @@ func getProviders(mapping map[string]types.ProxyProvider, list []string) ([]type
}
return ps, nil
}
func addTestUrlToProviders(providers []types.ProxyProvider, url string, expectedStatus utils.IntRanges[uint16], filter string, interval uint) {
if len(providers) == 0 || len(url) == 0 {
return
}
for _, pd := range providers {
pd.RegisterHealthCheckTask(url, expectedStatus, filter, interval)
}
}

View File

@@ -25,13 +25,12 @@ func urlTestWithTolerance(tolerance uint16) urlTestOption {
type URLTest struct {
*GroupBase
selected string
testUrl string
expectedStatus string
tolerance uint16
disableUDP bool
fastNode C.Proxy
fastSingle *singledo.Single[C.Proxy]
selected string
testUrl string
tolerance uint16
disableUDP bool
fastNode C.Proxy
fastSingle *singledo.Single[C.Proxy]
}
func (u *URLTest) Now() string {
@@ -113,8 +112,7 @@ func (u *URLTest) fast(touch bool) C.Proxy {
elm, _, shared := u.fastSingle.Do(func() (C.Proxy, error) {
fast := proxies[0]
// min := fast.LastDelay()
min := fast.LastDelayForTestUrl(u.testUrl)
min := fast.LastDelay()
fastNotExist := true
for _, proxy := range proxies[1:] {
@@ -122,13 +120,11 @@ func (u *URLTest) fast(touch bool) C.Proxy {
fastNotExist = false
}
// if !proxy.Alive() {
if !proxy.AliveForTestUrl(u.testUrl) {
if !proxy.Alive() {
continue
}
// delay := proxy.LastDelay()
delay := proxy.LastDelayForTestUrl(u.testUrl)
delay := proxy.LastDelay()
if delay < min {
fast = proxy
min = delay
@@ -136,8 +132,7 @@ func (u *URLTest) fast(touch bool) C.Proxy {
}
// tolerance
// if u.fastNode == nil || fastNotExist || !u.fastNode.Alive() || u.fastNode.LastDelay() > fast.LastDelay()+u.tolerance {
if u.fastNode == nil || fastNotExist || !u.fastNode.AliveForTestUrl(u.testUrl) || u.fastNode.LastDelayForTestUrl(u.testUrl) > fast.LastDelayForTestUrl(u.testUrl)+u.tolerance {
if u.fastNode == nil || fastNotExist || !u.fastNode.Alive() || u.fastNode.LastDelay() > fast.LastDelay()+u.tolerance {
u.fastNode = fast
}
return u.fastNode, nil
@@ -169,11 +164,9 @@ func (u *URLTest) MarshalJSON() ([]byte, error) {
all = append(all, proxy.Name())
}
return json.Marshal(map[string]any{
"type": u.Type().String(),
"now": u.Now(),
"all": all,
"testUrl": u.testUrl,
"expected": u.expectedStatus,
"type": u.Type().String(),
"now": u.Now(),
"all": all,
})
}
@@ -205,10 +198,9 @@ func NewURLTest(option *GroupCommonOption, providers []provider.ProxyProvider, o
option.ExcludeType,
providers,
}),
fastSingle: singledo.NewSingle[C.Proxy](time.Second * 10),
disableUDP: option.DisableUDP,
testUrl: option.URL,
expectedStatus: option.ExpectedStatus,
fastSingle: singledo.NewSingle[C.Proxy](time.Second * 10),
disableUDP: option.DisableUDP,
testUrl: option.URL,
}
for _, option := range options {

View File

@@ -106,13 +106,6 @@ func ParseProxy(mapping map[string]any) (C.Proxy, error) {
break
}
proxy, err = outbound.NewTuic(*tuicOption)
case "direct":
directOption := &outbound.DirectOption{}
err = decoder.Decode(mapping, directOption)
if err != nil {
break
}
proxy = outbound.NewDirectWithOption(*directOption)
default:
return nil, fmt.Errorf("unsupport proxy type: %s", proxyType)
}

View File

@@ -2,8 +2,6 @@ package provider
import (
"context"
"strings"
"sync"
"time"
"github.com/Dreamacro/clash/common/atomic"
@@ -12,8 +10,6 @@ import (
"github.com/Dreamacro/clash/common/utils"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/log"
"github.com/dlclark/regexp2"
)
const (
@@ -25,33 +21,18 @@ type HealthCheckOption struct {
Interval uint
}
type extraOption struct {
expectedStatus utils.IntRanges[uint16]
filters map[string]struct{}
}
type HealthCheck struct {
url string
extra map[string]*extraOption
mu sync.Mutex
started *atomic.Bool
proxies []C.Proxy
interval uint
lazy bool
expectedStatus utils.IntRanges[uint16]
lastTouch *atomic.Int64
done chan struct{}
singleDo *singledo.Single[struct{}]
url string
proxies []C.Proxy
interval uint
lazy bool
lastTouch *atomic.Int64
done chan struct{}
singleDo *singledo.Single[struct{}]
}
func (hc *HealthCheck) process() {
if hc.started.Load() {
log.Warnln("Skip start health check timer due to it's started")
return
}
ticker := time.NewTicker(time.Duration(hc.interval) * time.Second)
hc.start()
for {
select {
case <-ticker.C:
@@ -63,7 +44,6 @@ func (hc *HealthCheck) process() {
}
case <-hc.done:
ticker.Stop()
hc.stop()
return
}
}
@@ -73,63 +53,6 @@ func (hc *HealthCheck) setProxy(proxies []C.Proxy) {
hc.proxies = proxies
}
func (hc *HealthCheck) registerHealthCheckTask(url string, expectedStatus utils.IntRanges[uint16], filter string, interval uint) {
url = strings.TrimSpace(url)
if len(url) == 0 || url == hc.url {
log.Debugln("ignore invalid health check url: %s", url)
return
}
hc.mu.Lock()
defer hc.mu.Unlock()
// if the provider has not set up health checks, then modify it to be the same as the group's interval
if hc.interval == 0 {
hc.interval = interval
}
if hc.extra == nil {
hc.extra = make(map[string]*extraOption)
}
// prioritize the use of previously registered configurations, especially those from provider
if _, ok := hc.extra[url]; ok {
// provider default health check does not set filter
if url != hc.url && len(filter) != 0 {
splitAndAddFiltersToExtra(filter, hc.extra[url])
}
log.Debugln("health check url: %s exists", url)
return
}
// due to the time-consuming nature of health checks, a maximum of defaultMaxTestURLNum URLs can be set for testing
if len(hc.extra) > C.DefaultMaxHealthCheckUrlNum {
log.Debugln("skip add url: %s to health check because it has reached the maximum limit: %d", url, C.DefaultMaxHealthCheckUrlNum)
return
}
option := &extraOption{filters: map[string]struct{}{}, expectedStatus: expectedStatus}
splitAndAddFiltersToExtra(filter, option)
hc.extra[url] = option
if hc.auto() && !hc.started.Load() {
go hc.process()
}
}
func splitAndAddFiltersToExtra(filter string, option *extraOption) {
filter = strings.TrimSpace(filter)
if len(filter) != 0 {
for _, regex := range strings.Split(filter, "`") {
regex = strings.TrimSpace(regex)
if len(regex) != 0 {
option.filters[regex] = struct{}{}
}
}
}
}
func (hc *HealthCheck) auto() bool {
return hc.interval != 0
}
@@ -138,102 +61,41 @@ func (hc *HealthCheck) touch() {
hc.lastTouch.Store(time.Now().Unix())
}
func (hc *HealthCheck) start() {
hc.started.Store(true)
}
func (hc *HealthCheck) stop() {
hc.started.Store(false)
}
func (hc *HealthCheck) check() {
_, _, _ = hc.singleDo.Do(func() (struct{}, error) {
id := utils.NewUUIDV4().String()
log.Debugln("Start New Health Checking {%s}", id)
b, _ := batch.New[bool](context.Background(), batch.WithConcurrencyNum[bool](10))
// execute default health check
option := &extraOption{filters: nil, expectedStatus: hc.expectedStatus}
hc.execute(b, hc.url, id, option)
// execute extra health check
if len(hc.extra) != 0 {
for url, option := range hc.extra {
hc.execute(b, url, id, option)
}
for _, proxy := range hc.proxies {
p := proxy
b.Go(p.Name(), func() (bool, error) {
ctx, cancel := context.WithTimeout(context.Background(), defaultURLTestTimeout)
defer cancel()
log.Debugln("Health Checking %s {%s}", p.Name(), id)
_, _ = p.URLTest(ctx, hc.url)
log.Debugln("Health Checked %s : %t %d ms {%s}", p.Name(), p.Alive(), p.LastDelay(), id)
return false, nil
})
}
b.Wait()
log.Debugln("Finish A Health Checking {%s}", id)
return struct{}{}, nil
})
}
func (hc *HealthCheck) execute(b *batch.Batch[bool], url, uid string, option *extraOption) {
url = strings.TrimSpace(url)
if len(url) == 0 {
log.Debugln("Health Check has been skipped due to testUrl is empty, {%s}", uid)
return
}
var filterReg *regexp2.Regexp
var store = C.OriginalHistory
var expectedStatus utils.IntRanges[uint16]
if option != nil {
if url != hc.url {
store = C.ExtraHistory
}
expectedStatus = option.expectedStatus
if len(option.filters) != 0 {
filters := make([]string, 0, len(option.filters))
for filter := range option.filters {
filters = append(filters, filter)
}
filterReg = regexp2.MustCompile(strings.Join(filters, "|"), 0)
}
}
for _, proxy := range hc.proxies {
// skip proxies that do not require health check
if filterReg != nil {
if match, _ := filterReg.FindStringMatch(proxy.Name()); match == nil {
continue
}
}
p := proxy
b.Go(p.Name(), func() (bool, error) {
ctx, cancel := context.WithTimeout(context.Background(), defaultURLTestTimeout)
defer cancel()
log.Debugln("Health Checking, proxy: %s, url: %s, id: {%s}", p.Name(), url, uid)
_, _ = p.URLTest(ctx, url, expectedStatus, store)
log.Debugln("Health Checked, proxy: %s, url: %s, alive: %t, delay: %d ms uid: {%s}", p.Name(), url, p.AliveForTestUrl(url), p.LastDelayForTestUrl(url), uid)
return false, nil
})
}
}
func (hc *HealthCheck) close() {
hc.done <- struct{}{}
}
func NewHealthCheck(proxies []C.Proxy, url string, interval uint, lazy bool, expectedStatus utils.IntRanges[uint16]) *HealthCheck {
if len(url) == 0 {
interval = 0
expectedStatus = nil
}
func NewHealthCheck(proxies []C.Proxy, url string, interval uint, lazy bool) *HealthCheck {
return &HealthCheck{
proxies: proxies,
url: url,
extra: map[string]*extraOption{},
started: atomic.NewBool(false),
interval: interval,
lazy: lazy,
expectedStatus: expectedStatus,
lastTouch: atomic.NewInt64(0),
done: make(chan struct{}, 1),
singleDo: singledo.NewSingle[struct{}](time.Second),
proxies: proxies,
url: url,
interval: interval,
lazy: lazy,
lastTouch: atomic.NewInt64(0),
done: make(chan struct{}, 1),
singleDo: singledo.NewSingle[struct{}](time.Second),
}
}

View File

@@ -6,28 +6,23 @@ import (
"time"
"github.com/Dreamacro/clash/common/structure"
"github.com/Dreamacro/clash/common/utils"
"github.com/Dreamacro/clash/component/resource"
C "github.com/Dreamacro/clash/constant"
types "github.com/Dreamacro/clash/constant/provider"
)
var (
errVehicleType = errors.New("unsupport vehicle type")
errSubPath = errors.New("path is not subpath of home directory")
)
var errVehicleType = errors.New("unsupport vehicle type")
type healthCheckSchema struct {
Enable bool `provider:"enable"`
URL string `provider:"url"`
Interval int `provider:"interval"`
Lazy bool `provider:"lazy,omitempty"`
ExpectedStatus string `provider:"expected-status,omitempty"`
Enable bool `provider:"enable"`
URL string `provider:"url"`
Interval int `provider:"interval"`
Lazy bool `provider:"lazy,omitempty"`
}
type proxyProviderSchema struct {
Type string `provider:"type"`
Path string `provider:"path,omitempty"`
Path string `provider:"path"`
URL string `provider:"url,omitempty"`
Interval int `provider:"interval,omitempty"`
Filter string `provider:"filter,omitempty"`
@@ -49,33 +44,20 @@ func ParseProxyProvider(name string, mapping map[string]any) (types.ProxyProvide
return nil, err
}
expectedStatus, err := utils.NewIntRanges[uint16](schema.HealthCheck.ExpectedStatus)
if err != nil {
return nil, err
}
var hcInterval uint
if schema.HealthCheck.Enable {
hcInterval = uint(schema.HealthCheck.Interval)
}
hc := NewHealthCheck([]C.Proxy{}, schema.HealthCheck.URL, hcInterval, schema.HealthCheck.Lazy, expectedStatus)
hc := NewHealthCheck([]C.Proxy{}, schema.HealthCheck.URL, hcInterval, schema.HealthCheck.Lazy)
path := C.Path.Resolve(schema.Path)
var vehicle types.Vehicle
switch schema.Type {
case "file":
path := C.Path.Resolve(schema.Path)
vehicle = resource.NewFileVehicle(path)
case "http":
if schema.Path != "" {
path := C.Path.Resolve(schema.Path)
if !C.Path.IsSafePath(path) {
return nil, fmt.Errorf("%w: %s", errSubPath, path)
}
vehicle = resource.NewHTTPVehicle(schema.URL, path)
} else {
path := C.Path.GetPathByHash("proxies", schema.URL)
vehicle = resource.NewHTTPVehicle(schema.URL, path)
}
vehicle = resource.NewHTTPVehicle(schema.URL, path)
default:
return nil, fmt.Errorf("%w: %s", errVehicleType, schema.Type)
}

View File

@@ -12,7 +12,6 @@ import (
"github.com/Dreamacro/clash/adapter"
"github.com/Dreamacro/clash/common/convert"
"github.com/Dreamacro/clash/common/utils"
clashHttp "github.com/Dreamacro/clash/component/http"
"github.com/Dreamacro/clash/component/resource"
C "github.com/Dreamacro/clash/constant"
@@ -51,7 +50,6 @@ func (pp *proxySetProvider) MarshalJSON() ([]byte, error) {
"type": pp.Type().String(),
"vehicleType": pp.VehicleType().String(),
"proxies": pp.Proxies(),
"testUrl": pp.healthCheck.url,
"updatedAt": pp.UpdatedAt,
"subscriptionInfo": pp.subscriptionInfo,
})
@@ -100,10 +98,6 @@ func (pp *proxySetProvider) Touch() {
pp.healthCheck.touch()
}
func (pp *proxySetProvider) RegisterHealthCheckTask(url string, expectedStatus utils.IntRanges[uint16], filter string, interval uint) {
pp.healthCheck.registerHealthCheckTask(url, expectedStatus, filter, interval)
}
func (pp *proxySetProvider) setProxies(proxies []C.Proxy) {
pp.proxies = proxies
pp.healthCheck.setProxy(proxies)
@@ -147,15 +141,15 @@ func (pp *proxySetProvider) getSubscriptionInfo() {
}
func (pp *proxySetProvider) closeAllConnections() {
statistic.DefaultManager.Range(func(c statistic.Tracker) bool {
snapshot := statistic.DefaultManager.Snapshot()
for _, c := range snapshot.Connections {
for _, chain := range c.Chains() {
if chain == pp.Name() {
_ = c.Close()
break
}
}
return true
})
}
}
func stopProxyProvider(pd *ProxySetProvider) {
@@ -216,7 +210,6 @@ func (cp *compatibleProvider) MarshalJSON() ([]byte, error) {
"type": cp.Type().String(),
"vehicleType": cp.VehicleType().String(),
"proxies": cp.Proxies(),
"testUrl": cp.healthCheck.url,
})
}
@@ -256,10 +249,6 @@ func (cp *compatibleProvider) Touch() {
cp.healthCheck.touch()
}
func (cp *compatibleProvider) RegisterHealthCheckTask(url string, expectedStatus utils.IntRanges[uint16], filter string, interval uint) {
cp.healthCheck.registerHealthCheckTask(url, expectedStatus, filter, interval)
}
func stopCompatibleProvider(pd *CompatibleProvider) {
pd.healthCheck.close()
}
@@ -299,7 +288,7 @@ func proxiesParseAndFilter(filter string, excludeFilter string, excludeTypeArray
if err := yaml.Unmarshal(buf, schema); err != nil {
proxies, err1 := convert.ConvertsV2Ray(buf)
if err1 != nil {
return nil, fmt.Errorf("%w, %w", err, err1)
return nil, fmt.Errorf("%s, %w", err.Error(), err1)
}
schema.Proxies = proxies
}

View File

@@ -11,9 +11,18 @@ type Buffer = buf.Buffer
var New = buf.New
var NewSize = buf.NewSize
var StackNew = buf.StackNew
var StackNewSize = buf.StackNewSize
var With = buf.With
var As = buf.As
var KeepAlive = common.KeepAlive
//go:norace
func Dup[T any](obj T) T {
return common.Dup(obj)
}
var (
Must = common.Must
Error = common.Error

View File

@@ -7,8 +7,6 @@ import (
"time"
"github.com/Dreamacro/clash/common/generics/list"
"github.com/samber/lo"
)
// Option is part of Functional Options Pattern
@@ -84,27 +82,9 @@ func New[K comparable, V any](options ...Option[K, V]) *LruCache[K, V] {
// Get returns the any representation of a cached response and a bool
// set to true if the key was found.
func (c *LruCache[K, V]) Get(key K) (V, bool) {
c.mu.Lock()
defer c.mu.Unlock()
el := c.get(key)
if el == nil {
return lo.Empty[V](), false
}
value := el.value
return value, true
}
func (c *LruCache[K, V]) GetOrStore(key K, constructor func() V) (V, bool) {
c.mu.Lock()
defer c.mu.Unlock()
el := c.get(key)
if el == nil {
value := constructor()
c.set(key, value)
return value, false
return getZero[V](), false
}
value := el.value
@@ -116,12 +96,9 @@ func (c *LruCache[K, V]) GetOrStore(key K, constructor func() V) (V, bool) {
// and a bool set to true if the key was found.
// This method will NOT check the maxAge of element and will NOT update the expires.
func (c *LruCache[K, V]) GetWithExpire(key K) (V, time.Time, bool) {
c.mu.Lock()
defer c.mu.Unlock()
el := c.get(key)
if el == nil {
return lo.Empty[V](), time.Time{}, false
return getZero[V](), time.Time{}, false
}
return el.value, time.Unix(el.expires, 0), true
@@ -138,18 +115,11 @@ func (c *LruCache[K, V]) Exist(key K) bool {
// Set stores the any representation of a response for a given key.
func (c *LruCache[K, V]) Set(key K, value V) {
c.mu.Lock()
defer c.mu.Unlock()
c.set(key, value)
}
func (c *LruCache[K, V]) set(key K, value V) {
expires := int64(0)
if c.maxAge > 0 {
expires = time.Now().Unix() + c.maxAge
}
c.setWithExpire(key, value, time.Unix(expires, 0))
c.SetWithExpire(key, value, time.Unix(expires, 0))
}
// SetWithExpire stores the any representation of a response for a given key and given expires.
@@ -158,10 +128,6 @@ func (c *LruCache[K, V]) SetWithExpire(key K, value V, expires time.Time) {
c.mu.Lock()
defer c.mu.Unlock()
c.setWithExpire(key, value, expires)
}
func (c *LruCache[K, V]) setWithExpire(key K, value V, expires time.Time) {
if le, ok := c.cache[key]; ok {
c.lru.MoveToBack(le)
e := le.Value
@@ -199,6 +165,9 @@ func (c *LruCache[K, V]) CloneTo(n *LruCache[K, V]) {
}
func (c *LruCache[K, V]) get(key K) *entry[K, V] {
c.mu.Lock()
defer c.mu.Unlock()
le, ok := c.cache[key]
if !ok {
return nil
@@ -222,11 +191,12 @@ func (c *LruCache[K, V]) get(key K) *entry[K, V] {
// Delete removes the value associated with a key.
func (c *LruCache[K, V]) Delete(key K) {
c.mu.Lock()
defer c.mu.Unlock()
if le, ok := c.cache[key]; ok {
c.deleteElement(le)
}
c.mu.Unlock()
}
func (c *LruCache[K, V]) maybeDeleteOldest() {
@@ -249,10 +219,10 @@ func (c *LruCache[K, V]) deleteElement(le *list.Element[*entry[K, V]]) {
func (c *LruCache[K, V]) Clear() error {
c.mu.Lock()
defer c.mu.Unlock()
c.cache = make(map[K]*list.Element[*entry[K, V]])
c.mu.Unlock()
return nil
}
@@ -261,3 +231,8 @@ type entry[K comparable, V any] struct {
value V
expires int64
}
func getZero[T any]() T {
var result T
return result
}

View File

@@ -50,9 +50,7 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
hysteria["port"] = urlHysteria.Port()
hysteria["sni"] = query.Get("peer")
hysteria["obfs"] = query.Get("obfs")
if alpn := query.Get("alpn"); alpn != "" {
hysteria["alpn"] = strings.Split(alpn, ",")
}
hysteria["alpn"] = []string{query.Get("alpn")}
hysteria["auth_str"] = query.Get("auth")
hysteria["protocol"] = query.Get("protocol")
up := query.Get("up")
@@ -69,47 +67,6 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
proxies = append(proxies, hysteria)
case "tuic":
// A temporary unofficial TUIC share link standard
// Modified from https://github.com/daeuniverse/dae/discussions/182
// Changes:
// 1. Support TUICv4, just replace uuid:password with token
// 2. Remove `allow_insecure` field
urlTUIC, err := url.Parse(line)
if err != nil {
continue
}
query := urlTUIC.Query()
tuic := make(map[string]any, 20)
tuic["name"] = uniqueName(names, urlTUIC.Fragment)
tuic["type"] = scheme
tuic["server"] = urlTUIC.Hostname()
tuic["port"] = urlTUIC.Port()
tuic["udp"] = true
password, v5 := urlTUIC.User.Password()
if v5 {
tuic["uuid"] = urlTUIC.User.Username()
tuic["password"] = password
} else {
tuic["token"] = urlTUIC.User.Username()
}
if cc := query.Get("congestion_control"); cc != "" {
tuic["congestion-controller"] = cc
}
if alpn := query.Get("alpn"); alpn != "" {
tuic["alpn"] = strings.Split(alpn, ",")
}
if sni := query.Get("sni"); sni != "" {
tuic["sni"] = sni
}
if query.Get("disable_sni") == "1" {
tuic["disable-sni"] = true
}
if udpRelayMode := query.Get("udp_relay_mode"); udpRelayMode != "" {
tuic["udp-relay-mode"] = udpRelayMode
}
case "trojan":
urlTrojan, err := url.Parse(line)
if err != nil {
@@ -129,12 +86,10 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
trojan["udp"] = true
trojan["skip-cert-verify"], _ = strconv.ParseBool(query.Get("allowInsecure"))
if sni := query.Get("sni"); sni != "" {
sni := query.Get("sni")
if sni != "" {
trojan["sni"] = sni
}
if alpn := query.Get("alpn"); alpn != "" {
trojan["alpn"] = strings.Split(alpn, ",")
}
network := strings.ToLower(query.Get("type"))
if network != "" {
@@ -262,9 +217,6 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
if strings.HasSuffix(tls, "tls") {
vmess["tls"] = true
}
if alpn, ok := values["alpn"].(string); ok {
vmess["alpn"] = strings.Split(alpn, ",")
}
}
switch network {
@@ -380,7 +332,6 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
}
}
proxies = append(proxies, ss)
case "ssr":
dcBuf, err := encRaw.DecodeString(body)
if err != nil {

View File

@@ -24,6 +24,8 @@ func handleVShareLink(names map[string]int, url *url.URL, scheme string, proxy m
proxy["port"] = url.Port()
proxy["uuid"] = url.User.Username()
proxy["udp"] = true
proxy["skip-cert-verify"] = false
proxy["tls"] = false
tls := strings.ToLower(query.Get("security"))
if strings.HasSuffix(tls, "tls") || tls == "reality" {
proxy["tls"] = true
@@ -32,9 +34,6 @@ func handleVShareLink(names map[string]int, url *url.URL, scheme string, proxy m
} else {
proxy["client-fingerprint"] = fingerprint
}
if alpn := query.Get("alpn"); alpn != "" {
proxy["alpn"] = strings.Split(alpn, ",")
}
}
if sni := query.Get("sni"); sni != "" {
proxy["servername"] = sni

View File

@@ -47,7 +47,6 @@ func (p *Picker[T]) Wait() T {
p.wg.Wait()
if p.cancel != nil {
p.cancel()
p.cancel = nil
}
return p.result
}
@@ -70,7 +69,6 @@ func (p *Picker[T]) Go(f func() (T, error)) {
p.result = ret
if p.cancel != nil {
p.cancel()
p.cancel = nil
}
})
} else {
@@ -80,13 +78,3 @@ func (p *Picker[T]) Go(f func() (T, error)) {
}
}()
}
// Close cancels the picker context and releases resources associated with it.
// If Wait has been called, then there is no need to call Close.
func (p *Picker[T]) Close() error {
if p.cancel != nil {
p.cancel()
p.cancel = nil
}
return nil
}

View File

@@ -5,7 +5,6 @@ import (
"testing"
"time"
"github.com/samber/lo"
"github.com/stretchr/testify/assert"
)
@@ -16,7 +15,7 @@ func sleepAndSend[T any](ctx context.Context, delay int, input T) func() (T, err
case <-timer.C:
return input, nil
case <-ctx.Done():
return lo.Empty[T](), ctx.Err()
return getZero[T](), ctx.Err()
}
}
}
@@ -36,6 +35,11 @@ func TestPicker_Timeout(t *testing.T) {
picker.Go(sleepAndSend(ctx, 20, 1))
number := picker.Wait()
assert.Equal(t, number, lo.Empty[int]())
assert.Equal(t, number, getZero[int]())
assert.NotNil(t, picker.Error())
}
func getZero[T any]() T {
var result T
return result
}

View File

@@ -32,32 +32,23 @@ func NewAllocator() *Allocator {
// Get a []byte from pool with most appropriate cap
func (alloc *Allocator) Get(size int) []byte {
switch {
case size < 0:
panic("alloc.Get: len out of range")
case size == 0:
if size <= 0 || size > 65536 {
return nil
case size > 65536:
return make([]byte, size)
default:
bits := msb(size)
if size == 1<<bits {
return alloc.buffers[bits].Get().([]byte)[:size]
}
return alloc.buffers[bits+1].Get().([]byte)[:size]
}
bits := msb(size)
if size == 1<<bits {
return alloc.buffers[bits].Get().([]byte)[:size]
}
return alloc.buffers[bits+1].Get().([]byte)[:size]
}
// Put returns a []byte to pool for future use,
// which the cap must be exactly 2^n
func (alloc *Allocator) Put(buf []byte) error {
if cap(buf) == 0 || cap(buf) > 65536 {
return nil
}
bits := msb(cap(buf))
if cap(buf) != 1<<bits {
if cap(buf) == 0 || cap(buf) > 65536 || cap(buf) != 1<<bits {
return errors.New("allocator Put() incorrect buffer size")
}

View File

@@ -19,17 +19,17 @@ func TestAllocGet(t *testing.T) {
assert.Equal(t, 1024, cap(alloc.Get(1023)))
assert.Equal(t, 1024, len(alloc.Get(1024)))
assert.Equal(t, 65536, len(alloc.Get(65536)))
assert.Equal(t, 65537, len(alloc.Get(65537)))
assert.Nil(t, alloc.Get(65537))
}
func TestAllocPut(t *testing.T) {
alloc := NewAllocator()
assert.Nil(t, alloc.Put(nil), "put nil misbehavior")
assert.NotNil(t, alloc.Put(nil), "put nil misbehavior")
assert.NotNil(t, alloc.Put(make([]byte, 3)), "put elem:3 []bytes misbehavior")
assert.Nil(t, alloc.Put(make([]byte, 4)), "put elem:4 []bytes misbehavior")
assert.Nil(t, alloc.Put(make([]byte, 1023, 1024)), "put elem:1024 []bytes misbehavior")
assert.Nil(t, alloc.Put(make([]byte, 65536)), "put elem:65536 []bytes misbehavior")
assert.Nil(t, alloc.Put(make([]byte, 65537)), "put elem:65537 []bytes misbehavior")
assert.NotNil(t, alloc.Put(make([]byte, 65537)), "put elem:65537 []bytes misbehavior")
}
func TestAllocPutThenGet(t *testing.T) {

View File

@@ -2,8 +2,6 @@ package queue
import (
"sync"
"github.com/samber/lo"
)
// Queue is a simple concurrent safe queue
@@ -26,7 +24,7 @@ func (q *Queue[T]) Put(items ...T) {
// Pop returns the head of items.
func (q *Queue[T]) Pop() T {
if len(q.items) == 0 {
return lo.Empty[T]()
return GetZero[T]()
}
q.lock.Lock()
@@ -39,7 +37,7 @@ func (q *Queue[T]) Pop() T {
// Last returns the last of item.
func (q *Queue[T]) Last() T {
if len(q.items) == 0 {
return lo.Empty[T]()
return GetZero[T]()
}
q.lock.RLock()
@@ -71,3 +69,8 @@ func New[T any](hint int64) *Queue[T] {
items: make([]T, 0, hint),
}
}
func GetZero[T any]() T {
var result T
return result
}

View File

@@ -96,11 +96,6 @@ func (d *Decoder) decode(name string, data any, val reflect.Value) error {
return d.decodeFloat(name, data, val)
}
switch kind {
case reflect.Pointer:
if val.IsNil() {
val.Set(reflect.New(val.Type().Elem()))
}
return d.decode(name, data, val.Elem())
case reflect.String:
return d.decodeString(name, data, val)
case reflect.Bool:
@@ -287,9 +282,6 @@ func (d *Decoder) decodeSlice(name string, data any, val reflect.Value) error {
}
valSlice := val
// make a new slice with cap(val)==cap(dataVal)
// the caller can determine whether the original configuration contains this item by judging whether the value is nil.
valSlice = reflect.MakeSlice(valType, 0, dataVal.Len())
for i := 0; i < dataVal.Len(); i++ {
currentData := dataVal.Index(i).Interface()
for valSlice.Len() <= i {

View File

@@ -1,17 +0,0 @@
package utils
import (
"hash/maphash"
"unsafe"
)
var globalSeed = maphash.MakeSeed()
func GlobalID(material string) (id [8]byte) {
*(*uint64)(unsafe.Pointer(&id[0])) = maphash.String(globalSeed, material)
return
}
func MapHash(material string) uint64 {
return maphash.String(globalSeed, material)
}

View File

@@ -9,36 +9,36 @@ type Range[T constraints.Ordered] struct {
end T
}
func NewRange[T constraints.Ordered](start, end T) Range[T] {
func NewRange[T constraints.Ordered](start, end T) *Range[T] {
if start > end {
return Range[T]{
return &Range[T]{
start: end,
end: start,
}
}
return Range[T]{
return &Range[T]{
start: start,
end: end,
}
}
func (r Range[T]) Contains(t T) bool {
func (r *Range[T]) Contains(t T) bool {
return t >= r.start && t <= r.end
}
func (r Range[T]) LeftContains(t T) bool {
func (r *Range[T]) LeftContains(t T) bool {
return t >= r.start && t < r.end
}
func (r Range[T]) RightContains(t T) bool {
func (r *Range[T]) RightContains(t T) bool {
return t > r.start && t <= r.end
}
func (r Range[T]) Start() T {
func (r *Range[T]) Start() T {
return r.start
}
func (r Range[T]) End() T {
func (r *Range[T]) End() T {
return r.end
}

View File

@@ -1,77 +0,0 @@
package utils
import (
"errors"
"fmt"
"strconv"
"strings"
"golang.org/x/exp/constraints"
)
type IntRanges[T constraints.Integer] []Range[T]
var errIntRanges = errors.New("intRanges error")
func NewIntRanges[T constraints.Integer](expected string) (IntRanges[T], error) {
// example: 200 or 200/302 or 200-400 or 200/204/401-429/501-503
expected = strings.TrimSpace(expected)
if len(expected) == 0 || expected == "*" {
return nil, nil
}
list := strings.Split(expected, "/")
if len(list) > 28 {
return nil, fmt.Errorf("%w, too many ranges to use, maximum support 28 ranges", errIntRanges)
}
return NewIntRangesFromList[T](list)
}
func NewIntRangesFromList[T constraints.Integer](list []string) (IntRanges[T], error) {
var ranges IntRanges[T]
for _, s := range list {
if s == "" {
continue
}
status := strings.Split(s, "-")
statusLen := len(status)
if statusLen > 2 {
return nil, errIntRanges
}
start, err := strconv.ParseInt(strings.Trim(status[0], "[ ]"), 10, 64)
if err != nil {
return nil, errIntRanges
}
switch statusLen {
case 1:
ranges = append(ranges, NewRange(T(start), T(start)))
case 2:
end, err := strconv.ParseUint(strings.Trim(status[1], "[ ]"), 10, 64)
if err != nil {
return nil, errIntRanges
}
ranges = append(ranges, NewRange(T(start), T(end)))
}
}
return ranges, nil
}
func (ranges IntRanges[T]) Check(status T) bool {
if len(ranges) == 0 {
return true
}
for _, segment := range ranges {
if segment.Contains(status) {
return true
}
}
return false
}

View File

@@ -1,21 +0,0 @@
package utils
import "unsafe"
// ImmutableBytesFromString is equivalent to []byte(s), except that it uses the
// same memory backing s instead of making a heap-allocated copy. This is only
// valid if the returned slice is never mutated.
func ImmutableBytesFromString(s string) []byte {
b := unsafe.StringData(s)
return unsafe.Slice(b, len(s))
}
// StringFromImmutableBytes is equivalent to string(bs), except that it uses
// the same memory backing bs instead of making a heap-allocated copy. This is
// only valid if bs is never mutated after StringFromImmutableBytes returns.
func StringFromImmutableBytes(bs []byte) string {
if len(bs) == 0 {
return ""
}
return unsafe.String(&bs[0], len(bs))
}

View File

@@ -1,51 +0,0 @@
package dialer
import (
"net"
"net/netip"
"strings"
"github.com/Dreamacro/clash/component/iface"
)
func LookupLocalAddrFromIfaceName(ifaceName string, network string, destination netip.Addr, port int) (net.Addr, error) {
ifaceObj, err := iface.ResolveInterface(ifaceName)
if err != nil {
return nil, err
}
var addr *netip.Prefix
switch network {
case "udp4", "tcp4":
addr, err = ifaceObj.PickIPv4Addr(destination)
case "tcp6", "udp6":
addr, err = ifaceObj.PickIPv6Addr(destination)
default:
if destination.IsValid() {
if destination.Is4() || destination.Is4In6() {
addr, err = ifaceObj.PickIPv4Addr(destination)
} else {
addr, err = ifaceObj.PickIPv6Addr(destination)
}
} else {
addr, err = ifaceObj.PickIPv4Addr(destination)
}
}
if err != nil {
return nil, err
}
if strings.HasPrefix(network, "tcp") {
return &net.TCPAddr{
IP: addr.Addr().AsSlice(),
Port: port,
}, nil
} else if strings.HasPrefix(network, "udp") {
return &net.UDPAddr{
IP: addr.Addr().AsSlice(),
Port: port,
}, nil
}
return nil, iface.ErrAddrNotFound
}

View File

@@ -7,8 +7,52 @@ import (
"net/netip"
"strconv"
"strings"
"github.com/Dreamacro/clash/component/iface"
)
func lookupLocalAddr(ifaceName string, network string, destination netip.Addr, port int) (net.Addr, error) {
ifaceObj, err := iface.ResolveInterface(ifaceName)
if err != nil {
return nil, err
}
var addr *netip.Prefix
switch network {
case "udp4", "tcp4":
addr, err = ifaceObj.PickIPv4Addr(destination)
case "tcp6", "udp6":
addr, err = ifaceObj.PickIPv6Addr(destination)
default:
if destination.IsValid() {
if destination.Is4() {
addr, err = ifaceObj.PickIPv4Addr(destination)
} else {
addr, err = ifaceObj.PickIPv6Addr(destination)
}
} else {
addr, err = ifaceObj.PickIPv4Addr(destination)
}
}
if err != nil {
return nil, err
}
if strings.HasPrefix(network, "tcp") {
return &net.TCPAddr{
IP: addr.Addr().AsSlice(),
Port: port,
}, nil
} else if strings.HasPrefix(network, "udp") {
return &net.UDPAddr{
IP: addr.Addr().AsSlice(),
Port: port,
}, nil
}
return nil, iface.ErrAddrNotFound
}
func bindIfaceToDialer(ifaceName string, dialer *net.Dialer, network string, destination netip.Addr) error {
if !destination.IsGlobalUnicast() {
return nil
@@ -22,7 +66,7 @@ func bindIfaceToDialer(ifaceName string, dialer *net.Dialer, network string, des
}
}
addr, err := LookupLocalAddrFromIfaceName(ifaceName, network, destination, int(local))
addr, err := lookupLocalAddr(ifaceName, network, destination, int(local))
if err != nil {
return err
}
@@ -40,7 +84,7 @@ func bindIfaceToListenConfig(ifaceName string, _ *net.ListenConfig, network, add
local, _ := strconv.ParseUint(port, 10, 16)
addr, err := LookupLocalAddrFromIfaceName(ifaceName, network, netip.Addr{}, int(local))
addr, err := lookupLocalAddr(ifaceName, network, netip.Addr{}, int(local))
if err != nil {
return "", err
}

View File

@@ -20,20 +20,3 @@ func addControlToListenConfig(lc *net.ListenConfig, fn controlFn) {
return fn(context.Background(), network, address, c)
}
}
func addControlToDialer(d *net.Dialer, fn controlFn) {
ld := *d
d.ControlContext = func(ctx context.Context, network, address string, c syscall.RawConn) (err error) {
switch {
case ld.ControlContext != nil:
if err = ld.ControlContext(ctx, network, address, c); err != nil {
return
}
case ld.Control != nil:
if err = ld.Control(network, address, c); err != nil {
return
}
}
return fn(ctx, network, address, c)
}
}

View File

@@ -0,0 +1,22 @@
//go:build !go1.20
package dialer
import (
"context"
"net"
"syscall"
)
func addControlToDialer(d *net.Dialer, fn controlFn) {
ld := *d
d.Control = func(network, address string, c syscall.RawConn) (err error) {
switch {
case ld.Control != nil:
if err = ld.Control(network, address, c); err != nil {
return
}
}
return fn(context.Background(), network, address, c)
}
}

View File

@@ -0,0 +1,26 @@
//go:build go1.20
package dialer
import (
"context"
"net"
"syscall"
)
func addControlToDialer(d *net.Dialer, fn controlFn) {
ld := *d
d.ControlContext = func(ctx context.Context, network, address string, c syscall.RawConn) (err error) {
switch {
case ld.ControlContext != nil:
if err = ld.ControlContext(ctx, network, address, c); err != nil {
return
}
case ld.Control != nil:
if err = ld.Control(network, address, c); err != nil {
return
}
}
return fn(ctx, network, address, c)
}
}

View File

@@ -2,7 +2,6 @@ package dialer
import (
"context"
"errors"
"fmt"
"net"
"net/netip"
@@ -132,9 +131,6 @@ func dialContext(ctx context.Context, network string, destination netip.Addr, po
if opt.routingMark != 0 {
bindMarkToDialer(opt.routingMark, dialer, network, destination)
}
if opt.mpTcp {
setMultiPathTCP(dialer)
}
if opt.tfo {
return dialTFO(ctx, *dialer, network, address)
}
@@ -162,22 +158,14 @@ func concurrentDualStackDialContext(ctx context.Context, network string, ips []n
func dualStackDialContext(ctx context.Context, dialFn dialFunc, network string, ips []netip.Addr, port string, opt *option) (net.Conn, error) {
ipv4s, ipv6s := resolver.SortationAddr(ips)
if len(ipv4s) == 0 && len(ipv6s) == 0 {
return nil, ErrorNoIpAddress
}
preferIPVersion := opt.prefer
fallbackTicker := time.NewTicker(fallbackTimeout)
defer fallbackTicker.Stop()
results := make(chan dialResult)
returned := make(chan struct{})
defer close(returned)
var wg sync.WaitGroup
racer := func(ips []netip.Addr, isPrimary bool) {
defer wg.Done()
result := dialResult{isPrimary: isPrimary}
defer func() {
select {
@@ -190,36 +178,18 @@ func dualStackDialContext(ctx context.Context, dialFn dialFunc, network string,
}()
result.Conn, result.error = dialFn(ctx, network, ips, port, opt)
}
if len(ipv4s) != 0 {
wg.Add(1)
go racer(ipv4s, preferIPVersion != 6)
}
if len(ipv6s) != 0 {
wg.Add(1)
go racer(ipv6s, preferIPVersion != 4)
}
go func() {
wg.Wait()
close(results)
}()
go racer(ipv4s, preferIPVersion != 6)
go racer(ipv6s, preferIPVersion != 4)
var fallback dialResult
var errs []error
loop:
for {
for i := 0; i < 2; {
select {
case <-fallbackTicker.C:
if fallback.error == nil && fallback.Conn != nil {
return fallback.Conn, nil
}
case res, ok := <-results:
if !ok {
break loop
}
case res := <-results:
i++
if res.error == nil {
if res.isPrimary {
return res.Conn, nil
@@ -234,11 +204,10 @@ loop:
}
}
}
if fallback.error == nil && fallback.Conn != nil {
return fallback.Conn, nil
}
return nil, errors.Join(errs...)
return nil, errorsJoin(errs...)
}
func parallelDialContext(ctx context.Context, network string, ips []netip.Addr, port string, opt *option) (net.Conn, error) {
@@ -275,7 +244,7 @@ func parallelDialContext(ctx context.Context, network string, ips []netip.Addr,
}
if len(errs) > 0 {
return nil, errors.Join(errs...)
return nil, errorsJoin(errs...)
}
return nil, os.ErrDeadlineExceeded
}
@@ -292,7 +261,7 @@ func serialDialContext(ctx context.Context, network string, ips []netip.Addr, po
errs = append(errs, err)
}
}
return nil, errors.Join(errs...)
return nil, errorsJoin(errs...)
}
type dialResult struct {

View File

@@ -2,9 +2,17 @@ package dialer
import (
"errors"
E "github.com/sagernet/sing/common/exceptions"
)
var (
ErrorNoIpAddress = errors.New("no ip address")
ErrorInvalidedNetworkStack = errors.New("invalided network stack")
)
func errorsJoin(errs ...error) error {
// compatibility with golang<1.20
// maybe use errors.Join(errs...) is better after we drop the old version's support
return E.Errors(errs...)
}

View File

@@ -1,12 +0,0 @@
//go:build !go1.21
package dialer
import (
"net"
)
const multipathTCPAvailable = false
func setMultiPathTCP(dialer *net.Dialer) {
}

View File

@@ -1,11 +0,0 @@
//go:build go1.21
package dialer
import "net"
const multipathTCPAvailable = true
func setMultiPathTCP(dialer *net.Dialer) {
dialer.SetMultipathTCP(true)
}

View File

@@ -25,7 +25,6 @@ type option struct {
network int
prefer int
tfo bool
mpTcp bool
resolver resolver.Resolver
netDialer NetDialer
}
@@ -84,12 +83,6 @@ func WithTFO(tfo bool) Option {
}
}
func WithMPTCP(mpTcp bool) Option {
return func(opt *option) {
opt.mpTcp = mpTcp
}
}
func WithNetDialer(netDialer NetDialer) Option {
return func(opt *option) {
opt.netDialer = netDialer

View File

@@ -4,7 +4,6 @@ import (
"errors"
"net"
"net/netip"
"strings"
"time"
"github.com/Dreamacro/clash/common/singledo"
@@ -38,21 +37,12 @@ func ResolveInterface(name string) (*Interface, error) {
if err != nil {
continue
}
// if not available device like Meta, dummy0, docker0, etc.
if (iface.Flags&net.FlagMulticast == 0) || (iface.Flags&net.FlagPointToPoint != 0) || (iface.Flags&net.FlagRunning == 0) {
continue
}
ipNets := make([]*netip.Prefix, 0, len(addrs))
for _, addr := range addrs {
ipNet := addr.(*net.IPNet)
ip, _ := netip.AddrFromSlice(ipNet.IP)
//unavailable IPv6 Address
if ip.Is6() && strings.HasPrefix(ip.String(), "fe80") {
continue
}
ones, bits := ipNet.Mask.Size()
if bits == 32 {
ip = ip.Unmap()

View File

@@ -12,68 +12,42 @@ import (
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/log"
"github.com/oschwald/maxminddb-golang"
)
type databaseType = uint8
const (
typeMaxmind databaseType = iota
typeSing
typeMetaV0
"github.com/oschwald/geoip2-golang"
)
var (
reader Reader
once sync.Once
mmdb *geoip2.Reader
once sync.Once
)
func LoadFromBytes(buffer []byte) {
once.Do(func() {
mmdb, err := maxminddb.FromBytes(buffer)
var err error
mmdb, err = geoip2.FromBytes(buffer)
if err != nil {
log.Fatalln("Can't load mmdb: %s", err.Error())
}
reader = Reader{Reader: mmdb}
switch mmdb.Metadata.DatabaseType {
case "sing-geoip":
reader.databaseType = typeSing
case "Meta-geoip0":
reader.databaseType = typeMetaV0
default:
reader.databaseType = typeMaxmind
}
})
}
func Verify() bool {
instance, err := maxminddb.Open(C.Path.MMDB())
instance, err := geoip2.Open(C.Path.MMDB())
if err == nil {
instance.Close()
}
return err == nil
}
func Instance() Reader {
func Instance() *geoip2.Reader {
once.Do(func() {
mmdbPath := C.Path.MMDB()
log.Debugln("Load MMDB file: %s", mmdbPath)
mmdb, err := maxminddb.Open(mmdbPath)
var err error
mmdb, err = geoip2.Open(C.Path.MMDB())
if err != nil {
log.Fatalln("Can't load MMDB: %s", err.Error())
}
reader = Reader{Reader: mmdb}
switch mmdb.Metadata.DatabaseType {
case "sing-geoip":
reader.databaseType = typeSing
case "Meta-geoip0":
reader.databaseType = typeMetaV0
default:
reader.databaseType = typeMaxmind
log.Fatalln("Can't load mmdb: %s", err.Error())
}
})
return reader
return mmdb
}
func DownloadMMDB(path string) (err error) {

View File

@@ -1,56 +0,0 @@
package mmdb
import (
"fmt"
"net"
"github.com/oschwald/maxminddb-golang"
"github.com/sagernet/sing/common"
)
type geoip2Country struct {
Country struct {
IsoCode string `maxminddb:"iso_code"`
} `maxminddb:"country"`
}
type Reader struct {
*maxminddb.Reader
databaseType
}
func (r Reader) LookupCode(ipAddress net.IP) []string {
switch r.databaseType {
case typeMaxmind:
var country geoip2Country
_ = r.Lookup(ipAddress, &country)
if country.Country.IsoCode == "" {
return []string{}
}
return []string{country.Country.IsoCode}
case typeSing:
var code string
_ = r.Lookup(ipAddress, &code)
if code == "" {
return []string{}
}
return []string{code}
case typeMetaV0:
var record any
_ = r.Lookup(ipAddress, &record)
switch record := record.(type) {
case string:
return []string{record}
case []any: // lookup returned type of slice is []any
return common.Map(record, func(it any) string {
return it.(string)
})
}
return []string{}
default:
panic(fmt.Sprint("unknown geoip database type:", r.databaseType))
}
}

View File

@@ -1,26 +0,0 @@
package nat
import (
"net"
"github.com/Dreamacro/clash/common/atomic"
C "github.com/Dreamacro/clash/constant"
)
type writeBackProxy struct {
wb atomic.TypedValue[C.WriteBack]
}
func (w *writeBackProxy) WriteBack(b []byte, addr net.Addr) (n int, err error) {
return w.wb.Load().WriteBack(b, addr)
}
func (w *writeBackProxy) UpdateWriteBack(wb C.WriteBack) {
w.wb.Store(wb)
}
func NewWriteBackProxy(wb C.WriteBack) C.WriteBackProxy {
w := &writeBackProxy{}
w.UpdateWriteBack(wb)
return w
}

View File

@@ -13,24 +13,22 @@ type Table struct {
type Entry struct {
PacketConn C.PacketConn
WriteBackProxy C.WriteBackProxy
LocalUDPConnMap sync.Map
}
func (t *Table) Set(key string, e C.PacketConn, w C.WriteBackProxy) {
func (t *Table) Set(key string, e C.PacketConn) {
t.mapping.Store(key, &Entry{
PacketConn: e,
WriteBackProxy: w,
LocalUDPConnMap: sync.Map{},
})
}
func (t *Table) Get(key string) (C.PacketConn, C.WriteBackProxy) {
func (t *Table) Get(key string) C.PacketConn {
entry, exist := t.getEntry(key)
if !exist {
return nil, nil
return nil
}
return entry.PacketConn, entry.WriteBackProxy
return entry.PacketConn
}
func (t *Table) GetOrCreateLock(key string) (*sync.Cond, bool) {

View File

@@ -67,7 +67,7 @@ func findProcessName(network string, ip netip.Addr, srcPort int) (uint32, string
err := initWin32API()
if err != nil {
log.Errorln("Initialize PROCESS-NAME failed: %s", err.Error())
log.Warnln("All PROCESS-NAMES rules will be skipped")
log.Warnln("All PROCESS-NAMES rules will be skiped")
return
}
})

View File

@@ -9,8 +9,6 @@ import (
types "github.com/Dreamacro/clash/constant/provider"
"github.com/Dreamacro/clash/log"
"github.com/samber/lo"
)
var (
@@ -67,7 +65,7 @@ func (f *Fetcher[V]) Initial() (V, error) {
}
if err != nil {
return lo.Empty[V](), err
return getZero[V](), err
}
var contents V
@@ -87,18 +85,18 @@ func (f *Fetcher[V]) Initial() (V, error) {
if err != nil {
if !isLocal {
return lo.Empty[V](), err
return getZero[V](), err
}
// parse local file error, fallback to remote
buf, err = f.vehicle.Read()
if err != nil {
return lo.Empty[V](), err
return getZero[V](), err
}
contents, err = f.parser(buf)
if err != nil {
return lo.Empty[V](), err
return getZero[V](), err
}
isLocal = false
@@ -106,7 +104,7 @@ func (f *Fetcher[V]) Initial() (V, error) {
if f.vehicle.Type() != types.File && !isLocal {
if err := safeWrite(f.vehicle.Path(), buf); err != nil {
return lo.Empty[V](), err
return getZero[V](), err
}
}
@@ -123,7 +121,7 @@ func (f *Fetcher[V]) Initial() (V, error) {
func (f *Fetcher[V]) Update() (V, bool, error) {
buf, err := f.vehicle.Read()
if err != nil {
return lo.Empty[V](), false, err
return getZero[V](), false, err
}
now := time.Now()
@@ -131,17 +129,17 @@ func (f *Fetcher[V]) Update() (V, bool, error) {
if bytes.Equal(f.hash[:], hash[:]) {
f.UpdatedAt = &now
_ = os.Chtimes(f.vehicle.Path(), now, now)
return lo.Empty[V](), true, nil
return getZero[V](), true, nil
}
contents, err := f.parser(buf)
if err != nil {
return lo.Empty[V](), false, err
return getZero[V](), false, err
}
if f.vehicle.Type() != types.File {
if err := safeWrite(f.vehicle.Path(), buf); err != nil {
return lo.Empty[V](), false, err
return getZero[V](), false, err
}
}
@@ -212,3 +210,8 @@ func NewFetcher[V any](name string, interval time.Duration, vehicle types.Vehicl
interval: interval,
}
}
func getZero[V any]() V {
var result V
return result
}

View File

@@ -2,14 +2,12 @@ package resource
import (
"context"
"errors"
clashHttp "github.com/Dreamacro/clash/component/http"
types "github.com/Dreamacro/clash/constant/provider"
"io"
"net/http"
"os"
"time"
clashHttp "github.com/Dreamacro/clash/component/http"
types "github.com/Dreamacro/clash/constant/provider"
)
type FileVehicle struct {
@@ -56,10 +54,8 @@ func (h *HTTPVehicle) Read() ([]byte, error) {
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode > 299 {
return nil, errors.New(resp.Status)
}
buf, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err

View File

@@ -10,11 +10,11 @@ import (
type SnifferConfig struct {
OverrideDest bool
Ports utils.IntRanges[uint16]
Ports []utils.Range[uint16]
}
type BaseSniffer struct {
ports utils.IntRanges[uint16]
ports []utils.Range[uint16]
supportNetworkType constant.NetWork
}
@@ -35,10 +35,15 @@ func (bs *BaseSniffer) SupportNetwork() constant.NetWork {
// SupportPort implements sniffer.Sniffer
func (bs *BaseSniffer) SupportPort(port uint16) bool {
return bs.ports.Check(port)
for _, portRange := range bs.ports {
if portRange.Contains(port) {
return true
}
}
return false
}
func NewBaseSniffer(ports utils.IntRanges[uint16], networkType constant.NetWork) *BaseSniffer {
func NewBaseSniffer(ports []utils.Range[uint16], networkType constant.NetWork) *BaseSniffer {
return &BaseSniffer{
ports: ports,
supportNetworkType: networkType,

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"net"
"net/netip"
"strconv"
"sync"
"time"
@@ -25,23 +26,29 @@ var (
var Dispatcher *SnifferDispatcher
type SnifferDispatcher struct {
enable bool
sniffers map[sniffer.Sniffer]SnifferConfig
forceDomain *trie.DomainSet
skipSNI *trie.DomainSet
skipList *cache.LruCache[string, uint8]
rwMux sync.RWMutex
forceDnsMapping bool
parsePureIp bool
enable bool
sniffers map[sniffer.Sniffer]SnifferConfig
forceDomain *trie.DomainSet
skipSNI *trie.DomainSet
skipList *cache.LruCache[string, uint8]
rwMux sync.RWMutex
forceDnsMapping bool
parsePureIp bool
}
func (sd *SnifferDispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata) {
if (metadata.Host == "" && sd.parsePureIp) || sd.forceDomain.Has(metadata.Host) || (metadata.DNSMode == C.DNSMapping && sd.forceDnsMapping) {
port, err := strconv.ParseUint(metadata.DstPort, 10, 16)
if err != nil {
log.Debugln("[Sniffer] Dst port is error")
return
}
inWhitelist := false
overrideDest := false
for sniffer, config := range sd.sniffers {
if sniffer.SupportNetwork() == C.TCP || sniffer.SupportNetwork() == C.ALLNet {
inWhitelist = sniffer.SupportPort(metadata.DstPort)
inWhitelist = sniffer.SupportPort(uint16(port))
if inWhitelist {
overrideDest = config.OverrideDest
break
@@ -54,7 +61,7 @@ func (sd *SnifferDispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata
}
sd.rwMux.RLock()
dst := fmt.Sprintf("%s:%d", metadata.DstIP, metadata.DstPort)
dst := fmt.Sprintf("%s:%s", metadata.DstIP, metadata.DstPort)
if count, ok := sd.skipList.Get(dst); ok && count > 5 {
log.Debugln("[Sniffer] Skip sniffing[%s] due to multiple failures", dst)
defer sd.rwMux.RUnlock()
@@ -64,7 +71,7 @@ func (sd *SnifferDispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata
if host, err := sd.sniffDomain(conn, metadata); err != nil {
sd.cacheSniffFailed(metadata)
log.Debugln("[Sniffer] All sniffing sniff failed with from [%s:%d] to [%s:%d]", metadata.SrcIP, metadata.SrcPort, metadata.String(), metadata.DstPort)
log.Debugln("[Sniffer] All sniffing sniff failed with from [%s:%s] to [%s:%s]", metadata.SrcIP, metadata.SrcPort, metadata.String(), metadata.DstPort)
return
} else {
if sd.skipSNI.Has(host) {
@@ -142,7 +149,7 @@ func (sd *SnifferDispatcher) sniffDomain(conn *N.BufferedConn, metadata *C.Metad
func (sd *SnifferDispatcher) cacheSniffFailed(metadata *C.Metadata) {
sd.rwMux.Lock()
dst := fmt.Sprintf("%s:%d", metadata.DstIP, metadata.DstPort)
dst := fmt.Sprintf("%s:%s", metadata.DstIP, metadata.DstPort)
count, _ := sd.skipList.Get(dst)
if count <= 5 {
count++

View File

@@ -34,9 +34,11 @@ type HTTPSniffer struct {
var _ sniffer.Sniffer = (*HTTPSniffer)(nil)
func NewHTTPSniffer(snifferConfig SnifferConfig) (*HTTPSniffer, error) {
ports := snifferConfig.Ports
if len(ports) == 0 {
ports = utils.IntRanges[uint16]{utils.NewRange[uint16](80, 80)}
ports := make([]utils.Range[uint16], 0)
if len(snifferConfig.Ports) == 0 {
ports = append(ports, *utils.NewRange[uint16](80, 80))
} else {
ports = append(ports, snifferConfig.Ports...)
}
return &HTTPSniffer{
BaseSniffer: NewBaseSniffer(ports, C.TCP),

View File

@@ -22,9 +22,11 @@ type TLSSniffer struct {
}
func NewTLSSniffer(snifferConfig SnifferConfig) (*TLSSniffer, error) {
ports := snifferConfig.Ports
if len(ports) == 0 {
ports = utils.IntRanges[uint16]{utils.NewRange[uint16](443, 443)}
ports := make([]utils.Range[uint16], 0)
if len(snifferConfig.Ports) == 0 {
ports = append(ports, *utils.NewRange[uint16](443, 443))
} else {
ports = append(ports, snifferConfig.Ports...)
}
return &TLSSniffer{
BaseSniffer: NewBaseSniffer(ports, C.TCP),

View File

@@ -10,12 +10,14 @@ import (
"fmt"
"strings"
"sync"
xtls "github.com/xtls/go"
)
var trustCerts []*x509.Certificate
var certPool *x509.CertPool
var mutex sync.RWMutex
var errNotMatch = errors.New("certificate fingerprints do not match")
var errNotMacth error = errors.New("certificate fingerprints do not match")
func AddCertificate(certificate string) error {
mutex.Lock()
@@ -77,7 +79,7 @@ func verifyFingerprint(fingerprint *[32]byte) func(rawCerts [][]byte, verifiedCh
}
}
}
return errNotMatch
return errNotMacth
}
}
@@ -120,3 +122,27 @@ func GetGlobalTLSConfig(tlsConfig *tls.Config) *tls.Config {
tlsConfig.RootCAs = certPool
return tlsConfig
}
// GetSpecifiedFingerprintXTLSConfig specified fingerprint
func GetSpecifiedFingerprintXTLSConfig(tlsConfig *xtls.Config, fingerprint string) (*xtls.Config, error) {
if fingerprintBytes, err := convertFingerprint(fingerprint); err != nil {
return nil, err
} else {
tlsConfig = GetGlobalXTLSConfig(tlsConfig)
tlsConfig.VerifyPeerCertificate = verifyFingerprint(fingerprintBytes)
tlsConfig.InsecureSkipVerify = true
return tlsConfig, nil
}
}
func GetGlobalXTLSConfig(tlsConfig *xtls.Config) *xtls.Config {
certPool := getCertPool()
if tlsConfig == nil {
return &xtls.Config{
RootCAs: certPool,
}
}
tlsConfig.RootCAs = certPool
return tlsConfig
}

View File

@@ -22,11 +22,9 @@ import (
"github.com/Dreamacro/clash/common/utils"
"github.com/Dreamacro/clash/log"
"github.com/Dreamacro/clash/ntp"
utls "github.com/sagernet/utls"
"github.com/zhangyunhao116/fastrand"
"golang.org/x/crypto/chacha20poly1305"
"golang.org/x/crypto/curve25519"
"golang.org/x/crypto/hkdf"
"golang.org/x/net/http2"
@@ -39,9 +37,6 @@ type RealityConfig struct {
ShortID [RealityMaxShortIDLen]byte
}
//go:linkname aesgcmPreferred crypto/tls.aesgcmPreferred
func aesgcmPreferred(ciphers []uint16) bool
func GetRealityConn(ctx context.Context, conn net.Conn, ClientFingerprint string, tlsConfig *tls.Config, realityConfig *RealityConfig) (net.Conn, error) {
if fingerprint, exists := GetFingerprint(ClientFingerprint); exists {
verifier := &realityVerifier{
@@ -66,17 +61,17 @@ func GetRealityConn(ctx context.Context, conn net.Conn, ClientFingerprint string
}
hello := uConn.HandshakeState.Hello
rawSessionID := hello.Raw[39 : 39+32] // the location of session ID
for i := range rawSessionID { // https://github.com/golang/go/issues/5373
rawSessionID[i] = 0
for i := range hello.SessionId { // https://github.com/golang/go/issues/5373
hello.SessionId[i] = 0
}
copy(hello.Raw[39:], hello.SessionId)
binary.BigEndian.PutUint64(hello.SessionId, uint64(ntp.Now().Unix()))
binary.BigEndian.PutUint64(hello.SessionId, uint64(time.Now().Unix()))
copy(hello.SessionId[8:], realityConfig.ShortID[:])
hello.SessionId[0] = 1
hello.SessionId[1] = 8
hello.SessionId[2] = 2
hello.SessionId[2] = 0
copy(hello.SessionId[8:], realityConfig.ShortID[:])
//log.Debugln("REALITY hello.sessionId[:16]: %v", hello.SessionId[:16])
@@ -89,14 +84,9 @@ func GetRealityConn(ctx context.Context, conn net.Conn, ClientFingerprint string
if err != nil {
return nil, err
}
var aeadCipher cipher.AEAD
if aesgcmPreferred(hello.CipherSuites) {
aesBlock, _ := aes.NewCipher(authKey)
aeadCipher, _ = cipher.NewGCM(aesBlock)
} else {
aeadCipher, _ = chacha20poly1305.New(authKey)
}
aeadCipher.Seal(hello.SessionId[:0], hello.Random[20:], hello.SessionId[:16], hello.Raw)
aesBlock, _ := aes.NewCipher(authKey)
aesGcmCipher, _ := cipher.NewGCM(aesBlock)
aesGcmCipher.Seal(hello.SessionId[:0], hello.Random[20:], hello.SessionId[:16], hello.Raw)
copy(hello.Raw[39:], hello.SessionId)
//log.Debugln("REALITY hello.sessionId: %v", hello.SessionId)
//log.Debugln("REALITY uConn.AuthKey: %v", authKey)
@@ -106,7 +96,7 @@ func GetRealityConn(ctx context.Context, conn net.Conn, ClientFingerprint string
return nil, err
}
log.Debugln("REALITY Authentication: %v, AEAD: %T", verifier.verified, aeadCipher)
log.Debugln("REALITY Authentication: %v", verifier.verified)
if !verifier.verified {
go realityClientFallback(uConn, uConfig.ServerName, clientID)
@@ -147,7 +137,7 @@ type realityVerifier struct {
verified bool
}
var pOffset = utils.MustOK(reflect.TypeOf((*utls.Conn)(nil)).Elem().FieldByName("peerCertificates")).Offset
var pOffset = utils.MustOK(reflect.TypeOf((*utls.UConn)(nil)).Elem().FieldByName("peerCertificates")).Offset
func (c *realityVerifier) VerifyPeerCertificate(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
//p, _ := reflect.TypeOf(c.Conn).Elem().FieldByName("peerCertificates")

View File

@@ -23,8 +23,6 @@ type DomainSet struct {
ranks, selects []int32
}
type qElt struct{ s, e, col int }
// NewDomainSet creates a new *DomainSet struct, from a DomainTrie.
func (t *DomainTrie[T]) NewDomainSet() *DomainSet {
reserveDomains := make([]string, 0)
@@ -41,6 +39,7 @@ func (t *DomainTrie[T]) NewDomainSet() *DomainSet {
ss := &DomainSet{}
lIdx := 0
type qElt struct{ s, e, col int }
queue := []qElt{{0, len(keys), 0}}
for i := 0; i < len(queue); i++ {
elt := queue[i]

View File

@@ -1,9 +1,8 @@
package trie
import (
"net"
"github.com/Dreamacro/clash/log"
"net"
)
type IPV6 bool
@@ -48,10 +47,11 @@ func (trie *IpCidrTrie) AddIpCidrForString(ipCidr string) error {
}
func (trie *IpCidrTrie) IsContain(ip net.IP) bool {
ip, isIpv4 := checkAndConverterIp(ip)
if ip == nil {
return false
}
isIpv4 := len(ip) == net.IPv4len
var groupValues []uint32
var ipCidrNode *IpCidrNode
@@ -71,13 +71,7 @@ func (trie *IpCidrTrie) IsContain(ip net.IP) bool {
}
func (trie *IpCidrTrie) IsContainForString(ipString string) bool {
ip := net.ParseIP(ipString)
// deal with 4in6
actualIp := ip.To4()
if actualIp == nil {
actualIp = ip
}
return trie.IsContain(actualIp)
return trie.IsContain(net.ParseIP(ipString))
}
func ipCidrToSubIpCidr(ipNet *net.IPNet) ([]net.IP, int, bool, error) {
@@ -88,8 +82,9 @@ func ipCidrToSubIpCidr(ipNet *net.IPNet) ([]net.IP, int, bool, error) {
isIpv4 bool
err error
)
isIpv4 = len(ipNet.IP) == net.IPv4len
ipList, newMaskSize, err = subIpCidr(ipNet.IP, maskSize, isIpv4)
ip, isIpv4 := checkAndConverterIp(ipNet.IP)
ipList, newMaskSize, err = subIpCidr(ip, maskSize, isIpv4)
return ipList, newMaskSize, isIpv4, err
}
@@ -243,3 +238,18 @@ func search(root *IpCidrNode, groupValues []uint32) *IpCidrNode {
return nil
}
// return net.IP To4 or To16 and is ipv4
func checkAndConverterIp(ip net.IP) (net.IP, bool) {
ipResult := ip.To4()
if ipResult == nil {
ipResult = ip.To16()
if ipResult == nil {
return nil, false
}
return ipResult, false
}
return ipResult, true
}

View File

@@ -3,9 +3,8 @@ package trie
import (
"net"
"testing"
"github.com/stretchr/testify/assert"
)
import "github.com/stretchr/testify/assert"
func TestIpv4AddSuccess(t *testing.T) {
trie := NewIpCidrTrie()
@@ -97,11 +96,5 @@ func TestIpv6Search(t *testing.T) {
assert.Equal(t, true, trie.IsContainForString("2001:67c:4e8:9666::1213"))
assert.Equal(t, false, trie.IsContain(net.ParseIP("22233:22")))
}
func TestIpv4InIpv6(t *testing.T) {
trie := NewIpCidrTrie()
// Boundary testing
assert.NoError(t, trie.AddIpCidrForString("::ffff:198.18.5.138/128"))
}

View File

@@ -9,6 +9,7 @@ import (
"net/url"
"os"
"regexp"
"strconv"
"strings"
"time"
@@ -51,7 +52,6 @@ type General struct {
IPv6 bool `json:"ipv6"`
Interface string `json:"interface-name"`
RoutingMark int `json:"-"`
GeoXUrl GeoXUrl `json:"geox-url"`
GeodataMode bool `json:"geodata-mode"`
GeodataLoader string `json:"geodata-loader"`
TCPConcurrent bool `json:"tcp-concurrent"`
@@ -76,7 +76,6 @@ type Inbound struct {
AllowLan bool `json:"allow-lan"`
BindAddress string `json:"bind-address"`
InboundTfo bool `json:"inbound-tfo"`
InboundMPTCP bool `json:"inbound-mptcp"`
}
// Controller config
@@ -87,14 +86,6 @@ type Controller struct {
Secret string `json:"-"`
}
// NTP config
type NTP struct {
Enable bool `yaml:"enable"`
Server string `yaml:"server"`
Port int `yaml:"port"`
Interval int `yaml:"interval"`
}
// DNS config
type DNS struct {
Enable bool `yaml:"enable"`
@@ -159,7 +150,6 @@ type Experimental struct {
type Config struct {
General *General
IPTables *IPTables
NTP *NTP
DNS *DNS
Experimental *Experimental
Hosts *trie.DomainTrie[resolver.HostValue]
@@ -176,13 +166,6 @@ type Config struct {
TLS *TLS
}
type RawNTP struct {
Enable bool `yaml:"enable"`
Server string `yaml:"server"`
ServerPort int `yaml:"server-port"`
Interval int `yaml:"interval"`
}
type RawDNS struct {
Enable bool `yaml:"enable"`
PreferH3 bool `yaml:"prefer-h3"`
@@ -237,18 +220,16 @@ type RawTun struct {
}
type RawTuicServer struct {
Enable bool `yaml:"enable" json:"enable"`
Listen string `yaml:"listen" json:"listen"`
Token []string `yaml:"token" json:"token"`
Users map[string]string `yaml:"users" json:"users,omitempty"`
Certificate string `yaml:"certificate" json:"certificate"`
PrivateKey string `yaml:"private-key" json:"private-key"`
CongestionController string `yaml:"congestion-controller" json:"congestion-controller,omitempty"`
MaxIdleTime int `yaml:"max-idle-time" json:"max-idle-time,omitempty"`
AuthenticationTimeout int `yaml:"authentication-timeout" json:"authentication-timeout,omitempty"`
ALPN []string `yaml:"alpn" json:"alpn,omitempty"`
MaxUdpRelayPacketSize int `yaml:"max-udp-relay-packet-size" json:"max-udp-relay-packet-size,omitempty"`
CWND int `yaml:"cwnd" json:"cwnd,omitempty"`
Enable bool `yaml:"enable" json:"enable"`
Listen string `yaml:"listen" json:"listen"`
Token []string `yaml:"token" json:"token"`
Certificate string `yaml:"certificate" json:"certificate"`
PrivateKey string `yaml:"private-key" json:"private-key"`
CongestionController string `yaml:"congestion-controller" json:"congestion-controller,omitempty"`
MaxIdleTime int `yaml:"max-idle-time" json:"max-idle-time,omitempty"`
AuthenticationTimeout int `yaml:"authentication-timeout" json:"authentication-timeout,omitempty"`
ALPN []string `yaml:"alpn" json:"alpn,omitempty"`
MaxUdpRelayPacketSize int `yaml:"max-udp-relay-packet-size" json:"max-udp-relay-packet-size,omitempty"`
}
type RawConfig struct {
@@ -260,7 +241,6 @@ type RawConfig struct {
ShadowSocksConfig string `yaml:"ss-config"`
VmessConfig string `yaml:"vmess-config"`
InboundTfo bool `yaml:"inbound-tfo"`
InboundMPTCP bool `yaml:"inbound-mptcp"`
Authentication []string `yaml:"authentication"`
AllowLan bool `yaml:"allow-lan"`
BindAddress string `yaml:"bind-address"`
@@ -285,7 +265,6 @@ type RawConfig struct {
ProxyProvider map[string]map[string]any `yaml:"proxy-providers"`
RuleProvider map[string]map[string]any `yaml:"rule-providers"`
Hosts map[string]any `yaml:"hosts"`
NTP RawNTP `yaml:"ntp"`
DNS RawDNS `yaml:"dns"`
Tun RawTun `yaml:"tun"`
TuicServer RawTuicServer `yaml:"tuic-server"`
@@ -293,7 +272,7 @@ type RawConfig struct {
IPTables IPTables `yaml:"iptables"`
Experimental Experimental `yaml:"experimental"`
Profile Profile `yaml:"profile"`
GeoXUrl GeoXUrl `yaml:"geox-url"`
GeoXUrl RawGeoXUrl `yaml:"geox-url"`
Proxy []map[string]any `yaml:"proxies"`
ProxyGroup []map[string]any `yaml:"proxy-groups"`
Rule []string `yaml:"rules"`
@@ -302,7 +281,7 @@ type RawConfig struct {
Listeners []map[string]any `yaml:"listeners"`
}
type GeoXUrl struct {
type RawGeoXUrl struct {
GeoIp string `yaml:"geoip" json:"geoip"`
Mmdb string `yaml:"mmdb" json:"mmdb"`
GeoSite string `yaml:"geosite" json:"geosite"`
@@ -377,7 +356,6 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) {
TuicServer: RawTuicServer{
Enable: false,
Token: nil,
Users: nil,
Certificate: "",
PrivateKey: "",
Listen: "",
@@ -438,10 +416,10 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) {
Profile: Profile{
StoreSelected: true,
},
GeoXUrl: GeoXUrl{
Mmdb: "https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geoip.metadb",
GeoIp: "https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geoip.dat",
GeoSite: "https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geosite.dat",
GeoXUrl: RawGeoXUrl{
Mmdb: "https://testingcf.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/country.mmdb",
GeoIp: "https://testingcf.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geoip.dat",
GeoSite: "https://testingcf.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geosite.dat",
},
}
@@ -468,7 +446,7 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) {
config.General = general
if len(config.General.GlobalClientFingerprint) != 0 {
log.Debugln("GlobalClientFingerprint: %s", config.General.GlobalClientFingerprint)
log.Debugln("GlobalClientFingerprint:%s", config.General.GlobalClientFingerprint)
tlsC.SetGlobalUtlsClient(config.General.GlobalClientFingerprint)
}
@@ -510,9 +488,6 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) {
}
config.Hosts = hosts
ntpCfg := paresNTP(rawCfg)
config.NTP = ntpCfg
dnsCfg, err := parseDNS(rawCfg, hosts, rules, ruleProviders)
if err != nil {
return nil, err
@@ -555,10 +530,6 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) {
func parseGeneral(cfg *RawConfig) (*General, error) {
externalUI := cfg.ExternalUI
geodata.SetLoader(cfg.GeodataLoader)
C.GeoIpUrl = cfg.GeoXUrl.GeoIp
C.GeoSiteUrl = cfg.GeoXUrl.GeoSite
C.MmdbUrl = cfg.GeoXUrl.Mmdb
C.GeodataMode = cfg.GeodataMode
// checkout externalUI exist
if externalUI != "" {
externalUI = C.Path.Resolve(externalUI)
@@ -579,7 +550,6 @@ func parseGeneral(cfg *RawConfig) (*General, error) {
AllowLan: cfg.AllowLan,
BindAddress: cfg.BindAddress,
InboundTfo: cfg.InboundTfo,
InboundMPTCP: cfg.InboundMPTCP,
},
Controller: Controller{
ExternalController: cfg.ExternalController,
@@ -593,7 +563,6 @@ func parseGeneral(cfg *RawConfig) (*General, error) {
IPv6: cfg.IPv6,
Interface: cfg.Interface,
RoutingMark: cfg.RoutingMark,
GeoXUrl: cfg.GeoXUrl,
GeodataMode: cfg.GeodataMode,
GeodataLoader: cfg.GeodataLoader,
TCPConcurrent: cfg.TCPConcurrent,
@@ -686,7 +655,7 @@ func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[
}
ps = append(ps, proxies[v])
}
hc := provider.NewHealthCheck(ps, "", 0, true, nil)
hc := provider.NewHealthCheck(ps, "", 0, true)
pd, _ := provider.NewCompatibleProvider(provider.ReservedName, ps, hc)
providersMap[provider.ReservedName] = pd
@@ -741,9 +710,6 @@ func parseRuleProviders(cfg *RawConfig) (ruleProviders map[string]providerTypes.
func parseSubRules(cfg *RawConfig, proxies map[string]C.Proxy) (subRules map[string][]C.Rule, err error) {
subRules = map[string][]C.Rule{}
for name := range cfg.SubRules {
subRules[name] = make([]C.Rule, 0)
}
for name, rawRules := range cfg.SubRules {
if len(name) == 0 {
return nil, fmt.Errorf("sub-rule name is empty")
@@ -948,7 +914,7 @@ func parseNameServer(servers []string, preferH3 bool) ([]dns.NameServer, error)
addr, err = hostWithDefaultPort(u.Host, "443")
if err == nil {
proxyName = ""
clearURL := url.URL{Scheme: "https", Host: addr, Path: u.Path, User: u.User}
clearURL := url.URL{Scheme: "https", Host: addr, Path: u.Path}
addr = clearURL.String()
dnsNetType = "https" // DNS over HTTPS
if len(u.Fragment) != 0 {
@@ -974,19 +940,6 @@ func parseNameServer(servers []string, preferH3 bool) ([]dns.NameServer, error)
dnsNetType = "quic" // DNS over QUIC
case "system":
dnsNetType = "system" // System DNS
case "rcode":
dnsNetType = "rcode"
addr = u.Host
switch addr {
case "success",
"format_error",
"server_failure",
"name_error",
"not_implemented",
"refused":
default:
err = fmt.Errorf("unsupported RCode type: %s", addr)
}
default:
return nil, fmt.Errorf("DNS NameServer[%d] unsupport scheme: %s", idx, u.Scheme)
}
@@ -1152,29 +1105,6 @@ func parseFallbackGeoSite(countries []string, rules []C.Rule) ([]*router.DomainM
return sites, nil
}
func paresNTP(rawCfg *RawConfig) *NTP {
var server = "time.apple.com"
var port = 123
var interval = 30
cfg := rawCfg.NTP
if len(cfg.Server) != 0 {
server = cfg.Server
}
if cfg.ServerPort != 0 {
port = cfg.ServerPort
}
if cfg.Interval != 0 {
interval = cfg.Interval
}
ntpCfg := &NTP{
Enable: cfg.Enable,
Server: server,
Port: port,
Interval: interval,
}
return ntpCfg
}
func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rules []C.Rule, ruleProviders map[string]providerTypes.RuleProvider) (*DNS, error) {
cfg := rawCfg.DNS
if cfg.Enable && len(cfg.NameServer) == 0 {
@@ -1352,7 +1282,6 @@ func parseTuicServer(rawTuic RawTuicServer, general *General) error {
Enable: rawTuic.Enable,
Listen: rawTuic.Listen,
Token: rawTuic.Token,
Users: rawTuic.Users,
Certificate: rawTuic.Certificate,
PrivateKey: rawTuic.PrivateKey,
CongestionController: rawTuic.CongestionController,
@@ -1360,7 +1289,6 @@ func parseTuicServer(rawTuic RawTuicServer, general *General) error {
AuthenticationTimeout: rawTuic.AuthenticationTimeout,
ALPN: rawTuic.ALPN,
MaxUdpRelayPacketSize: rawTuic.MaxUdpRelayPacketSize,
CWND: rawTuic.CWND,
}
return nil
}
@@ -1376,7 +1304,7 @@ func parseSniffer(snifferRaw RawSniffer) (*Sniffer, error) {
if len(snifferRaw.Sniff) != 0 {
for sniffType, sniffConfig := range snifferRaw.Sniff {
find := false
ports, err := utils.NewIntRangesFromList[uint16](sniffConfig.Ports)
ports, err := parsePortRange(sniffConfig.Ports)
if err != nil {
return nil, err
}
@@ -1403,7 +1331,7 @@ func parseSniffer(snifferRaw RawSniffer) (*Sniffer, error) {
// Deprecated: Use Sniff instead
log.Warnln("Deprecated: Use Sniff instead")
}
globalPorts, err := utils.NewIntRangesFromList[uint16](snifferRaw.Ports)
globalPorts, err := parsePortRange(snifferRaw.Ports)
if err != nil {
return nil, err
}
@@ -1448,3 +1376,28 @@ func parseSniffer(snifferRaw RawSniffer) (*Sniffer, error) {
return sniffer, nil
}
func parsePortRange(portRanges []string) ([]utils.Range[uint16], error) {
ports := make([]utils.Range[uint16], 0)
for _, portRange := range portRanges {
portRaws := strings.Split(portRange, "-")
p, err := strconv.ParseUint(portRaws[0], 10, 16)
if err != nil {
return nil, fmt.Errorf("%s format error", portRange)
}
start := uint16(p)
if len(portRaws) > 1 {
p, err = strconv.ParseUint(portRaws[1], 10, 16)
if err != nil {
return nil, fmt.Errorf("%s format error", portRange)
}
end := uint16(p)
ports = append(ports, *utils.NewRange(start, end))
} else {
ports = append(ports, *utils.NewRange(start, start))
}
}
return ports, nil
}

View File

@@ -2,6 +2,7 @@ package config
import (
"fmt"
"github.com/Dreamacro/clash/component/geodata"
"os"
C "github.com/Dreamacro/clash/constant"
@@ -27,6 +28,23 @@ func Init(dir string) error {
f.Write([]byte(`mixed-port: 7890`))
f.Close()
}
buf, _ := os.ReadFile(C.Path.Config())
rawCfg, err := UnmarshalRawConfig(buf)
if err != nil {
log.Errorln(err.Error())
fmt.Printf("configuration file %s test failed\n", C.Path.Config())
os.Exit(1)
}
if !C.GeodataMode {
C.GeodataMode = rawCfg.GeodataMode
}
C.GeoIpUrl = rawCfg.GeoXUrl.GeoIp
C.GeoSiteUrl = rawCfg.GeoXUrl.GeoSite
C.MmdbUrl = rawCfg.GeoXUrl.Mmdb
// initial GeoIP
if err := geodata.InitGeoIP(); err != nil {
return fmt.Errorf("can't initial GeoIP: %w", err)
}
return nil
}

View File

@@ -14,7 +14,7 @@ import (
clashHttp "github.com/Dreamacro/clash/component/http"
C "github.com/Dreamacro/clash/constant"
"github.com/oschwald/maxminddb-golang"
"github.com/oschwald/geoip2-golang"
)
func UpdateGeoDatabases() error {
@@ -44,7 +44,7 @@ func UpdateGeoDatabases() error {
return fmt.Errorf("can't download MMDB database file: %w", err)
}
instance, err := maxminddb.FromBytes(data)
instance, err := geoip2.FromBytes(data)
if err != nil {
return fmt.Errorf("invalid MMDB database file: %s", err)
}

View File

@@ -10,7 +10,6 @@ import (
"time"
N "github.com/Dreamacro/clash/common/net"
"github.com/Dreamacro/clash/common/utils"
"github.com/Dreamacro/clash/component/dialer"
)
@@ -41,10 +40,9 @@ const (
)
const (
DefaultTCPTimeout = 5 * time.Second
DefaultUDPTimeout = DefaultTCPTimeout
DefaultTLSTimeout = DefaultTCPTimeout
DefaultMaxHealthCheckUrlNum = 16
DefaultTCPTimeout = 5 * time.Second
DefaultUDPTimeout = DefaultTCPTimeout
DefaultTLSTimeout = DefaultTCPTimeout
)
var ErrNotSupport = errors.New("no support")
@@ -134,7 +132,7 @@ type ProxyAdapter interface {
}
type Group interface {
URLTest(ctx context.Context, url string, expectedStatus utils.IntRanges[uint16]) (mp map[string]uint16, err error)
URLTest(ctx context.Context, url string) (mp map[string]uint16, err error)
GetProxies(touch bool) []Proxy
Touch()
}
@@ -144,23 +142,12 @@ type DelayHistory struct {
Delay uint16 `json:"delay"`
}
type DelayHistoryStoreType int
const (
OriginalHistory DelayHistoryStoreType = iota
ExtraHistory
DropHistory
)
type Proxy interface {
ProxyAdapter
Alive() bool
AliveForTestUrl(url string) bool
DelayHistory() []DelayHistory
ExtraDelayHistory() map[string][]DelayHistory
LastDelay() uint16
LastDelayForTestUrl(url string) uint16
URLTest(ctx context.Context, url string, expectedStatus utils.IntRanges[uint16], store DelayHistoryStoreType) (uint16, error)
URLTest(ctx context.Context, url string) (uint16, error)
// Deprecated: use DialContext instead.
Dial(metadata *Metadata) (Conn, error)
@@ -230,7 +217,7 @@ type UDPPacket interface {
// - variable source IP/Port is important to STUN
// - if addr is not provided, WriteBack will write out UDP packet with SourceIP/Port equals to original Target,
// this is important when using Fake-IP.
WriteBack
WriteBack(b []byte, addr net.Addr) (n int, err error)
// Drop call after packet is used, could recycle buffer in this function.
Drop()
@@ -249,19 +236,10 @@ type PacketAdapter interface {
Metadata() *Metadata
}
type WriteBack interface {
WriteBack(b []byte, addr net.Addr) (n int, err error)
}
type WriteBackProxy interface {
WriteBack
UpdateWriteBack(wb WriteBack)
}
type NatTable interface {
Set(key string, e PacketConn, w WriteBackProxy)
Set(key string, e PacketConn)
Get(key string) (PacketConn, WriteBackProxy)
Get(key string) PacketConn
GetOrCreateLock(key string) (*sync.Cond, bool)

View File

@@ -128,10 +128,10 @@ type Metadata struct {
Type Type `json:"type"`
SrcIP netip.Addr `json:"sourceIP"`
DstIP netip.Addr `json:"destinationIP"`
SrcPort uint16 `json:"sourcePort,string"` // `,string` is used to compatible with old version json output
DstPort uint16 `json:"destinationPort,string"` // `,string` is used to compatible with old version json output
SrcPort string `json:"sourcePort"`
DstPort string `json:"destinationPort"`
InIP netip.Addr `json:"inboundIP"`
InPort uint16 `json:"inboundPort,string"` // `,string` is used to compatible with old version json output
InPort string `json:"inboundPort"`
InName string `json:"inboundName"`
InUser string `json:"inboundUser"`
Host string `json:"host"`
@@ -147,11 +147,11 @@ type Metadata struct {
}
func (m *Metadata) RemoteAddress() string {
return net.JoinHostPort(m.String(), strconv.FormatUint(uint64(m.DstPort), 10))
return net.JoinHostPort(m.String(), m.DstPort)
}
func (m *Metadata) SourceAddress() string {
return net.JoinHostPort(m.SrcIP.String(), strconv.FormatUint(uint64(m.SrcPort), 10))
return net.JoinHostPort(m.SrcIP.String(), m.SrcPort)
}
func (m *Metadata) SourceDetail() string {
@@ -171,10 +171,6 @@ func (m *Metadata) SourceDetail() string {
}
}
func (m *Metadata) SourceValid() bool {
return m.SrcPort != 0 && m.SrcIP.IsValid()
}
func (m *Metadata) AddrType() int {
switch true {
case m.Host != "" || !m.DstIP.IsValid():
@@ -211,7 +207,8 @@ func (m *Metadata) Pure() *Metadata {
}
func (m *Metadata) AddrPort() netip.AddrPort {
return netip.AddrPortFrom(m.DstIP.Unmap(), m.DstPort)
port, _ := strconv.ParseUint(m.DstPort, 10, 16)
return netip.AddrPortFrom(m.DstIP.Unmap(), uint16(port))
}
func (m *Metadata) UDPAddr() *net.UDPAddr {
@@ -241,11 +238,6 @@ func (m *Metadata) SetRemoteAddress(rawAddress string) error {
return err
}
var uint16Port uint16
if port, err := strconv.ParseUint(port, 10, 16); err == nil {
uint16Port = uint16(port)
}
if ip, err := netip.ParseAddr(host); err != nil {
m.Host = host
m.DstIP = netip.Addr{}
@@ -253,7 +245,7 @@ func (m *Metadata) SetRemoteAddress(rawAddress string) error {
m.Host = ""
m.DstIP = ip.Unmap()
}
m.DstPort = uint16Port
m.DstPort = port
return nil
}

View File

@@ -1,12 +1,9 @@
package constant
import (
"crypto/md5"
"encoding/hex"
"os"
P "path"
"path/filepath"
"strconv"
"strings"
)
@@ -23,15 +20,14 @@ var Path = func() *path {
if err != nil {
homeDir, _ = os.Getwd()
}
allowUnsafePath, _ := strconv.ParseBool(os.Getenv("SKIP_SAFE_PATH_CHECK"))
homeDir = P.Join(homeDir, ".config", Name)
return &path{homeDir: homeDir, configFile: "config.yaml", allowUnsafePath: allowUnsafePath}
return &path{homeDir: homeDir, configFile: "config.yaml"}
}()
type path struct {
homeDir string
configFile string
allowUnsafePath bool
homeDir string
configFile string
}
// SetHomeDir is used to set the configuration path
@@ -60,27 +56,6 @@ func (p *path) Resolve(path string) string {
return path
}
// IsSafePath return true if path is a subpath of homedir
func (p *path) IsSafePath(path string) bool {
if p.allowUnsafePath {
return true
}
homedir := p.HomeDir()
path = p.Resolve(path)
rel, err := filepath.Rel(homedir, path)
if err != nil {
return false
}
return !strings.Contains(rel, "..")
}
func (p *path) GetPathByHash(prefix, name string) string {
hash := md5.Sum([]byte(name))
filename := hex.EncodeToString(hash[:])
return filepath.Join(p.HomeDir(), prefix, filename)
}
func (p *path) MMDB() string {
files, err := os.ReadDir(p.homeDir)
if err != nil {
@@ -91,15 +66,13 @@ func (p *path) MMDB() string {
// 目录则直接跳过
continue
} else {
if strings.EqualFold(fi.Name(), "Country.mmdb") ||
strings.EqualFold(fi.Name(), "geoip.db") ||
strings.EqualFold(fi.Name(), "geoip.metadb") {
if strings.EqualFold(fi.Name(), "Country.mmdb") {
GeoipName = fi.Name()
return P.Join(p.homeDir, fi.Name())
}
}
}
return P.Join(p.homeDir, "geoip.metadb")
return P.Join(p.homeDir, "Country.mmdb")
}
func (p *path) OldCache() string {

View File

@@ -1,7 +1,6 @@
package provider
import (
"github.com/Dreamacro/clash/common/utils"
"github.com/Dreamacro/clash/constant"
)
@@ -72,7 +71,6 @@ type ProxyProvider interface {
Touch()
HealthCheck()
Version() uint32
RegisterHealthCheckTask(url string, expectedStatus utils.IntRanges[uint16], filter string, interval uint)
}
// RuleProvider interface

View File

@@ -10,14 +10,12 @@ var StackTypeMapping = map[string]TUNStack{
strings.ToLower(TunGvisor.String()): TunGvisor,
strings.ToLower(TunSystem.String()): TunSystem,
strings.ToLower(TunLWIP.String()): TunLWIP,
strings.ToLower(TunMixed.String()): TunMixed,
}
const (
TunGvisor TUNStack = iota
TunSystem
TunLWIP
TunMixed
)
type TUNStack int
@@ -66,8 +64,6 @@ func (e TUNStack) String() string {
return "System"
case TunLWIP:
return "LWIP"
case TunMixed:
return "Mixed"
default:
return "unknown"
}

View File

@@ -59,8 +59,7 @@ func (d *dhcpClient) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg,
return nil, err
}
msg, _, err = batchExchange(ctx, clients, m)
return
return batchExchange(ctx, clients, m)
}
func (d *dhcpClient) resolve(ctx context.Context) ([]dnsClient, error) {

View File

@@ -543,17 +543,7 @@ func (doh *dnsOverHTTPS) dialQuic(ctx context.Context, addr string, tlsCfg *tls.
if err != nil {
return nil, err
}
transport := quic.Transport{Conn: conn}
transport.SetCreatedConn(true) // auto close conn
transport.SetSingleUse(true) // auto close transport
tlsCfg = tlsCfg.Clone()
if host, _, err := net.SplitHostPort(doh.url.Host); err == nil {
tlsCfg.ServerName = host
} else {
// It's ok if net.SplitHostPort returns an error - it could be a hostname/IP address without a port.
tlsCfg.ServerName = doh.url.Host
}
return transport.DialEarly(ctx, &udpAddr, tlsCfg, cfg)
return quic.DialEarlyContext(ctx, conn, &udpAddr, doh.url.Host, tlsCfg, cfg)
}
// probeH3 runs a test to check whether QUIC is faster than TLS for this

View File

@@ -302,6 +302,14 @@ func (doq *dnsOverQUIC) openStream(ctx context.Context, conn quic.Connection) (q
// openConnection opens a new QUIC connection.
func (doq *dnsOverQUIC) openConnection(ctx context.Context) (conn quic.Connection, err error) {
tlsConfig := tlsC.GetGlobalTLSConfig(
&tls.Config{
InsecureSkipVerify: false,
NextProtos: []string{
NextProtoDQ,
},
SessionTicketsDisabled: false,
})
// we're using bootstrapped address instead of what's passed to the function
// it does not create an actual connection, but it helps us determine
// what IP is actually reachable (when there're v4/v6 addresses).
@@ -330,20 +338,7 @@ func (doq *dnsOverQUIC) openConnection(ctx context.Context) (conn quic.Connectio
return nil, err
}
tlsConfig := tlsC.GetGlobalTLSConfig(
&tls.Config{
ServerName: host,
InsecureSkipVerify: false,
NextProtos: []string{
NextProtoDQ,
},
SessionTicketsDisabled: false,
})
transport := quic.Transport{Conn: udp}
transport.SetCreatedConn(true) // auto close conn
transport.SetSingleUse(true) // auto close transport
conn, err = transport.Dial(ctx, &udpAddr, tlsConfig, doq.getQUICConfig())
conn, err = quic.DialContext(ctx, udp, &udpAddr, host, tlsConfig, doq.getQUICConfig())
if err != nil {
return nil, fmt.Errorf("opening quic connection to %s: %w", doq.addr, err)
}

View File

@@ -109,7 +109,7 @@ func NewEnhancer(cfg Config) *ResolverEnhancer {
if cfg.EnhancedMode != C.DNSNormal {
fakePool = cfg.Pool
mapping = cache.New(cache.WithSize[netip.Addr, string](4096))
mapping = cache.New(cache.WithSize[netip.Addr, string](4096), cache.WithStale[netip.Addr, string](true))
}
return &ResolverEnhancer{

View File

@@ -2,7 +2,6 @@ package dns
import (
"net/netip"
"strings"
"github.com/Dreamacro/clash/component/geodata"
"github.com/Dreamacro/clash/component/geodata/router"
@@ -10,6 +9,7 @@ import (
"github.com/Dreamacro/clash/component/trie"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/log"
"strings"
)
type fallbackIPFilter interface {
@@ -24,13 +24,8 @@ var geoIPMatcher *router.GeoIPMatcher
func (gf *geoipFilter) Match(ip netip.Addr) bool {
if !C.GeodataMode {
codes := mmdb.Instance().LookupCode(ip.AsSlice())
for _, code := range codes {
if !strings.EqualFold(code, gf.code) && !ip.IsPrivate() {
return true
}
}
return false
record, _ := mmdb.Instance().Country(ip.AsSlice())
return !strings.EqualFold(record.Country.IsoCode, gf.code) && !ip.IsPrivate()
}
if geoIPMatcher == nil {

View File

@@ -129,10 +129,6 @@ func withMapping(mapping *cache.LruCache[netip.Addr, string]) middleware {
continue
}
if ttl < 1 {
ttl = 1
}
mapping.SetWithExpire(ip, host, time.Now().Add(time.Second*time.Duration(ttl)))
}

View File

@@ -1,54 +0,0 @@
package dns
import (
"context"
"fmt"
D "github.com/miekg/dns"
)
func newRCodeClient(addr string) rcodeClient {
var rcode int
switch addr {
case "success":
rcode = D.RcodeSuccess
case "format_error":
rcode = D.RcodeFormatError
case "server_failure":
rcode = D.RcodeServerFailure
case "name_error":
rcode = D.RcodeNameError
case "not_implemented":
rcode = D.RcodeNotImplemented
case "refused":
rcode = D.RcodeRefused
default:
panic(fmt.Errorf("unsupported RCode type: %s", addr))
}
return rcodeClient{
rcode: rcode,
addr: "rcode://" + addr,
}
}
type rcodeClient struct {
rcode int
addr string
}
var _ dnsClient = rcodeClient{}
func (r rcodeClient) Exchange(m *D.Msg) (*D.Msg, error) {
m.Response = true
m.Rcode = r.rcode
return m, nil
}
func (r rcodeClient) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error) {
return r.Exchange(m)
}
func (r rcodeClient) Address() string {
return r.addr
}

View File

@@ -165,8 +165,7 @@ func (r *Resolver) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, e
setMsgTTL(msg, uint32(1)) // Continue fetch
continueFetch = true
} else {
// updating TTL by subtracting common delta time from each DNS record
updateMsgTTL(msg, uint32(time.Until(expireTime).Seconds()))
setMsgTTL(msg, uint32(time.Until(expireTime).Seconds()))
}
return
}
@@ -182,7 +181,6 @@ func (r *Resolver) exchangeWithoutCache(ctx context.Context, m *D.Msg) (msg *D.M
fn := func() (result any, err error) {
ctx, cancel := context.WithTimeout(context.Background(), resolver.DefaultDNSTimeout) // reset timeout in singleflight
defer cancel()
cache := false
defer func() {
if err != nil {
@@ -193,9 +191,7 @@ func (r *Resolver) exchangeWithoutCache(ctx context.Context, m *D.Msg) (msg *D.M
msg := result.(*D.Msg)
if cache {
putMsgToCache(r.lruCache, q.String(), msg)
}
putMsgToCache(r.lruCache, q.String(), msg)
}()
isIPReq := isIPRequest(q)
@@ -204,11 +200,9 @@ func (r *Resolver) exchangeWithoutCache(ctx context.Context, m *D.Msg) (msg *D.M
}
if matched := r.matchPolicy(m); len(matched) != 0 {
result, cache, err = r.batchExchange(ctx, matched, m)
return
return r.batchExchange(ctx, matched, m)
}
result, cache, err = r.batchExchange(ctx, r.main, m)
return
return r.batchExchange(ctx, r.main, m)
}
ch := r.group.DoChan(q.String(), fn)
@@ -249,7 +243,7 @@ func (r *Resolver) exchangeWithoutCache(ctx context.Context, m *D.Msg) (msg *D.M
return
}
func (r *Resolver) batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.Msg, cache bool, err error) {
func (r *Resolver) batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.Msg, err error) {
ctx, cancel := context.WithTimeout(ctx, resolver.DefaultDNSTimeout)
defer cancel()
@@ -376,7 +370,7 @@ func (r *Resolver) lookupIP(ctx context.Context, host string, dnsType uint16) (i
func (r *Resolver) asyncExchange(ctx context.Context, client []dnsClient, msg *D.Msg) <-chan *result {
ch := make(chan *result, 1)
go func() {
res, _, err := r.batchExchange(ctx, client, msg)
res, err := r.batchExchange(ctx, client, msg)
ch <- &result{Msg: res, Error: err}
}()
return ch

View File

@@ -3,41 +3,25 @@
package dns
import (
"bufio"
"fmt"
"net/netip"
"os"
"strings"
"regexp"
)
const resolvConf = "/etc/resolv.conf"
var (
// nameserver xxx.xxx.xxx.xxx
nameserverPattern = regexp.MustCompile(`nameserver\s+(?P<ip>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})`)
)
func dnsReadConfig() (servers []string, err error) {
file, err := os.Open(resolvConf)
content, err := os.ReadFile("/etc/resolv.conf")
if err != nil {
err = fmt.Errorf("failed to read %s: %w", resolvConf, err)
err = fmt.Errorf("failed to read /etc/resolv.conf: %w", err)
return
}
defer func() { _ = file.Close() }()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
if len(line) > 0 && (line[0] == ';' || line[0] == '#') {
// comment.
continue
}
f := strings.Fields(line)
if len(f) < 1 {
continue
}
switch f[0] {
case "nameserver": // add one name server
if len(f) > 1 {
if addr, err := netip.ParseAddr(f[1]); err == nil {
servers = append(servers, addr.String())
}
}
}
for _, line := range nameserverPattern.FindAllStringSubmatch(string(content), -1) {
addr := line[1]
servers = append(servers, addr)
}
return
}

View File

@@ -7,7 +7,6 @@ import (
"fmt"
"net"
"net/netip"
"strconv"
"strings"
"time"
@@ -22,29 +21,12 @@ import (
"github.com/Dreamacro/clash/tunnel"
D "github.com/miekg/dns"
"github.com/samber/lo"
)
const (
MaxMsgSize = 65535
)
func minimalTTL(records []D.RR) uint32 {
return lo.MinBy(records, func(r1 D.RR, r2 D.RR) bool {
return r1.Header().Ttl < r2.Header().Ttl
}).Header().Ttl
}
func updateTTL(records []D.RR, ttl uint32) {
if len(records) == 0 {
return
}
delta := minimalTTL(records) - ttl
for i := range records {
records[i].Header().Ttl = lo.Clamp(records[i].Header().Ttl-delta, 1, records[i].Header().Ttl)
}
}
func putMsgToCache(c *cache.LruCache[string, *D.Msg], key string, msg *D.Msg) {
// skip dns cache for acme challenge
if len(msg.Question) != 0 {
@@ -56,11 +38,11 @@ func putMsgToCache(c *cache.LruCache[string, *D.Msg], key string, msg *D.Msg) {
var ttl uint32
switch {
case len(msg.Answer) != 0:
ttl = minimalTTL(msg.Answer)
ttl = msg.Answer[0].Header().Ttl
case len(msg.Ns) != 0:
ttl = minimalTTL(msg.Ns)
ttl = msg.Ns[0].Header().Ttl
case len(msg.Extra) != 0:
ttl = minimalTTL(msg.Extra)
ttl = msg.Extra[0].Header().Ttl
default:
log.Debugln("[DNS] response msg empty: %#v", msg)
return
@@ -83,18 +65,12 @@ func setMsgTTL(msg *D.Msg, ttl uint32) {
}
}
func updateMsgTTL(msg *D.Msg, ttl uint32) {
updateTTL(msg.Answer, ttl)
updateTTL(msg.Ns, ttl)
updateTTL(msg.Extra, ttl)
}
func isIPRequest(q D.Question) bool {
return q.Qclass == D.ClassINET && (q.Qtype == D.TypeA || q.Qtype == D.TypeAAAA || q.Qtype == D.TypeCNAME)
}
func transform(servers []NameServer, resolver *Resolver) []dnsClient {
ret := make([]dnsClient, 0, len(servers))
ret := []dnsClient{}
for _, s := range servers {
switch s.Net {
case "https":
@@ -115,9 +91,6 @@ func transform(servers []NameServer, resolver *Resolver) []dnsClient {
}
ret = append(ret, clients...)
continue
case "rcode":
ret = append(ret, newRCodeClient(s.Addr))
continue
case "quic":
if doq, err := newDoQ(resolver, s.Addr, s.ProxyAdapter, s.ProxyName); err == nil {
ret = append(ret, doq)
@@ -194,10 +167,6 @@ func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string,
if err != nil {
return nil, err
}
uintPort, err := strconv.ParseUint(port, 10, 16)
if err != nil {
return nil, err
}
if proxyAdapter == nil {
var ok bool
proxyAdapter, ok = tunnel.Proxies()[proxyName]
@@ -211,7 +180,7 @@ func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string,
metadata := &C.Metadata{
NetWork: C.TCP,
Host: host,
DstPort: uint16(uintPort),
DstPort: port,
}
if proxyAdapter != nil {
if proxyAdapter.IsL3Protocol(metadata) { // L3 proxy should resolve domain before to avoid loopback
@@ -236,7 +205,7 @@ func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string,
NetWork: C.UDP,
Host: "",
DstIP: dstIP,
DstPort: uint16(uintPort),
DstPort: port,
}
if proxyAdapter == nil {
return dialer.DialContext(ctx, network, addr, opts...)
@@ -262,10 +231,6 @@ func listenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName st
if err != nil {
return nil, err
}
uintPort, err := strconv.ParseUint(port, 10, 16)
if err != nil {
return nil, err
}
if proxyAdapter == nil {
var ok bool
proxyAdapter, ok = tunnel.Proxies()[proxyName]
@@ -283,7 +248,7 @@ func listenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName st
NetWork: C.UDP,
Host: "",
DstIP: dstIP,
DstPort: uint16(uintPort),
DstPort: port,
}
if proxyAdapter == nil {
return dialer.ListenPacket(ctx, dialer.ParseNetwork(network, dstIP), "", opts...)
@@ -296,38 +261,32 @@ func listenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName st
return proxyAdapter.ListenPacketContext(ctx, metadata, opts...)
}
func batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.Msg, cache bool, err error) {
cache = true
func batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.Msg, err error) {
fast, ctx := picker.WithTimeout[*D.Msg](ctx, resolver.DefaultDNSTimeout)
defer fast.Close()
domain := msgToDomain(m)
for _, client := range clients {
if _, isRCodeClient := client.(rcodeClient); isRCodeClient {
msg, err = client.Exchange(m)
return msg, false, err
}
client := client // shadow define client to ensure the value captured by the closure will not be changed in the next loop
r := client
fast.Go(func() (*D.Msg, error) {
log.Debugln("[DNS] resolve %s from %s", domain, client.Address())
m, err := client.ExchangeContext(ctx, m)
log.Debugln("[DNS] resolve %s from %s", domain, r.Address())
m, err := r.ExchangeContext(ctx, m)
if err != nil {
return nil, err
} else if cache && (m.Rcode == D.RcodeServerFailure || m.Rcode == D.RcodeRefused) {
// currently, cache indicates whether this msg was from a RCode client,
// so we would ignore RCode errors from RCode clients.
return nil, errors.New("server failure: " + D.RcodeToString[m.Rcode])
} else if m.Rcode == D.RcodeServerFailure || m.Rcode == D.RcodeRefused {
return nil, errors.New("server failure")
}
log.Debugln("[DNS] %s --> %s, from %s", domain, msgToIP(m), client.Address())
log.Debugln("[DNS] %s --> %s, from %s", domain, msgToIP(m), r.Address())
return m, nil
})
}
msg = fast.Wait()
if msg == nil {
err = errors.New("all DNS requests failed")
elm := fast.Wait()
if elm == nil {
err := errors.New("all DNS requests failed")
if fErr := fast.Error(); fErr != nil {
err = fmt.Errorf("%w, first error: %w", err, fErr)
err = fmt.Errorf("%w, first error: %s", err, fErr.Error())
}
return nil, err
}
msg = elm
return
}

View File

@@ -19,9 +19,9 @@ mode: rule
#自定义 geodata url
geox-url:
geoip: "https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geoip.dat"
geosite: "https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geosite.dat"
mmdb: "https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geoip.metadb"
geoip: "https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/geoip.dat"
geosite: "https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/geosite.dat"
mmdb: "https://cdn.jsdelivr.net/gh/Loyalsoldier/geoip@release/Country.mmdb"
log-level: debug # 日志等级 silent/error/warning/info/debug
@@ -64,7 +64,7 @@ hosts:
profile: # 存储 select 选择记录
store-selected: false
# 持久化 fake-ip
store-fake-ip: true
@@ -93,10 +93,10 @@ tun:
#- 1000
# exclude-uid-range: # 排除路由的的用户范围
# - 1000-99999
# Android 用户和应用规则仅在 Android 下被支持
# 并且需要 auto-route
# include-android-user: # 限制被路由的 Android 用户
# - 0
# - 10
@@ -126,9 +126,10 @@ sniffer:
sniff: # TLS 默认如果不配置 ports 默认嗅探 443
TLS:
# ports: [443, 8443]
# 默认嗅探 80
HTTP: # 需要嗅探的端口
ports: [80, 8080-8880]
# 可覆盖 sniffer.override-destination
override-destination: true
@@ -143,7 +144,7 @@ sniffer:
- tls
- http
# 强制对此域名进行嗅探
# 仅对白名单中的端口进行嗅探,默认为 44380
# 已废弃,若 sniffer.sniff 配置则此项无效
port-whitelist:
@@ -151,6 +152,7 @@ sniffer:
- "443"
# - 8000-9999
tunnels: # one line config
- tcp/udp,127.0.0.1:6553,114.114.114.114:53,proxy
- tcp,127.0.0.1:6666,rds.mysql.com:3306,vpn
@@ -160,6 +162,7 @@ tunnels: # one line config
target: target.com
proxy: proxy
# DNS配置
dns:
enable: false # 关闭将使用系统 DNS
@@ -174,18 +177,18 @@ dns:
- 8.8.8.8
- tls://1.12.12.12:853
- tls://223.5.5.5:853
- system # append DNS server from system configuration. If not found, it would print an error log and skip.
- system # append DNS server from system configuration. If not found, it would print an error log and skip.
enhanced-mode: fake-ip # or redir-host
fake-ip-range: 198.18.0.1/16 # fake-ip 池设置
# use-hosts: true # 查询 hosts
# 配置不使用fake-ip的域名
# fake-ip-filter:
# - '*.lan'
# - localhost.ptlogin2.qq.com
# DNS主要域名配置
# 支持 UDPTCPDoTDoHDoQ
# 这部分为主要 DNS 配置,影响所有直连,确保使用对大陆解析精准的 DNS
@@ -199,20 +202,20 @@ dns:
- dhcp://en0 # dns from dhcp
- quic://dns.adguard.com:784 # DNS over QUIC
# - '8.8.8.8#en0' # 兼容指定DNS出口网卡
# 当配置 fallback 时,会查询 nameserver 中返回的 IP 是否为 CN非必要配置
# 当不是 CN则使用 fallback 中的 DNS 查询结果
# 确保配置 fallback 时能够正常查询
# fallback:
# - tcp://1.1.1.1
# - 'tcp://1.1.1.1#ProxyGroupName' # 指定 DNS 过代理查询ProxyGroupName 为策略组名或节点名,过代理配置优先于配置出口网卡,当找不到策略组或节点名则设置为出口网卡
# 专用于节点域名解析的 DNS 服务器,非必要配置项
# 配置服务器若查询失败将使用 nameserver非并发查询
# proxy-server-nameserver:
# - https://dns.google/dns-query
# - tls://one.one.one.one
# 配置 fallback 使用条件
# fallback-filter:
# geoip: true # 配置是否使用 geoip
@@ -227,7 +230,7 @@ dns:
# - '+.google.com'
# - '+.facebook.com'
# - '+.youtube.com'
# 配置查询域名使用的 DNS 服务器
nameserver-policy:
# 'www.baidu.com': '114.114.114.114'
@@ -235,10 +238,9 @@ dns:
"geosite:cn,private,apple":
- https://doh.pub/dns-query
- https://dns.alidns.com/dns-query
"geosite:category-ads-all": rcode://success
"www.baidu.com,+.google.cn": [223.5.5.5, https://dns.alidns.com/dns-query]
## globaldns 为 rule-providers 中的名为 global 和 dns 规则订阅,
## 且 behavior 必须为 domain/classical当为 classical 时仅会生效域名类规则
## 且 behavior 必须为 domain/classical当为 classical 时仅会生效域名类规则
# "rule-set:global,dns": 8.8.8.8
proxies: # socks5
@@ -253,7 +255,7 @@ proxies: # socks5
# skip-cert-verify: true
# udp: true
# ip-version: ipv6
# http
- name: "http"
type: http
@@ -266,7 +268,7 @@ proxies: # socks5
# sni: custom.com
# fingerprint: xxxx # 同 experimental.fingerprints 使用 sha256 指纹,配置协议独立的指纹,将忽略 experimental.fingerprints
# ip-version: dual
# Snell
# Beware that there's currently no UDP support yet
- name: "snell"
@@ -278,7 +280,7 @@ proxies: # socks5
# obfs-opts:
# mode: http # or tls
# host: bing.com
# Shadowsocks
# cipher支持:
# aes-128-gcm aes-192-gcm aes-256-gcm
@@ -310,7 +312,7 @@ proxies: # socks5
# padding: false # Enable padding. Requires sing-box server version 1.3-beta9 or later.
# statistic: false # 控制是否将底层连接显示在面板中,方便打断底层连接
# only-tcp: false # 如果设置为true, smux的设置将不会对udp生效udp连接会直接走底层协议
- name: "ss2"
type: ss
server: server
@@ -321,7 +323,7 @@ proxies: # socks5
plugin-opts:
mode: tls # or http
# host: bing.com
- name: "ss3"
type: ss
server: server
@@ -341,7 +343,7 @@ proxies: # socks5
# mux: true
# headers:
# custom: value
- name: "ss4-shadow-tls"
type: ss
server: server
@@ -361,22 +363,20 @@ proxies: # socks5
port: 443
cipher: chacha20-ietf-poly1305
password: [YOUR_SS_PASSWORD]
client-fingerprint:
chrome # One of: chrome, ios, firefox or safari
# 可以是chrome, ios, firefox, safari中的一个
client-fingerprint: chrome # One of: chrome, ios, firefox or safari
# 可以是chrome, ios, firefox, safari中的一个
plugin: restls
plugin-opts:
host:
"www.microsoft.com" # Must be a TLS 1.3 server
# 应当是一个TLS 1.3 服务器
password: [YOUR_RESTLS_PASSWORD]
version-hint: "tls13"
# Control your post-handshake traffic through restls-script
# Hide proxy behaviors like "tls in tls".
# see https://github.com/3andne/restls/blob/main/Restls-Script:%20Hide%20Your%20Proxy%20Traffic%20Behavior.md
# 用restls剧本来控制握手后的行为隐藏"tls in tls"等特征
# 详情https://github.com/3andne/restls/blob/main/Restls-Script:%20%E9%9A%90%E8%97%8F%E4%BD%A0%E7%9A%84%E4%BB%A3%E7%90%86%E8%A1%8C%E4%B8%BA.md
restls-script: "300?100<1,400~100,350~100,600~100,300~200,300~100"
host: "www.microsoft.com" # Must be a TLS 1.3 server
# 应当是一个TLS 1.3 服务器
password: [YOUR_RESTLS_PASSWORD]
version-hint: "tls13"
# Control your post-handshake traffic through restls-script
# Hide proxy behaviors like "tls in tls".
# see https://github.com/3andne/restls/blob/main/Restls-Script:%20Hide%20Your%20Proxy%20Traffic%20Behavior.md
# 用restls剧本来控制握手后的行为隐藏"tls in tls"等特征
# 详情https://github.com/3andne/restls/blob/main/Restls-Script:%20%E9%9A%90%E8%97%8F%E4%BD%A0%E7%9A%84%E4%BB%A3%E7%90%86%E8%A1%8C%E4%B8%BA.md
restls-script: "300?100<1,400~100,350~100,600~100,300~200,300~100"
- name: "ss-restls-tls12"
type: ss
@@ -384,18 +384,16 @@ proxies: # socks5
port: 443
cipher: chacha20-ietf-poly1305
password: [YOUR_SS_PASSWORD]
client-fingerprint:
chrome # One of: chrome, ios, firefox or safari
# 可以是chrome, ios, firefox, safari中的一个
client-fingerprint: chrome # One of: chrome, ios, firefox or safari
# 可以是chrome, ios, firefox, safari中的一个
plugin: restls
plugin-opts:
host:
"vscode.dev" # Must be a TLS 1.2 server
# 应当是一个TLS 1.2 服务器
password: [YOUR_RESTLS_PASSWORD]
version-hint: "tls12"
restls-script: "1000?100<1,500~100,350~100,600~100,400~200"
host: "vscode.dev" # Must be a TLS 1.2 server
# 应当是一个TLS 1.2 服务器
password: [YOUR_RESTLS_PASSWORD]
version-hint: "tls12"
restls-script: "1000?100<1,500~100,350~100,600~100,400~200"
# vmess
# cipher支持 auto/aes-128-gcm/chacha20-poly1305/none
- name: "vmess"
@@ -418,7 +416,7 @@ proxies: # socks5
# Host: v2ray.com
# max-early-data: 2048
# early-data-header-name: Sec-WebSocket-Protocol
- name: "vmess-h2"
type: vmess
server: server
@@ -434,7 +432,7 @@ proxies: # socks5
- http.example.com
- http-alt.example.com
path: /
- name: "vmess-http"
type: vmess
server: server
@@ -453,7 +451,7 @@ proxies: # socks5
# Connection:
# - keep-alive
# ip-version: ipv4 # 设置使用 IP 类型偏好可选ipv4ipv6dual默认值dual
- name: vmess-grpc
server: server
port: 443
@@ -469,7 +467,7 @@ proxies: # socks5
grpc-opts:
grpc-service-name: "example"
# ip-version: ipv4
# vless
- name: "vless-tcp"
type: vless
@@ -482,7 +480,7 @@ proxies: # socks5
# skip-cert-verify: true
# fingerprint: xxxx
# client-fingerprint: random # Available: "chrome","firefox","safari","random","none"
- name: "vless-vision"
type: vless
server: server
@@ -495,7 +493,7 @@ proxies: # socks5
client-fingerprint: chrome
# fingerprint: xxxx
# skip-cert-verify: true
- name: "vless-reality-vision"
type: vless
server: server
@@ -510,7 +508,7 @@ proxies: # socks5
public-key: xxx
short-id: xxx # optional
client-fingerprint: chrome # cannot be empty
- name: "vless-reality-grpc"
type: vless
server: server
@@ -528,7 +526,7 @@ proxies: # socks5
reality-opts:
public-key: CrrQSjAG_YkHLwvM2M-7XkKJilgL5upBKCp0od0tLhE
short-id: 10f897e26c4b9478
- name: "vless-ws"
type: vless
server: server
@@ -545,7 +543,7 @@ proxies: # socks5
path: "/"
headers:
Host: example.com
# Trojan
- name: "trojan"
type: trojan
@@ -560,7 +558,7 @@ proxies: # socks5
# - h2
# - http/1.1
# skip-cert-verify: true
- name: trojan-grpc
server: server
port: 443
@@ -573,7 +571,7 @@ proxies: # socks5
udp: true
grpc-opts:
grpc-service-name: "example"
- name: trojan-ws
server: server
port: 443
@@ -588,7 +586,7 @@ proxies: # socks5
# path: /path
# headers:
# Host: example.com
- name: "trojan-xtls"
type: trojan
server: server
@@ -600,7 +598,7 @@ proxies: # socks5
# sni: example.com # aka server name
# skip-cert-verify: true
# fingerprint: xxxx
#hysteria
- name: "hysteria"
type: hysteria
@@ -627,7 +625,7 @@ proxies: # socks5
# disable_mtu_discovery: false
# fingerprint: xxxx
# fast-open: true # 支持 TCP 快速打开,默认为 false
# wireguard
- name: "wg"
type: wireguard
@@ -656,17 +654,13 @@ proxies: # socks5
# # pre-shared-key: 31aIhAPwktDGpH4JDhA8GNvjFXEf/a6+UaQRyOAiyfM=
# allowed_ips: ['0.0.0.0/0']
# reserved: [209,98,59]
# tuic
- name: tuic
server: www.example.com
port: 10443
type: tuic
# tuicV4必须填写token 不可同时填写uuid和password
token: TOKEN
# tuicV5必须填写uuid和password不可同时填写token
uuid: 00000000-0000-0000-0000-000000000001
password: PASSWORD_1
# ip: 127.0.0.1 # for overwriting the DNS lookup result of the server address set in option 'server'
# heartbeat-interval: 10000
# alpn: [h3]
@@ -675,18 +669,12 @@ proxies: # socks5
request-timeout: 8000
udp-relay-mode: native # Available: "native", "quic". Default: "native"
# congestion-controller: bbr # Available: "cubic", "new_reno", "bbr". Default: "cubic"
# cwnd: 10 # default: 32
# max-udp-relay-packet-size: 1500
# fast-open: true
# skip-cert-verify: true
# max-open-streams: 20 # default 100, too many open streams may hurt performance
# sni: example.com
#
# meta和sing-box私有扩展将ss-uot用于udp中继开启此选项后udp-relay-mode将失效
# 警告与原版tuic不兼容
# udp-over-stream: false
# udp-over-stream-version: 1
# ShadowsocksR
# The supported ciphers (encryption methods): all stream ciphers in ss
# The supported obfses:
@@ -718,7 +706,7 @@ proxy-groups:
- vmess
- ss1
- ss2
# url-test 将按照 url 测试结果使用延迟最低节点
- name: "auto"
type: url-test
@@ -730,7 +718,7 @@ proxy-groups:
# lazy: true
url: "https://cp.cloudflare.com/generate_204"
interval: 300
# fallback 将按照 url 测试结果按照节点顺序选择
- name: "fallback-auto"
type: fallback
@@ -740,7 +728,7 @@ proxy-groups:
- vmess1
url: "https://cp.cloudflare.com/generate_204"
interval: 300
# load-balance 将按照算法随机选择节点
- name: "load-balance"
type: load-balance
@@ -751,7 +739,7 @@ proxy-groups:
url: "https://cp.cloudflare.com/generate_204"
interval: 300
# strategy: consistent-hashing # 可选 round-robin 和 sticky-sessions
# select 用户自行选择节点
- name: Proxy
type: select
@@ -761,7 +749,7 @@ proxy-groups:
- ss2
- vmess1
- auto
# 配置指定 interface-name 和 fwmark 的 DIRECT
- name: en1
type: select
@@ -769,7 +757,7 @@ proxy-groups:
routing-mark: 6667
proxies:
- DIRECT
- name: UseProvider
type: select
filter: "HK|TW" # 正则表达式,过滤 provider1 中节点名包含 HK 或 TW
@@ -782,10 +770,10 @@ proxy-groups:
# Clash 格式的节点或支持 *ray 的分享格式
proxy-providers:
provider1:
type: http # http 的 path 可空置,默认储存路径为 homedir的proxies文件夹,文件名为url的md5
type: http
url: "url"
interval: 3600
path: ./provider1.yaml # 默认只允许存储在 clash 的 Home Dir如果想存储到任意位置添加环境变量 SKIP_SAFE_PATH_CHECK=1
path: ./provider1.yaml
health-check:
enable: true
interval: 600
@@ -802,8 +790,8 @@ rule-providers:
rule1:
behavior: classical # domain ipcidr
interval: 259200
path: /path/to/save/file.yaml # 默认只允许存储在 clash 的 Home Dir如果想存储到任意位置添加环境变量 SKIP_SAFE_PATH_CHECK=1
type: http # http 的 path 可空置,默认储存路径为 homedir的rules文件夹,文件名为url的md5
path: /path/to/save/file.yaml
type: http
url: "url"
rule2:
behavior: classical
@@ -853,14 +841,14 @@ listeners:
# rule: sub-rule-name1 # 默认使用 rules如果未找到 sub-rule 则直接使用 rules
# proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理
# udp: false # 默认 true
- name: http-in-1
type: http
port: 10809
listen: 0.0.0.0
# rule: sub-rule-name1 # 默认使用 rules如果未找到 sub-rule 则直接使用 rules
# proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时这里的proxy名称必须合法否则会出错)
- name: mixed-in-1
type: mixed # HTTP(S) 和 SOCKS 代理混合
port: 10810
@@ -868,14 +856,14 @@ listeners:
# rule: sub-rule-name1 # 默认使用 rules如果未找到 sub-rule 则直接使用 rules
# proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时这里的proxy名称必须合法否则会出错)
# udp: false # 默认 true
- name: reidr-in-1
type: redir
port: 10811
listen: 0.0.0.0
# rule: sub-rule-name1 # 默认使用 rules如果未找到 sub-rule 则直接使用 rules
# proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时这里的proxy名称必须合法否则会出错)
- name: tproxy-in-1
type: tproxy
port: 10812
@@ -883,7 +871,7 @@ listeners:
# rule: sub-rule-name1 # 默认使用 rules如果未找到 sub-rule 则直接使用 rules
# proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时这里的proxy名称必须合法否则会出错)
# udp: false # 默认 true
- name: shadowsocks-in-1
type: shadowsocks
port: 10813
@@ -892,7 +880,7 @@ listeners:
# proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时这里的proxy名称必须合法否则会出错)
password: vlmpIPSyHH6f4S8WVPdRIHIlzmB+GIRfoH3aNJ/t9Gg=
cipher: 2022-blake3-aes-256-gcm
- name: vmess-in-1
type: vmess
port: 10814
@@ -903,18 +891,15 @@ listeners:
- username: 1
uuid: 9d0cb9d0-964f-4ef6-897d-6c6b3ccf9e68
alterId: 1
- name: tuic-in-1
type: tuic
port: 10815
listen: 0.0.0.0
# rule: sub-rule-name1 # 默认使用 rules如果未找到 sub-rule 则直接使用 rules
# proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时这里的proxy名称必须合法否则会出错)
# token: # tuicV4填写可以同时填写users
# - TOKEN
# users: # tuicV5填写可以同时填写token
# 00000000-0000-0000-0000-000000000000: PASSWORD_0
# 00000000-0000-0000-0000-000000000001: PASSWORD_1
# token:
# - TOKEN
# certificate: ./server.crt
# private-key: ./server.key
# congestion-controller: bbr
@@ -923,7 +908,7 @@ listeners:
# alpn:
# - h3
# max-udp-relay-packet-size: 1500
- name: tunnel-in-1
type: tunnel
port: 10816
@@ -932,7 +917,7 @@ listeners:
# proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时这里的proxy名称必须合法否则会出错)
network: [tcp, udp]
target: target.com
- name: tun-in-1
type: tun
# rule: sub-rule-name1 # 默认使用 rules如果未找到 sub-rule 则直接使用 rules
@@ -963,10 +948,10 @@ listeners:
# - 1000
# exclude_uid_range: # 排除路由的的用户范围
# - 1000-99999
# Android 用户和应用规则仅在 Android 下被支持
# 并且需要 auto_route
# include_android_user: # 限制被路由的 Android 用户
# - 0
# - 10
@@ -974,6 +959,7 @@ listeners:
# - com.android.chrome
# exclude_package: # 排除被路由的 Android 应用包名
# - com.android.captiveportallogin
# 入口配置与 Listener 等价,传入流量将和 socks,mixed 等入口一样按照 mode 所指定的方式进行匹配处理
# shadowsocks,vmess 入口配置传入流量将和socks,mixed等入口一样按照mode所指定的方式进行匹配处理
# ss-config: ss://2022-blake3-aes-256-gcm:vlmpIPSyHH6f4S8WVPdRIHIlzmB+GIRfoH3aNJ/t9Gg=@:23456
@@ -983,11 +969,8 @@ listeners:
# tuic-server:
# enable: true
# listen: 127.0.0.1:10443
# token: # tuicV4填写可以同时填写users
# token:
# - TOKEN
# users: # tuicV5填写可以同时填写token
# 00000000-0000-0000-0000-000000000000: PASSWORD_0
# 00000000-0000-0000-0000-000000000001: PASSWORD_1
# certificate: ./server.crt
# private-key: ./server.key
# congestion-controller: bbr

102
go.mod
View File

@@ -1,57 +1,58 @@
module github.com/Dreamacro/clash
go 1.20
go 1.19
require (
github.com/3andne/restls-client-go v0.1.6
github.com/3andne/restls-client-go v0.1.4
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da
github.com/beevik/ntp v1.3.0
github.com/cilium/ebpf v0.11.0
github.com/coreos/go-iptables v0.7.0
github.com/dlclark/regexp2 v1.10.0
github.com/go-chi/chi/v5 v5.0.10
github.com/cilium/ebpf v0.10.0
github.com/coreos/go-iptables v0.6.0
github.com/dlclark/regexp2 v1.9.0
github.com/go-chi/chi/v5 v5.0.8
github.com/go-chi/cors v1.2.1
github.com/go-chi/render v1.0.3
github.com/go-chi/render v1.0.2
github.com/gofrs/uuid/v5 v5.0.0
github.com/google/gopacket v1.1.19
github.com/gorilla/websocket v1.5.0
github.com/insomniacslk/dhcp v0.0.0-20230731140434-0f9eb93a696c
github.com/hashicorp/golang-lru v0.5.4
github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16
github.com/jpillora/backoff v1.0.0
github.com/klauspost/cpuid/v2 v2.2.5
github.com/klauspost/cpuid/v2 v2.0.12
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40
github.com/mdlayher/netlink v1.7.2
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759
github.com/metacubex/quic-go v0.38.1-0.20230821081539-517fdb17fb28
github.com/metacubex/sing-shadowsocks v0.2.4
github.com/metacubex/sing-shadowsocks2 v0.1.3
github.com/metacubex/sing-tun v0.1.11
github.com/metacubex/sing-vmess v0.1.8-0.20230801054944-603005461ff8
github.com/metacubex/sing-wireguard v0.0.0-20230611155257-1498ae315a28
github.com/miekg/dns v1.1.55
github.com/mroth/weightedrand/v2 v2.1.0
github.com/metacubex/quic-go v0.33.3-0.20230510010206-687b537b6a58
github.com/metacubex/sing-shadowsocks v0.2.2-0.20230509230448-a5157cc00a1c
github.com/metacubex/sing-shadowsocks2 v0.0.0-20230529235701-a238874242ca
github.com/metacubex/sing-tun v0.1.5-0.20230530125750-171afb2dfd8e
github.com/metacubex/sing-wireguard v0.0.0-20230426030325-41db09ae771a
github.com/miekg/dns v1.1.54
github.com/mroth/weightedrand/v2 v2.0.1
github.com/openacid/low v0.1.21
github.com/oschwald/maxminddb-golang v1.12.0
github.com/oschwald/geoip2-golang v1.8.0
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97
github.com/sagernet/sing v0.2.10-0.20230807080248-4db0062caa0a
github.com/sagernet/sing-mux v0.1.3-0.20230811111955-dc1639b5204c
github.com/sagernet/sing-shadowtls v0.1.4
github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6
github.com/sagernet/sing v0.2.5-0.20230530114415-221f066dba7c
github.com/sagernet/sing-mux v0.0.0-20230517134606-1ebe6bb26646
github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b
github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2
github.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f
github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77
github.com/samber/lo v1.38.1
github.com/shirou/gopsutil/v3 v3.23.7
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.8.4
github.com/shirou/gopsutil/v3 v3.23.4
github.com/sirupsen/logrus v1.9.0
github.com/stretchr/testify v1.8.2
github.com/xtls/go v0.0.0-20230107031059-4610f88d00f3
github.com/zhangyunhao116/fastrand v0.3.0
go.etcd.io/bbolt v1.3.7
go.uber.org/automaxprocs v1.5.3
golang.org/x/crypto v0.12.0
golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb
golang.org/x/net v0.14.0
golang.org/x/sync v0.3.0
golang.org/x/sys v0.11.0
google.golang.org/protobuf v1.31.0
go.uber.org/automaxprocs v1.5.2
golang.org/x/crypto v0.9.0
golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53
golang.org/x/net v0.10.0
golang.org/x/sync v0.2.0
golang.org/x/sys v0.8.0
google.golang.org/protobuf v1.30.0
gopkg.in/yaml.v3 v3.0.1
lukechampine.com/blake3 v1.2.1
lukechampine.com/blake3 v1.1.7
)
require (
@@ -65,43 +66,46 @@ require (
github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1 // indirect
github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/gaukas/godicttls v0.0.4 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/google/btree v1.1.2 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
github.com/hashicorp/yamux v0.1.1 // indirect
github.com/josharian/native v1.1.0 // indirect
github.com/klauspost/compress v1.16.7 // indirect
github.com/klauspost/compress v1.15.15 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/mdlayher/socket v0.4.1 // indirect
github.com/metacubex/gvisor v0.0.0-20230611153922-78842f086475 // indirect
github.com/metacubex/gvisor v0.0.0-20230417114019-3c3ee672d60c // indirect
github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // indirect
github.com/onsi/ginkgo/v2 v2.9.5 // indirect
github.com/onsi/ginkgo/v2 v2.2.0 // indirect
github.com/oschwald/maxminddb-golang v1.10.0 // indirect
github.com/pierrec/lz4/v4 v4.1.14 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/quic-go/qpack v0.4.0 // indirect
github.com/quic-go/qtls-go1-20 v0.3.2 // indirect
github.com/quic-go/qtls-go1-19 v0.2.1 // indirect
github.com/quic-go/qtls-go1-20 v0.1.1 // indirect
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 // indirect
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/shoenig/go-m1cpu v0.1.5 // indirect
github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b // indirect
github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c // indirect
github.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e // indirect
github.com/tklauser/go-sysconf v0.3.11 // indirect
github.com/tklauser/numcpus v0.6.0 // indirect
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
github.com/yusufpapurcu/wmi v1.2.3 // indirect
github.com/vishvananda/netns v0.0.4 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect
golang.org/x/mod v0.11.0 // indirect
golang.org/x/text v0.12.0 // indirect
golang.org/x/mod v0.8.0 // indirect
golang.org/x/text v0.9.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.9.1 // indirect
golang.org/x/tools v0.6.0 // indirect
)
replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20230817143035-28d23f152579
replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20230530121223-b768faae5c6b
replace github.com/sagernet/sing-vmess => github.com/metacubex/sing-vmess v0.1.5-0.20230520082358-78b126617899

231
go.sum
View File

@@ -1,5 +1,5 @@
github.com/3andne/restls-client-go v0.1.6 h1:tRx/YilqW7iHpgmEL4E1D8dAsuB0tFF3uvncS+B6I08=
github.com/3andne/restls-client-go v0.1.6/go.mod h1:iEdTZNt9kzPIxjIGSMScUFSBrUH6bFRNg0BWlP4orEY=
github.com/3andne/restls-client-go v0.1.4 h1:kLNC2aSRHPlEVYmTj6EOqJoorCpobEe2toMRSfBF7FU=
github.com/3andne/restls-client-go v0.1.4/go.mod h1:04CGbRk1BwBiEDles8b5mlKgTqIwE5MqF7JDloJV47I=
github.com/RyuaNerin/go-krypto v1.0.2 h1:9KiZrrBs+tDrQ66dNy4nrX6SzntKtSKdm0wKHhdB4WM=
github.com/RyuaNerin/go-krypto v1.0.2/go.mod h1:17LzMeJCgzGTkPH3TmfzRnEJ/yA7ErhTPp9sxIqONtA=
github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 h1:cDVUiFo+npB0ZASqnw4q90ylaVAbnYyx0JYqK4YcGok=
@@ -10,21 +10,19 @@ github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/beevik/ntp v1.3.0 h1:/w5VhpW5BGKS37vFm1p9oVk/t4HnnkKZAZIubHM6F7Q=
github.com/beevik/ntp v1.3.0/go.mod h1:vD6h1um4kzXpqmLTuu0cCLcC+NfvC0IC+ltmEDA8E78=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/cilium/ebpf v0.11.0 h1:V8gS/bTCCjX9uUnkUFUpPsksM8n1lXBAvHcpiFk1X2Y=
github.com/cilium/ebpf v0.11.0/go.mod h1:WE7CZAnqOL2RouJ4f1uyNhqr2P4CCvXFIqdRDUgWsVs=
github.com/coreos/go-iptables v0.7.0 h1:XWM3V+MPRr5/q51NuWSgU0fqMad64Zyxs8ZUoMsamr8=
github.com/coreos/go-iptables v0.7.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
github.com/cilium/ebpf v0.10.0 h1:nk5HPMeoBXtOzbkZBWym+ZWq1GIiHUsBFXxwewXAHLQ=
github.com/cilium/ebpf v0.10.0/go.mod h1:DPiVdY/kT534dgc9ERmvP8mWA+9gvwgKfRvk4nNWnoE=
github.com/coreos/go-iptables v0.6.0 h1:is9qnZMPYjLd8LYqmm/qlE+wwEgJIkTYdhV3rfZo4jk=
github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0=
github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dlclark/regexp2 v1.9.0 h1:pTK/l/3qYIKaRXuHnEnIf7Y5NxfRPfpb7dis6/gdlVI=
github.com/dlclark/regexp2 v1.9.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9 h1:/5RkVc9Rc81XmMyVqawCiDyrBHZbLAZgTTCqou4mwj8=
github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9/go.mod h1:hkIFzoiIPZYxdFOOLyDho59b7SrDfo+w3h+yWdlg45I=
github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 h1:8j2RH289RJplhA6WfdaPqzg1MjH2K8wX5e0uhAxrw2g=
@@ -34,54 +32,56 @@ github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1 h1:tlDMEdcPRQKBE
github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1/go.mod h1:4RfsapbGx2j/vU5xC/5/9qB3kn9Awp1YDiEnN43QrJ4=
github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010 h1:fuGucgPk5dN6wzfnxl3D0D3rVLw4v2SbBT9jb4VnxzA=
github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010/go.mod h1:JtBcj7sBuTTRupn7c2bFspMDIObMJsVK8TeUvpShPok=
github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA=
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/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.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk=
github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0=
github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
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=
github.com/go-chi/render v1.0.2 h1:4ER/udB0+fMWB2Jlf15RV3F4A2FDuYi/9f+lFttR/Lg=
github.com/go-chi/render v1.0.2/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M=
github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/tink/go v1.6.1 h1:t7JHqO8Ath2w2ig5vjwQYJzhGEZymedQc90lQXUBa4I=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/insomniacslk/dhcp v0.0.0-20230731140434-0f9eb93a696c h1:P/3mFnHCv1A/ej4m8pF5EB6FUt9qEL2Q9lfrcUNwCYs=
github.com/insomniacslk/dhcp v0.0.0-20230731140434-0f9eb93a696c/go.mod h1:7474bZ1YNCvarT6WFKie4kEET6J0KYRDC4XJqqXzQW4=
github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16 h1:+aAGyK41KRn8jbF2Q7PLL0Sxwg6dShGcQSeCC7nZQ8E=
github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16/go.mod h1:IKrnDWs3/Mqq5n0lI+RxA2sB7MvN/vbMBP3ehXg65UI=
github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=
github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw=
github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.0.12 h1:p9dKCg8i4gmOxtv35DvrYoWqYzQrvEVdjQ762Y0OqZE=
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
@@ -92,40 +92,40 @@ github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/
github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=
github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
github.com/metacubex/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-20230611153922-78842f086475 h1:qSEOvPPaMrWggFyFhFYGyMR8i1HKyhXjdi1QYUAa2ww=
github.com/metacubex/gvisor v0.0.0-20230611153922-78842f086475/go.mod h1:wehEpqiogdeyncfhckJP5gD2LtBgJW0wnDC24mJ+8Jg=
github.com/metacubex/quic-go v0.38.1-0.20230821081539-517fdb17fb28 h1:ggSo4B1LDH9ZIROoUibxlrUpi7YCMri7HMXn4aNQkiM=
github.com/metacubex/quic-go v0.38.1-0.20230821081539-517fdb17fb28/go.mod h1:SthFvvoqgrEUgIxQXRnqdUAAYQECBavkhl7iA0geVd8=
github.com/metacubex/sing v0.0.0-20230817143035-28d23f152579 h1:dE1dBB6CTzNdSMFTE5OCHvzHLewiqiA1nhD+7egtvAc=
github.com/metacubex/sing v0.0.0-20230817143035-28d23f152579/go.mod h1:9uOZwWkhT2Z2WldolLxX34s+1svAX4i4vvz5hy8u1MA=
github.com/metacubex/sing-shadowsocks v0.2.4 h1:Gc99Z17JVif1PKKq1pjqhSmc2kvHUgk+AqxOstCzhQ0=
github.com/metacubex/sing-shadowsocks v0.2.4/go.mod h1:w9qoEZSh9aKeXSLXHe0DGbG2UE9/2VlLGwukzQZ7byI=
github.com/metacubex/sing-shadowsocks2 v0.1.3 h1:nZvH+4jQXZ92NeNdR9fXaUGTPNJPt6u0nkcuh/NEt5Y=
github.com/metacubex/sing-shadowsocks2 v0.1.3/go.mod h1:5Mt93RlmRlIcDmvtapkhQJ8YTRGLFhHciLYopJjs7j8=
github.com/metacubex/sing-tun v0.1.11 h1:B8meDewklvKkeUfjqR2ViuYLam0/m4IgkTi3qcJIOuc=
github.com/metacubex/sing-tun v0.1.11/go.mod h1:vbki176Y5sxXC1DWXucrPh3q5j8cKai1D87y8m8rjQc=
github.com/metacubex/sing-vmess v0.1.8-0.20230801054944-603005461ff8 h1:AqqZCr9gOeKdO6oIzFh4b2puOUFcw8MdpmGHWRehyX8=
github.com/metacubex/sing-vmess v0.1.8-0.20230801054944-603005461ff8/go.mod h1:tyJg7b4s8NrSztl/Y1ajA7X0sJLlIsEJWkgRVocjmgY=
github.com/metacubex/sing-wireguard v0.0.0-20230611155257-1498ae315a28 h1:mXFpxfR/1nADh+GoT8maWEvc6LO6uatPsARD8WzUDMA=
github.com/metacubex/sing-wireguard v0.0.0-20230611155257-1498ae315a28/go.mod h1:KrDPq/dE793jGIJw9kcIvjA/proAfU0IeU7WlMXW7rs=
github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo=
github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
github.com/mroth/weightedrand/v2 v2.1.0 h1:o1ascnB1CIVzsqlfArQQjeMy1U0NcIbBO5rfd5E/OeU=
github.com/mroth/weightedrand/v2 v2.1.0/go.mod h1:f2faGsfOGOwc1p94wzHKKZyTpcJUW7OJ/9U4yfiNAOU=
github.com/metacubex/gvisor v0.0.0-20230417114019-3c3ee672d60c h1:D62872jiuzC6b+3aI8tqfeyc6YgbfarYKywTnnvXwEM=
github.com/metacubex/gvisor v0.0.0-20230417114019-3c3ee672d60c/go.mod h1:wqEuzdImyqD2MCGE8CYRJXbB77oSEJeoSSXXdwKjnsE=
github.com/metacubex/quic-go v0.33.3-0.20230510010206-687b537b6a58 h1:E/sNW9tugFjoBjAkth89MHlKHRaMdo43tGQ3MOPVayQ=
github.com/metacubex/quic-go v0.33.3-0.20230510010206-687b537b6a58/go.mod h1:9nOiGX6kqV3+ZbkDKdTNzdFD726QQHPH6WDb36jUSpA=
github.com/metacubex/sing v0.0.0-20230530121223-b768faae5c6b h1:Bw4j3ktf5vivi5qm/ZQGtyRAgybRKSGJaMV1t3rtC+I=
github.com/metacubex/sing v0.0.0-20230530121223-b768faae5c6b/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w=
github.com/metacubex/sing-shadowsocks v0.2.2-0.20230509230448-a5157cc00a1c h1:LpVNvlW/xE+mR8z76xJeYZlYznZXEmU4TeWeuygYdJg=
github.com/metacubex/sing-shadowsocks v0.2.2-0.20230509230448-a5157cc00a1c/go.mod h1:4uQQReKMTU7KTfOykVBe/oGJ00pl38d+BYJ99+mx26s=
github.com/metacubex/sing-shadowsocks2 v0.0.0-20230529235701-a238874242ca h1:10qc50Q1hHrfGO4NjEJpIAgHX63Y256tHE0dFCTN8J4=
github.com/metacubex/sing-shadowsocks2 v0.0.0-20230529235701-a238874242ca/go.mod h1:jVDD4N22bDPPKA73NvB7aqdlLWiAwv8D+jx7HwhcWak=
github.com/metacubex/sing-tun v0.1.5-0.20230530125750-171afb2dfd8e h1:7QlJQl4S3F3YXn48fYxjymMw8HkXg9bl++hLi4ZRyCY=
github.com/metacubex/sing-tun v0.1.5-0.20230530125750-171afb2dfd8e/go.mod h1:u9onX49LZPYuIPQ7SdM64Gnins8y5wg4Cn6ZYRSxWHU=
github.com/metacubex/sing-vmess v0.1.5-0.20230520082358-78b126617899 h1:iRfcuztp7REfmOyasSlCL/pqNWfUDMTJ2CwbGpxpeks=
github.com/metacubex/sing-vmess v0.1.5-0.20230520082358-78b126617899/go.mod h1:RSt9rxGHllLdc5JUebkQwaqyWLx09Lqya37DlBe8CP8=
github.com/metacubex/sing-wireguard v0.0.0-20230426030325-41db09ae771a h1:cWKym33Qvl6HA3hj4/YuYD8hHyqQPb47wT5cJRAPgco=
github.com/metacubex/sing-wireguard v0.0.0-20230426030325-41db09ae771a/go.mod h1:Bsw2BvKMMMY0FhZPseDI50ZOalvoUPMKYyGpyqvIIqY=
github.com/miekg/dns v1.1.54 h1:5jon9mWcb0sFJGpnI99tOMhCPyJ+RPVz5b63MQG0VWI=
github.com/miekg/dns v1.1.54/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
github.com/mroth/weightedrand/v2 v2.0.1 h1:zrEVDIaau/E4QLOKu02kpg8T8myweFlMGikIgbIdrRA=
github.com/mroth/weightedrand/v2 v2.0.1/go.mod h1:f2faGsfOGOwc1p94wzHKKZyTpcJUW7OJ/9U4yfiNAOU=
github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 h1:1102pQc2SEPp5+xrS26wEaeb26sZy6k9/ZXlZN+eXE4=
github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7/go.mod h1:UqoUn6cHESlliMhOnKLWr+CBH+e3bazUPvFj1XZwAjs=
github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=
github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
github.com/onsi/ginkgo/v2 v2.2.0 h1:3ZNA3L1c5FYDFTTxbFeVGGD8jYvjYauHD30YgLxVsNI=
github.com/onsi/ginkgo/v2 v2.2.0/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk=
github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q=
github.com/openacid/errors v0.8.1/go.mod h1:GUQEJJOJE3W9skHm8E8Y4phdl2LLEN8iD7c5gcGgdx0=
github.com/openacid/low v0.1.21 h1:Tr2GNu4N/+rGRYdOsEHOE89cxUIaDViZbVmKz29uKGo=
github.com/openacid/low v0.1.21/go.mod h1:q+MsKI6Pz2xsCkzV4BLj7NR5M4EX0sGz5AqotpZDVh0=
github.com/openacid/must v0.1.3/go.mod h1:luPiXCuJlEo3UUFQngVQokV0MPGryeYvtCbQPs3U1+I=
github.com/openacid/testkeys v0.1.6/go.mod h1:MfA7cACzBpbiwekivj8StqX0WIRmqlMsci1c37CA3Do=
github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs=
github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY=
github.com/oschwald/geoip2-golang v1.8.0 h1:KfjYB8ojCEn/QLqsDU0AzrJ3R5Qa9vFlx3z6SLNcKTs=
github.com/oschwald/geoip2-golang v1.8.0/go.mod h1:R7bRvYjOeaoenAp9sKRS8GX5bJWcZ0laWO5+DauEktw=
github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg=
github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0=
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -136,165 +136,138 @@ github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:Om
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
github.com/quic-go/qtls-go1-20 v0.3.2 h1:rRgN3WfnKbyik4dBV8A6girlJVxGand/d+jVKbQq5GI=
github.com/quic-go/qtls-go1-20 v0.3.2/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
github.com/quic-go/qtls-go1-19 v0.2.1 h1:aJcKNMkH5ASEJB9FXNeZCyTEIHU1J7MmHyz1Q1TSG1A=
github.com/quic-go/qtls-go1-19 v0.2.1/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
github.com/quic-go/qtls-go1-20 v0.1.1 h1:KbChDlg82d3IHqaj2bn6GfKRj84Per2VGf5XV3wSwQk=
github.com/quic-go/qtls-go1-20 v0.1.1/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms=
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
github.com/sagernet/sing-mux v0.1.3-0.20230811111955-dc1639b5204c h1:35/FowAvt3Z62mck0TXzVc4jS5R5CWq62qcV2P1cp0I=
github.com/sagernet/sing-mux v0.1.3-0.20230811111955-dc1639b5204c/go.mod h1:TKxqIvfQQgd36jp2tzsPavGjYTVZilV+atip1cssjIY=
github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k=
github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4=
github.com/sagernet/sing-mux v0.0.0-20230517134606-1ebe6bb26646 h1:X3ADfMqeGns1Q1FlXc9kaL9FwW1UM6D6tEQo8jFstpc=
github.com/sagernet/sing-mux v0.0.0-20230517134606-1ebe6bb26646/go.mod h1:pF+RnLvCAOhECrvauy6LYOpBakJ/vuaF1Wm4lPsWryI=
github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b h1:ouW/6IDCrxkBe19YSbdCd7buHix7b+UZ6BM4Zz74XF4=
github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b/go.mod h1:oG8bPerYI6cZ74KquY3DvA7ynECyrILPBnce6wtBqeI=
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as=
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37/go.mod h1:3skNSftZDJWTGVtVaM2jfbce8qHnmH/AGDRe62iNOg0=
github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6 h1:Px+hN4Vzgx+iCGVnWH5A8eR7JhNnIV3rGQmBxA7cw6Q=
github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6/go.mod h1:zovq6vTvEM6ECiqE3Eeb9rpIylPpamPcmrJ9tv0Bt0M=
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 h1:2ItpW1nMNkPzmBTxV0/eClCklHrFSQMnUGcpUmJxVeE=
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9/go.mod h1:FUyTEc5ye5NjKnDTDMuiLF2M6T4BE6y6KZuax//UCEg=
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 h1:kDUqhc9Vsk5HJuhfIATJ8oQwBmpOZJuozQG7Vk88lL4=
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2/go.mod h1:JKQMZq/O2qnZjdrt+B57olmfgEmLtY9iiSIEYtWvoSM=
github.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f h1:Kvo8w8Y9lzFGB/7z09MJ3TR99TFtfI/IuY87Ygcycho=
github.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f/go.mod h1:mySs0abhpc/gLlvhoq7HP1RzOaRmIXVeZGCh++zoApk=
github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77 h1:g6QtRWQ2dKX7EQP++1JLNtw4C2TNxd4/ov8YUpOPOSo=
github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77/go.mod h1:pJDdXzZIwJ+2vmnT0TKzmf8meeum+e2mTDSehw79eE0=
github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM=
github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 h1:rc/CcqLH3lh8n+csdOuDfP+NuykE0U6AeYSJJHKDgSg=
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9/go.mod h1:a/83NAfUXvEuLpmxDssAXxgUgrEy12MId3Wd7OTs76s=
github.com/shirou/gopsutil/v3 v3.23.7 h1:C+fHO8hfIppoJ1WdsVm1RoI0RwXoNdfTK7yWXV0wVj4=
github.com/shirou/gopsutil/v3 v3.23.7/go.mod h1:c4gnmoRC0hQuaLqvxnx1//VXQ0Ms/X9UnJF8pddY5z4=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
github.com/shirou/gopsutil/v3 v3.23.4 h1:hZwmDxZs7Ewt75DV81r4pFMqbq+di2cbt9FsQBqLD2o=
github.com/shirou/gopsutil/v3 v3.23.4/go.mod h1:ZcGxyfzAMRevhUR2+cfhXDH6gQdFYE/t8j1nsU4mPI8=
github.com/shoenig/go-m1cpu v0.1.5 h1:LF57Z/Fpb/WdGLjt2HZilNnmZOxg/q2bSKTQhgbrLrQ=
github.com/shoenig/go-m1cpu v0.1.5/go.mod h1:Wwvst4LR89UxjeFtLRMrpgRiyY4xPsejnVZym39dbAQ=
github.com/shoenig/test v0.6.3 h1:GVXWJFk9PiOjN0KoJ7VrJGH6uLPnqxR7/fe3HUPfE0c=
github.com/shoenig/test v0.6.3/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b h1:rXHg9GrUEtWZhEkrykicdND3VPjlVbYiLdX9J7gimS8=
github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b/go.mod h1:X7qrxNQViEaAN9LNZOPl9PfvQtp3V3c7LTo0dvGi0fM=
github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c h1:DjKMC30y6yjG3IxDaeAj3PCoRr+IsO+bzyT+Se2m2Hk=
github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c/go.mod h1:NV/a66PhhWYVmUMaotlXJ8fIEFB98u+c8l/CQIEFLrU=
github.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e h1:ur8uMsPIFG3i4Gi093BQITvwH9znsz2VUZmnmwHvpIo=
github.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e/go.mod h1:+e5fBW3bpPyo+3uLo513gIUblc03egGjMM0+5GKbzK8=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM=
github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI=
github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms=
github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4=
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA=
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
github.com/xtls/go v0.0.0-20230107031059-4610f88d00f3 h1:a3Y4WVjCxwoyO4E2xdNvq577tW8lkSBgyrA8E9+2NtM=
github.com/xtls/go v0.0.0-20230107031059-4610f88d00f3/go.mod h1:YJTRELIWrGxR1s8xcEBgxcxBfwQfMGjdvNLTjN9XFgY=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
github.com/zhangyunhao116/fastrand v0.3.0 h1:7bwe124xcckPulX6fxtr2lFdO2KQqaefdtbk+mqO/Ig=
github.com/zhangyunhao116/fastrand v0.3.0/go.mod h1:0v5KgHho0VE6HU192HnY15de/oDS8UrbBChIFjIhBtc=
gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec h1:FpfFs4EhNehiVfzQttTuxanPIT43FtkkCFypIod8LHo=
gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec/go.mod h1:BZ1RAoRPbCxum9Grlv5aeksu2H8BiKehBYooU2LFiOQ=
go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ=
go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
go.uber.org/automaxprocs v1.5.2 h1:2LxUOGiR3O6tw8ui5sZa2LAaHnsviZdVOUZw4fvbnME=
go.uber.org/automaxprocs v1.5.2/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb h1:mIKbk8weKhSeLH2GmUTrvx8CjkyJmnU1wFmg59CUjFA=
golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 h1:5llv2sWeaMSnA3w2kS57ouQQ4pudlXrR0dCgw51QK9o=
golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU=
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo=
golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI=
lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0=
lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA=

View File

@@ -2,15 +2,11 @@ package executor
import (
"fmt"
"github.com/Dreamacro/clash/ntp"
"net"
"net/netip"
"os"
"runtime"
"strconv"
"strings"
"sync"
"time"
"github.com/Dreamacro/clash/adapter"
"github.com/Dreamacro/clash/adapter/inbound"
@@ -96,7 +92,6 @@ func ApplyConfig(cfg *config.Config, force bool) {
updateSniffer(cfg.Sniffer)
updateHosts(cfg.Hosts)
updateGeneral(cfg.General)
updateNTP(cfg.NTP)
updateDNS(cfg.DNS, cfg.RuleProviders, cfg.General.IPv6)
updateListeners(cfg.General, cfg.Listeners, force)
updateIPTables(cfg)
@@ -134,7 +129,7 @@ func GetGeneral() *config.General {
RedirPort: ports.RedirPort,
TProxyPort: ports.TProxyPort,
MixedPort: ports.MixedPort,
Tun: listener.LastTunConf,
Tun: listener.GetTunConf(),
TuicServer: listener.GetTuicConf(),
ShadowSocksConfig: ports.ShadowSocksConfig,
VmessConfig: ports.VmessConfig,
@@ -183,13 +178,6 @@ func updateListeners(general *config.General, listeners map[string]C.InboundList
func updateExperimental(c *config.Config) {
}
func updateNTP(c *config.NTP) {
if c.Enable {
ntp.ReCreateNTPService(net.JoinHostPort(c.Server, strconv.Itoa(c.Port)),
time.Duration(c.Interval))
}
}
func updateDNS(c *config.DNS, ruleProvider map[string]provider.RuleProvider, generalIPv6 bool) {
if !c.Enable {
resolver.DefaultResolver = nil
@@ -372,7 +360,6 @@ func updateGeneral(general *config.General) {
}
inbound.SetTfo(general.InboundTfo)
inbound.SetMPTCP(general.InboundMPTCP)
adapter.UnifiedDelay.Store(general.UnifiedDelay)

View File

@@ -84,18 +84,16 @@ type tunSchema struct {
}
type tuicServerSchema struct {
Enable bool `yaml:"enable" json:"enable"`
Listen *string `yaml:"listen" json:"listen"`
Token *[]string `yaml:"token" json:"token"`
Users *map[string]string `yaml:"users" json:"users,omitempty"`
Certificate *string `yaml:"certificate" json:"certificate"`
PrivateKey *string `yaml:"private-key" json:"private-key"`
CongestionController *string `yaml:"congestion-controller" json:"congestion-controller,omitempty"`
MaxIdleTime *int `yaml:"max-idle-time" json:"max-idle-time,omitempty"`
AuthenticationTimeout *int `yaml:"authentication-timeout" json:"authentication-timeout,omitempty"`
ALPN *[]string `yaml:"alpn" json:"alpn,omitempty"`
MaxUdpRelayPacketSize *int `yaml:"max-udp-relay-packet-size" json:"max-udp-relay-packet-size,omitempty"`
CWND *int `yaml:"cwnd" json:"cwnd,omitempty"`
Enable bool `yaml:"enable" json:"enable"`
Listen *string `yaml:"listen" json:"listen"`
Token *[]string `yaml:"token" json:"token"`
Certificate *string `yaml:"certificate" json:"certificate"`
PrivateKey *string `yaml:"private-key" json:"private-key"`
CongestionController *string `yaml:"congestion-controller" json:"congestion-controller,omitempty"`
MaxIdleTime *int `yaml:"max-idle-time" json:"max-idle-time,omitempty"`
AuthenticationTimeout *int `yaml:"authentication-timeout" json:"authentication-timeout,omitempty"`
ALPN *[]string `yaml:"alpn" json:"alpn,omitempty"`
MaxUdpRelayPacketSize *int `yaml:"max-udp-relay-packet-size" json:"max-udp-relay-packet-size,omitempty"`
}
func getConfigs(w http.ResponseWriter, r *http.Request) {
@@ -188,9 +186,6 @@ func pointerOrDefaultTuicServer(p *tuicServerSchema, def LC.TuicServer) LC.TuicS
if p.Token != nil {
def.Token = *p.Token
}
if p.Users != nil {
def.Users = *p.Users
}
if p.Certificate != nil {
def.Certificate = *p.Certificate
}
@@ -212,9 +207,6 @@ func pointerOrDefaultTuicServer(p *tuicServerSchema, def LC.TuicServer) LC.TuicS
if p.MaxUdpRelayPacketSize != nil {
def.MaxUdpRelayPacketSize = *p.MaxUdpRelayPacketSize
}
if p.CWND != nil {
def.CWND = *p.CWND
}
}
return def
}

Some files were not shown because too many files have changed in this diff Show More