Merge branch 'Alpha' into Meta
This commit is contained in:
commit
bda71dbfa1
127 changed files with 2056 additions and 1170 deletions
69
.github/workflows/android-branch-auto-sync.yml
vendored
69
.github/workflows/android-branch-auto-sync.yml
vendored
|
@ -1,69 +0,0 @@
|
|||
name: Android Branch Auto Sync
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths-ignore:
|
||||
- "docs/**"
|
||||
- "README.md"
|
||||
- ".github/ISSUE_TEMPLATE/**"
|
||||
branches:
|
||||
- Alpha
|
||||
- android-open
|
||||
tags:
|
||||
- "v*"
|
||||
pull_request_target:
|
||||
branches:
|
||||
- Alpha
|
||||
- android-open
|
||||
|
||||
jobs:
|
||||
update-dependencies:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Configure Git
|
||||
run: |
|
||||
git config --global user.name 'GitHub Action'
|
||||
git config --global user.email 'action@github.com'
|
||||
|
||||
- name: Sync android-real with Alpha rebase android-open
|
||||
run: |
|
||||
git fetch origin
|
||||
git checkout origin/Alpha -b android-real
|
||||
git merge --squash origin/android-open
|
||||
git commit -m "Android: patch"
|
||||
|
||||
- name: Check for conflicts
|
||||
run: |
|
||||
CONFLICTS=$(git diff --name-only --diff-filter=U)
|
||||
if [ ! -z "$CONFLICTS" ]; then
|
||||
echo "There are conflicts in the following files:"
|
||||
echo $CONFLICTS
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Push changes
|
||||
run: |
|
||||
git push origin android-real --force
|
||||
|
||||
# Send "core-updated" to MetaCubeX/MihomoForAndroid to trigger update-dependencies
|
||||
trigger-MFA-update:
|
||||
needs: update-dependencies
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: tibdex/github-app-token@v1
|
||||
id: generate-token
|
||||
with:
|
||||
app_id: ${{ secrets.MAINTAINER_APPID }}
|
||||
private_key: ${{ secrets.MAINTAINER_APP_PRIVATE_KEY }}
|
||||
|
||||
- name: Trigger update-dependencies
|
||||
run: |
|
||||
curl -X POST https://api.github.com/repos/MetaCubeX/MihomoForAndroid/dispatches \
|
||||
-H "Accept: application/vnd.github.everest-preview+json" \
|
||||
-H "Authorization: token ${{ steps.generate-token.outputs.token }}" \
|
||||
-d '{"event_type": "core-updated"}'
|
27
.github/workflows/build.yml
vendored
27
.github/workflows/build.yml
vendored
|
@ -19,7 +19,7 @@ concurrency:
|
|||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
REGISTRY: docker.io
|
||||
REGISTRY: ghcr.io
|
||||
jobs:
|
||||
Build:
|
||||
permissions: write-all
|
||||
|
@ -161,9 +161,8 @@ jobs:
|
|||
if: ${{ matrix.job.type=='WithCGO' && matrix.job.target=='android' }}
|
||||
id: setup-ndk
|
||||
with:
|
||||
ndk-version: r26
|
||||
add-to-path: false
|
||||
local-cache: true
|
||||
ndk-version: r26b
|
||||
add-to-path: true
|
||||
|
||||
- name: Build Android
|
||||
if: ${{ matrix.job.type=='WithCGO' && matrix.job.target=='android' }}
|
||||
|
@ -343,24 +342,25 @@ jobs:
|
|||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ secrets.DOCKERHUB_ACCOUNT }}/${{secrets.DOCKERHUB_REPO}}
|
||||
images: ${{ env.REGISTRY }}/${{ github.repository }}
|
||||
|
||||
- name: Show files
|
||||
run: |
|
||||
ls .
|
||||
ls bin/
|
||||
- name: Log into registry
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v2
|
||||
|
||||
- name: login to ghcr.io
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ secrets.DOCKER_HUB_USER }}
|
||||
password: ${{ secrets.DOCKER_HUB_TOKEN }}
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# Build and push Docker image with Buildx (don't push on PR)
|
||||
# https://github.com/docker/build-push-action
|
||||
- name: Build and push Docker image
|
||||
id: build-and-push
|
||||
uses: docker/build-push-action@v4
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
|
@ -368,8 +368,7 @@ jobs:
|
|||
platforms: |
|
||||
linux/386
|
||||
linux/amd64
|
||||
linux/arm64/v8
|
||||
linux/arm64
|
||||
linux/arm/v7
|
||||
# linux/riscv64
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
|
|
33
.github/workflows/trigger-cmfa-update.yml
vendored
Normal file
33
.github/workflows/trigger-cmfa-update.yml
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
name: Trigger CMFA Update
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths-ignore:
|
||||
- "docs/**"
|
||||
- "README.md"
|
||||
- ".github/ISSUE_TEMPLATE/**"
|
||||
branches:
|
||||
- Alpha
|
||||
tags:
|
||||
- "v*"
|
||||
pull_request_target:
|
||||
branches:
|
||||
- Alpha
|
||||
|
||||
jobs:
|
||||
# Send "core-updated" to MetaCubeX/MihomoForAndroid to trigger update-dependencies
|
||||
trigger-CMFA-update:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: tibdex/github-app-token@v1
|
||||
id: generate-token
|
||||
with:
|
||||
app_id: ${{ secrets.MAINTAINER_APPID }}
|
||||
private_key: ${{ secrets.MAINTAINER_APP_PRIVATE_KEY }}
|
||||
|
||||
- name: Trigger update-dependencies
|
||||
run: |
|
||||
curl -X POST https://api.github.com/repos/MetaCubeX/MihomoForAndroid/dispatches \
|
||||
-H "Accept: application/vnd.github.everest-preview+json" \
|
||||
-H "Authorization: token ${{ steps.generate-token.outputs.token }}" \
|
||||
-d '{"event_type": "core-updated"}'
|
|
@ -17,9 +17,7 @@ import (
|
|||
"github.com/metacubex/mihomo/common/utils"
|
||||
"github.com/metacubex/mihomo/component/dialer"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/log"
|
||||
|
||||
"github.com/puzpuzpuz/xsync/v2"
|
||||
"github.com/puzpuzpuz/xsync/v3"
|
||||
)
|
||||
|
||||
var UnifiedDelay = atomic.NewBool(false)
|
||||
|
@ -41,11 +39,6 @@ type Proxy struct {
|
|||
extra *xsync.MapOf[string, *extraProxyState]
|
||||
}
|
||||
|
||||
// Alive implements C.Proxy
|
||||
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 {
|
||||
|
@ -181,7 +174,7 @@ func (p *Proxy) MarshalJSON() ([]byte, error) {
|
|||
_ = json.Unmarshal(inner, &mapping)
|
||||
mapping["history"] = p.DelayHistory()
|
||||
mapping["extra"] = p.ExtraDelayHistory()
|
||||
mapping["alive"] = p.Alive()
|
||||
mapping["alive"] = p.AliveForTestUrl(p.url)
|
||||
mapping["name"] = p.Name()
|
||||
mapping["udp"] = p.SupportUDP()
|
||||
mapping["xudp"] = p.SupportXUDP()
|
||||
|
@ -191,13 +184,11 @@ 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, expectedStatus utils.IntRanges[uint16]) (t uint16, err error) {
|
||||
defer func() {
|
||||
alive := err == nil
|
||||
store = p.determineFinalStoreType(store, url)
|
||||
|
||||
switch store {
|
||||
case C.OriginalHistory:
|
||||
if len(p.url) == 0 || url == p.url {
|
||||
p.alive.Store(alive)
|
||||
record := C.DelayHistory{Time: time.Now()}
|
||||
if alive {
|
||||
|
@ -212,7 +203,7 @@ func (p *Proxy) URLTest(ctx context.Context, url string, expectedStatus utils.In
|
|||
if len(p.url) == 0 {
|
||||
p.url = url
|
||||
}
|
||||
case C.ExtraHistory:
|
||||
} else {
|
||||
record := C.DelayHistory{Time: time.Now()}
|
||||
if alive {
|
||||
record.Delay = t
|
||||
|
@ -236,8 +227,6 @@ func (p *Proxy) URLTest(ctx context.Context, url string, expectedStatus utils.In
|
|||
if state.history.Len() > defaultHistoriesNum {
|
||||
state.history.Pop()
|
||||
}
|
||||
default:
|
||||
log.Debugln("health check result will be discarded, url: %s alive: %t, delay: %d", url, alive, t)
|
||||
}
|
||||
}()
|
||||
|
||||
|
@ -316,7 +305,7 @@ func NewProxy(adapter C.ProxyAdapter) *Proxy {
|
|||
history: queue.New[C.DelayHistory](defaultHistoriesNum),
|
||||
alive: atomic.NewBool(true),
|
||||
url: "",
|
||||
extra: xsync.NewMapOf[*extraProxyState]()}
|
||||
extra: xsync.NewMapOf[string, *extraProxyState]()}
|
||||
}
|
||||
|
||||
func urlToMetadata(rawURL string) (addr C.Metadata, err error) {
|
||||
|
@ -349,24 +338,3 @@ func urlToMetadata(rawURL string) (addr C.Metadata, err error) {
|
|||
}
|
||||
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
|
||||
}
|
||||
|
||||
if p.extra.Size() < 2*C.DefaultMaxHealthCheckUrlNum {
|
||||
return C.ExtraHistory
|
||||
}
|
||||
|
||||
_, ok := p.extra.Load(url)
|
||||
if ok {
|
||||
return C.ExtraHistory
|
||||
}
|
||||
|
||||
return store
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@ func NewHTTP(target socks5.Addr, srcConn net.Conn, conn net.Conn, additions ...A
|
|||
metadata := parseSocksAddr(target)
|
||||
metadata.NetWork = C.TCP
|
||||
metadata.Type = C.HTTP
|
||||
metadata.RawSrcAddr = srcConn.RemoteAddr()
|
||||
metadata.RawDstAddr = srcConn.LocalAddr()
|
||||
ApplyAdditions(metadata, WithSrcAddr(srcConn.RemoteAddr()), WithInAddr(conn.LocalAddr()))
|
||||
ApplyAdditions(metadata, additions...)
|
||||
return conn, metadata
|
||||
|
|
|
@ -10,6 +10,8 @@ func NewPacket(target socks5.Addr, packet C.UDPPacket, source C.Type, additions
|
|||
metadata := parseSocksAddr(target)
|
||||
metadata.NetWork = C.UDP
|
||||
metadata.Type = source
|
||||
metadata.RawSrcAddr = packet.LocalAddr()
|
||||
metadata.RawDstAddr = metadata.UDPAddr()
|
||||
ApplyAdditions(metadata, WithSrcAddr(packet.LocalAddr()))
|
||||
if p, ok := packet.(C.UDPPacketInAddr); ok {
|
||||
ApplyAdditions(metadata, WithInAddr(p.InAddr()))
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
|
||||
type Reject struct {
|
||||
*Base
|
||||
drop bool
|
||||
}
|
||||
|
||||
type RejectOption struct {
|
||||
|
@ -21,12 +22,18 @@ type RejectOption struct {
|
|||
|
||||
// DialContext implements C.ProxyAdapter
|
||||
func (r *Reject) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) {
|
||||
if r.drop {
|
||||
return NewConn(dropConn{}, r), nil
|
||||
}
|
||||
return NewConn(nopConn{}, r), nil
|
||||
}
|
||||
|
||||
// ListenPacketContext implements C.ProxyAdapter
|
||||
func (r *Reject) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) {
|
||||
return newPacketConn(nopPacketConn{}, r), nil
|
||||
if r.drop {
|
||||
return newPacketConn(&dropPacketConn{}, r), nil
|
||||
}
|
||||
return newPacketConn(&nopPacketConn{}, r), nil
|
||||
}
|
||||
|
||||
func NewRejectWithOption(option RejectOption) *Reject {
|
||||
|
@ -50,6 +57,18 @@ func NewReject() *Reject {
|
|||
}
|
||||
}
|
||||
|
||||
func NewRejectDrop() *Reject {
|
||||
return &Reject{
|
||||
Base: &Base{
|
||||
name: "REJECT-DROP",
|
||||
tp: C.RejectDrop,
|
||||
udp: true,
|
||||
prefer: C.DualStack,
|
||||
},
|
||||
drop: true,
|
||||
}
|
||||
}
|
||||
|
||||
func NewPass() *Reject {
|
||||
return &Reject{
|
||||
Base: &Base{
|
||||
|
@ -63,35 +82,29 @@ func NewPass() *Reject {
|
|||
|
||||
type nopConn struct{}
|
||||
|
||||
func (rw nopConn) Read(b []byte) (int, error) {
|
||||
return 0, io.EOF
|
||||
}
|
||||
func (rw nopConn) Read(b []byte) (int, error) { return 0, io.EOF }
|
||||
|
||||
func (rw nopConn) ReadBuffer(buffer *buf.Buffer) error {
|
||||
return io.EOF
|
||||
}
|
||||
func (rw nopConn) ReadBuffer(buffer *buf.Buffer) error { return io.EOF }
|
||||
|
||||
func (rw nopConn) Write(b []byte) (int, error) {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
func (rw nopConn) WriteBuffer(buffer *buf.Buffer) error {
|
||||
return io.EOF
|
||||
}
|
||||
|
||||
func (rw nopConn) Close() error { return nil }
|
||||
func (rw nopConn) LocalAddr() net.Addr { return nil }
|
||||
func (rw nopConn) RemoteAddr() net.Addr { return nil }
|
||||
func (rw nopConn) SetDeadline(time.Time) error { return nil }
|
||||
func (rw nopConn) SetReadDeadline(time.Time) error { return nil }
|
||||
func (rw nopConn) SetWriteDeadline(time.Time) error { return nil }
|
||||
func (rw nopConn) Write(b []byte) (int, error) { return 0, io.EOF }
|
||||
func (rw nopConn) WriteBuffer(buffer *buf.Buffer) error { return io.EOF }
|
||||
func (rw nopConn) Close() error { return nil }
|
||||
func (rw nopConn) LocalAddr() net.Addr { return nil }
|
||||
func (rw nopConn) RemoteAddr() net.Addr { return nil }
|
||||
func (rw nopConn) SetDeadline(time.Time) error { return nil }
|
||||
func (rw nopConn) SetReadDeadline(time.Time) error { return nil }
|
||||
func (rw nopConn) SetWriteDeadline(time.Time) error { return nil }
|
||||
|
||||
var udpAddrIPv4Unspecified = &net.UDPAddr{IP: net.IPv4zero, Port: 0}
|
||||
|
||||
type nopPacketConn struct{}
|
||||
|
||||
func (npc nopPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) { return len(b), nil }
|
||||
func (npc nopPacketConn) ReadFrom(b []byte) (int, net.Addr, error) { return 0, nil, io.EOF }
|
||||
func (npc nopPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
|
||||
return len(b), nil
|
||||
}
|
||||
func (npc nopPacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
|
||||
return 0, nil, io.EOF
|
||||
}
|
||||
func (npc nopPacketConn) WaitReadFrom() ([]byte, func(), net.Addr, error) {
|
||||
return nil, nil, nil, io.EOF
|
||||
}
|
||||
|
@ -100,3 +113,38 @@ func (npc nopPacketConn) LocalAddr() net.Addr { return udpAddrIPv4U
|
|||
func (npc nopPacketConn) SetDeadline(time.Time) error { return nil }
|
||||
func (npc nopPacketConn) SetReadDeadline(time.Time) error { return nil }
|
||||
func (npc nopPacketConn) SetWriteDeadline(time.Time) error { return nil }
|
||||
|
||||
type dropConn struct{}
|
||||
|
||||
func (rw dropConn) Read(b []byte) (int, error) { return 0, io.EOF }
|
||||
func (rw dropConn) ReadBuffer(buffer *buf.Buffer) error {
|
||||
time.Sleep(C.DefaultDropTime)
|
||||
return io.EOF
|
||||
}
|
||||
func (rw dropConn) Write(b []byte) (int, error) { return 0, io.EOF }
|
||||
func (rw dropConn) WriteBuffer(buffer *buf.Buffer) error { return io.EOF }
|
||||
func (rw dropConn) Close() error { return nil }
|
||||
func (rw dropConn) LocalAddr() net.Addr { return nil }
|
||||
func (rw dropConn) RemoteAddr() net.Addr { return nil }
|
||||
func (rw dropConn) SetDeadline(time.Time) error { return nil }
|
||||
func (rw dropConn) SetReadDeadline(time.Time) error { return nil }
|
||||
func (rw dropConn) SetWriteDeadline(time.Time) error { return nil }
|
||||
|
||||
type dropPacketConn struct{}
|
||||
|
||||
func (npc dropPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
|
||||
time.Sleep(C.DefaultDropTime)
|
||||
return len(b), nil
|
||||
}
|
||||
func (npc dropPacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
|
||||
time.Sleep(C.DefaultDropTime)
|
||||
return 0, nil, io.EOF
|
||||
}
|
||||
func (npc dropPacketConn) WaitReadFrom() ([]byte, func(), net.Addr, error) {
|
||||
return nil, nil, nil, io.EOF
|
||||
}
|
||||
func (npc dropPacketConn) Close() error { return nil }
|
||||
func (npc dropPacketConn) LocalAddr() net.Addr { return udpAddrIPv4Unspecified }
|
||||
func (npc dropPacketConn) SetDeadline(time.Time) error { return nil }
|
||||
func (npc dropPacketConn) SetReadDeadline(time.Time) error { return nil }
|
||||
func (npc dropPacketConn) SetWriteDeadline(time.Time) error { return nil }
|
||||
|
|
|
@ -58,15 +58,16 @@ type simpleObfsOption struct {
|
|||
}
|
||||
|
||||
type v2rayObfsOption struct {
|
||||
Mode string `obfs:"mode"`
|
||||
Host string `obfs:"host,omitempty"`
|
||||
Path string `obfs:"path,omitempty"`
|
||||
TLS bool `obfs:"tls,omitempty"`
|
||||
Fingerprint string `obfs:"fingerprint,omitempty"`
|
||||
Headers map[string]string `obfs:"headers,omitempty"`
|
||||
SkipCertVerify bool `obfs:"skip-cert-verify,omitempty"`
|
||||
Mux bool `obfs:"mux,omitempty"`
|
||||
V2rayHttpUpgrade bool `obfs:"v2ray-http-upgrade,omitempty"`
|
||||
Mode string `obfs:"mode"`
|
||||
Host string `obfs:"host,omitempty"`
|
||||
Path string `obfs:"path,omitempty"`
|
||||
TLS bool `obfs:"tls,omitempty"`
|
||||
Fingerprint string `obfs:"fingerprint,omitempty"`
|
||||
Headers map[string]string `obfs:"headers,omitempty"`
|
||||
SkipCertVerify bool `obfs:"skip-cert-verify,omitempty"`
|
||||
Mux bool `obfs:"mux,omitempty"`
|
||||
V2rayHttpUpgrade bool `obfs:"v2ray-http-upgrade,omitempty"`
|
||||
V2rayHttpUpgradeFastOpen bool `obfs:"v2ray-http-upgrade-fast-open,omitempty"`
|
||||
}
|
||||
|
||||
type shadowTLSOption struct {
|
||||
|
@ -260,11 +261,12 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
|
|||
}
|
||||
obfsMode = opts.Mode
|
||||
v2rayOption = &v2rayObfs.Option{
|
||||
Host: opts.Host,
|
||||
Path: opts.Path,
|
||||
Headers: opts.Headers,
|
||||
Mux: opts.Mux,
|
||||
V2rayHttpUpgrade: opts.V2rayHttpUpgrade,
|
||||
Host: opts.Host,
|
||||
Path: opts.Path,
|
||||
Headers: opts.Headers,
|
||||
Mux: opts.Mux,
|
||||
V2rayHttpUpgrade: opts.V2rayHttpUpgrade,
|
||||
V2rayHttpUpgradeFastOpen: opts.V2rayHttpUpgradeFastOpen,
|
||||
}
|
||||
|
||||
if opts.TLS {
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/metacubex/mihomo/component/proxydialer"
|
||||
"github.com/metacubex/mihomo/component/resolver"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/log"
|
||||
|
||||
mux "github.com/sagernet/sing-mux"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
|
@ -25,14 +26,21 @@ type SingMux struct {
|
|||
}
|
||||
|
||||
type SingMuxOption struct {
|
||||
Enabled bool `proxy:"enabled,omitempty"`
|
||||
Protocol string `proxy:"protocol,omitempty"`
|
||||
MaxConnections int `proxy:"max-connections,omitempty"`
|
||||
MinStreams int `proxy:"min-streams,omitempty"`
|
||||
MaxStreams int `proxy:"max-streams,omitempty"`
|
||||
Padding bool `proxy:"padding,omitempty"`
|
||||
Statistic bool `proxy:"statistic,omitempty"`
|
||||
OnlyTcp bool `proxy:"only-tcp,omitempty"`
|
||||
Enabled bool `proxy:"enabled,omitempty"`
|
||||
Protocol string `proxy:"protocol,omitempty"`
|
||||
MaxConnections int `proxy:"max-connections,omitempty"`
|
||||
MinStreams int `proxy:"min-streams,omitempty"`
|
||||
MaxStreams int `proxy:"max-streams,omitempty"`
|
||||
Padding bool `proxy:"padding,omitempty"`
|
||||
Statistic bool `proxy:"statistic,omitempty"`
|
||||
OnlyTcp bool `proxy:"only-tcp,omitempty"`
|
||||
BrutalOpts BrutalOption `proxy:"brutal-opts,omitempty"`
|
||||
}
|
||||
|
||||
type BrutalOption struct {
|
||||
Enabled bool `proxy:"enabled,omitempty"`
|
||||
Up string `proxy:"up,omitempty"`
|
||||
Down string `proxy:"down,omitempty"`
|
||||
}
|
||||
|
||||
type ProxyBase interface {
|
||||
|
@ -94,14 +102,23 @@ func closeSingMux(s *SingMux) {
|
|||
}
|
||||
|
||||
func NewSingMux(option SingMuxOption, proxy C.ProxyAdapter, base ProxyBase) (C.ProxyAdapter, error) {
|
||||
// TODO
|
||||
// "TCP Brutal is only supported on Linux-based systems"
|
||||
|
||||
singDialer := proxydialer.NewSingDialer(proxy, dialer.NewDialer(), option.Statistic)
|
||||
client, err := mux.NewClient(mux.Options{
|
||||
Dialer: singDialer,
|
||||
Logger: log.SingLogger,
|
||||
Protocol: option.Protocol,
|
||||
MaxConnections: option.MaxConnections,
|
||||
MinStreams: option.MinStreams,
|
||||
MaxStreams: option.MaxStreams,
|
||||
Padding: option.Padding,
|
||||
Brutal: mux.BrutalOptions{
|
||||
Enabled: option.BrutalOpts.Enabled,
|
||||
SendBPS: StringToBps(option.BrutalOpts.Up),
|
||||
ReceiveBPS: StringToBps(option.BrutalOpts.Down),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -53,10 +53,12 @@ func (t *Trojan) plainStream(ctx context.Context, c net.Conn) (net.Conn, error)
|
|||
if t.option.Network == "ws" {
|
||||
host, port, _ := net.SplitHostPort(t.addr)
|
||||
wsOpts := &trojan.WebsocketOption{
|
||||
Host: host,
|
||||
Port: port,
|
||||
Path: t.option.WSOpts.Path,
|
||||
V2rayHttpUpgrade: t.option.WSOpts.V2rayHttpUpgrade,
|
||||
Host: host,
|
||||
Port: port,
|
||||
Path: t.option.WSOpts.Path,
|
||||
V2rayHttpUpgrade: t.option.WSOpts.V2rayHttpUpgrade,
|
||||
V2rayHttpUpgradeFastOpen: t.option.WSOpts.V2rayHttpUpgradeFastOpen,
|
||||
Headers: http.Header{},
|
||||
}
|
||||
|
||||
if t.option.SNI != "" {
|
||||
|
@ -64,11 +66,9 @@ func (t *Trojan) plainStream(ctx context.Context, c net.Conn) (net.Conn, error)
|
|||
}
|
||||
|
||||
if len(t.option.WSOpts.Headers) != 0 {
|
||||
header := http.Header{}
|
||||
for key, value := range t.option.WSOpts.Headers {
|
||||
header.Add(key, value)
|
||||
wsOpts.Headers.Add(key, value)
|
||||
}
|
||||
wsOpts.Headers = header
|
||||
}
|
||||
|
||||
return t.instance.StreamWebsocketConn(ctx, c, wsOpts)
|
||||
|
|
|
@ -88,14 +88,15 @@ func (v *Vless) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.M
|
|||
case "ws":
|
||||
host, port, _ := net.SplitHostPort(v.addr)
|
||||
wsOpts := &vmess.WebsocketConfig{
|
||||
Host: host,
|
||||
Port: port,
|
||||
Path: v.option.WSOpts.Path,
|
||||
MaxEarlyData: v.option.WSOpts.MaxEarlyData,
|
||||
EarlyDataHeaderName: v.option.WSOpts.EarlyDataHeaderName,
|
||||
V2rayHttpUpgrade: v.option.WSOpts.V2rayHttpUpgrade,
|
||||
ClientFingerprint: v.option.ClientFingerprint,
|
||||
Headers: http.Header{},
|
||||
Host: host,
|
||||
Port: port,
|
||||
Path: v.option.WSOpts.Path,
|
||||
MaxEarlyData: v.option.WSOpts.MaxEarlyData,
|
||||
EarlyDataHeaderName: v.option.WSOpts.EarlyDataHeaderName,
|
||||
V2rayHttpUpgrade: v.option.WSOpts.V2rayHttpUpgrade,
|
||||
V2rayHttpUpgradeFastOpen: v.option.WSOpts.V2rayHttpUpgradeFastOpen,
|
||||
ClientFingerprint: v.option.ClientFingerprint,
|
||||
Headers: http.Header{},
|
||||
}
|
||||
|
||||
if len(v.option.WSOpts.Headers) != 0 {
|
||||
|
|
|
@ -87,11 +87,12 @@ type GrpcOptions struct {
|
|||
}
|
||||
|
||||
type WSOptions struct {
|
||||
Path string `proxy:"path,omitempty"`
|
||||
Headers map[string]string `proxy:"headers,omitempty"`
|
||||
MaxEarlyData int `proxy:"max-early-data,omitempty"`
|
||||
EarlyDataHeaderName string `proxy:"early-data-header-name,omitempty"`
|
||||
V2rayHttpUpgrade bool `proxy:"v2ray-http-upgrade,omitempty"`
|
||||
Path string `proxy:"path,omitempty"`
|
||||
Headers map[string]string `proxy:"headers,omitempty"`
|
||||
MaxEarlyData int `proxy:"max-early-data,omitempty"`
|
||||
EarlyDataHeaderName string `proxy:"early-data-header-name,omitempty"`
|
||||
V2rayHttpUpgrade bool `proxy:"v2ray-http-upgrade,omitempty"`
|
||||
V2rayHttpUpgradeFastOpen bool `proxy:"v2ray-http-upgrade-fast-open,omitempty"`
|
||||
}
|
||||
|
||||
// StreamConnContext implements C.ProxyAdapter
|
||||
|
@ -106,14 +107,15 @@ func (v *Vmess) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.M
|
|||
case "ws":
|
||||
host, port, _ := net.SplitHostPort(v.addr)
|
||||
wsOpts := &mihomoVMess.WebsocketConfig{
|
||||
Host: host,
|
||||
Port: port,
|
||||
Path: v.option.WSOpts.Path,
|
||||
MaxEarlyData: v.option.WSOpts.MaxEarlyData,
|
||||
EarlyDataHeaderName: v.option.WSOpts.EarlyDataHeaderName,
|
||||
V2rayHttpUpgrade: v.option.WSOpts.V2rayHttpUpgrade,
|
||||
ClientFingerprint: v.option.ClientFingerprint,
|
||||
Headers: http.Header{},
|
||||
Host: host,
|
||||
Port: port,
|
||||
Path: v.option.WSOpts.Path,
|
||||
MaxEarlyData: v.option.WSOpts.MaxEarlyData,
|
||||
EarlyDataHeaderName: v.option.WSOpts.EarlyDataHeaderName,
|
||||
V2rayHttpUpgrade: v.option.WSOpts.V2rayHttpUpgrade,
|
||||
V2rayHttpUpgradeFastOpen: v.option.WSOpts.V2rayHttpUpgradeFastOpen,
|
||||
ClientFingerprint: v.option.ClientFingerprint,
|
||||
Headers: http.Header{},
|
||||
}
|
||||
|
||||
if len(v.option.WSOpts.Headers) != 0 {
|
||||
|
|
|
@ -84,11 +84,11 @@ 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,
|
||||
"testUrl": f.testUrl,
|
||||
"expectedStatus": f.expectedStatus,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -102,13 +102,11 @@ 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) {
|
||||
return proxy
|
||||
}
|
||||
} else {
|
||||
if proxy.Name() == f.selected {
|
||||
// if proxy.Alive() {
|
||||
if proxy.AliveForTestUrl(f.testUrl) {
|
||||
return proxy
|
||||
} else {
|
||||
|
@ -135,12 +133,11 @@ func (f *Fallback) Set(name string) error {
|
|||
}
|
||||
|
||||
f.selected = name
|
||||
// if !p.Alive() {
|
||||
if !p.AliveForTestUrl(f.testUrl) {
|
||||
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, expectedStatus)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -202,7 +202,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, expectedStatus)
|
||||
if err == nil {
|
||||
lock.Lock()
|
||||
mp[proxy.Name()] = delay
|
||||
|
@ -222,7 +222,7 @@ func (gb *GroupBase) URLTest(ctx context.Context, url string, expectedStatus uti
|
|||
}
|
||||
|
||||
func (gb *GroupBase) onDialFailed(adapterType C.AdapterType, err error) {
|
||||
if adapterType == C.Direct || adapterType == C.Compatible || adapterType == C.Reject || adapterType == C.Pass {
|
||||
if adapterType == C.Direct || adapterType == C.Compatible || adapterType == C.Reject || adapterType == C.Pass || adapterType == C.RejectDrop {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -10,8 +10,8 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/metacubex/mihomo/adapter/outbound"
|
||||
"github.com/metacubex/mihomo/common/cache"
|
||||
"github.com/metacubex/mihomo/common/callback"
|
||||
"github.com/metacubex/mihomo/common/lru"
|
||||
N "github.com/metacubex/mihomo/common/net"
|
||||
"github.com/metacubex/mihomo/common/utils"
|
||||
"github.com/metacubex/mihomo/component/dialer"
|
||||
|
@ -190,9 +190,9 @@ func strategyConsistentHashing(url string) strategyFn {
|
|||
func strategyStickySessions(url string) strategyFn {
|
||||
ttl := time.Minute * 10
|
||||
maxRetry := 5
|
||||
lruCache := cache.New[uint64, int](
|
||||
cache.WithAge[uint64, int](int64(ttl.Seconds())),
|
||||
cache.WithSize[uint64, int](1000))
|
||||
lruCache := lru.New[uint64, int](
|
||||
lru.WithAge[uint64, int](int64(ttl.Seconds())),
|
||||
lru.WithSize[uint64, int](1000))
|
||||
return func(proxies []C.Proxy, metadata *C.Metadata, touch bool) C.Proxy {
|
||||
key := utils.MapHash(getKeyWithSrcAndDst(metadata))
|
||||
length := len(proxies)
|
||||
|
|
|
@ -22,18 +22,19 @@ var (
|
|||
|
||||
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"`
|
||||
ExpectedStatus string `group:"expected-status,omitempty"`
|
||||
IncludeAllProviders bool `group:"include-all-providers,omitempty"`
|
||||
}
|
||||
|
||||
func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, providersMap map[string]types.ProxyProvider) (C.ProxyAdapter, error) {
|
||||
|
@ -54,7 +55,18 @@ func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, provide
|
|||
|
||||
providers := []types.ProxyProvider{}
|
||||
|
||||
if len(groupOption.Proxies) == 0 && len(groupOption.Use) == 0 {
|
||||
var GroupUse []string
|
||||
visited := make(map[string]bool)
|
||||
if groupOption.IncludeAllProviders {
|
||||
for name := range provider.ProxyProviderName {
|
||||
GroupUse = append(GroupUse, name)
|
||||
visited[name] = true
|
||||
}
|
||||
} else {
|
||||
GroupUse = groupOption.Use
|
||||
}
|
||||
|
||||
if len(groupOption.Proxies) == 0 && len(GroupUse) == 0 {
|
||||
return nil, fmt.Errorf("%s: %w", groupName, errMissProxy)
|
||||
}
|
||||
|
||||
|
@ -80,24 +92,20 @@ func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, provide
|
|||
return nil, fmt.Errorf("%s: %w", groupName, errDuplicateProvider)
|
||||
}
|
||||
|
||||
var url string
|
||||
var interval uint
|
||||
|
||||
// select don't need health check
|
||||
if groupOption.Type != "select" && groupOption.Type != "relay" {
|
||||
if groupOption.URL == "" {
|
||||
groupOption.URL = "https://cp.cloudflare.com/generate_204"
|
||||
groupOption.URL = C.DefaultTestURL
|
||||
testUrl = groupOption.URL
|
||||
}
|
||||
|
||||
if groupOption.Interval == 0 {
|
||||
groupOption.Interval = 300
|
||||
}
|
||||
|
||||
url = groupOption.URL
|
||||
interval = uint(groupOption.Interval)
|
||||
}
|
||||
|
||||
hc := provider.NewHealthCheck(ps, url, interval, true, expectedStatus)
|
||||
hc := provider.NewHealthCheck(ps, groupOption.URL, uint(groupOption.Interval), true, expectedStatus)
|
||||
|
||||
pd, err := provider.NewCompatibleProvider(groupName, ps, hc)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: %w", groupName, err)
|
||||
|
@ -107,8 +115,8 @@ func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, provide
|
|||
providersMap[groupName] = pd
|
||||
}
|
||||
|
||||
if len(groupOption.Use) != 0 {
|
||||
list, err := getProviders(providersMap, groupOption.Use)
|
||||
if len(GroupUse) != 0 {
|
||||
list, err := getProviders(providersMap, GroupUse)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: %w", groupName, err)
|
||||
}
|
||||
|
|
64
adapter/outboundgroup/patch_android.go
Normal file
64
adapter/outboundgroup/patch_android.go
Normal file
|
@ -0,0 +1,64 @@
|
|||
//go:build android && cmfa
|
||||
|
||||
package outboundgroup
|
||||
|
||||
import (
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/constant/provider"
|
||||
)
|
||||
|
||||
type ProxyGroup interface {
|
||||
C.ProxyAdapter
|
||||
|
||||
Providers() []provider.ProxyProvider
|
||||
Proxies() []C.Proxy
|
||||
Now() string
|
||||
}
|
||||
|
||||
func (f *Fallback) Providers() []provider.ProxyProvider {
|
||||
return f.providers
|
||||
}
|
||||
|
||||
func (lb *LoadBalance) Providers() []provider.ProxyProvider {
|
||||
return lb.providers
|
||||
}
|
||||
|
||||
func (f *Fallback) Proxies() []C.Proxy {
|
||||
return f.GetProxies(false)
|
||||
}
|
||||
|
||||
func (lb *LoadBalance) Proxies() []C.Proxy {
|
||||
return lb.GetProxies(false)
|
||||
}
|
||||
|
||||
func (lb *LoadBalance) Now() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (r *Relay) Providers() []provider.ProxyProvider {
|
||||
return r.providers
|
||||
}
|
||||
|
||||
func (r *Relay) Proxies() []C.Proxy {
|
||||
return r.GetProxies(false)
|
||||
}
|
||||
|
||||
func (r *Relay) Now() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (s *Selector) Providers() []provider.ProxyProvider {
|
||||
return s.providers
|
||||
}
|
||||
|
||||
func (s *Selector) Proxies() []C.Proxy {
|
||||
return s.GetProxies(false)
|
||||
}
|
||||
|
||||
func (u *URLTest) Providers() []provider.ProxyProvider {
|
||||
return u.providers
|
||||
}
|
||||
|
||||
func (u *URLTest) Proxies() []C.Proxy {
|
||||
return u.GetProxies(false)
|
||||
}
|
|
@ -101,7 +101,7 @@ func (u *URLTest) fast(touch bool) C.Proxy {
|
|||
proxies := u.GetProxies(touch)
|
||||
if u.selected != "" {
|
||||
for _, proxy := range proxies {
|
||||
if !proxy.Alive() {
|
||||
if !proxy.AliveForTestUrl(u.testUrl) {
|
||||
continue
|
||||
}
|
||||
if proxy.Name() == u.selected {
|
||||
|
@ -113,7 +113,6 @@ 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)
|
||||
fastNotExist := true
|
||||
|
||||
|
@ -122,12 +121,10 @@ func (u *URLTest) fast(touch bool) C.Proxy {
|
|||
fastNotExist = false
|
||||
}
|
||||
|
||||
// if !proxy.Alive() {
|
||||
if !proxy.AliveForTestUrl(u.testUrl) {
|
||||
continue
|
||||
}
|
||||
|
||||
// delay := proxy.LastDelay()
|
||||
delay := proxy.LastDelayForTestUrl(u.testUrl)
|
||||
if delay < min {
|
||||
fast = proxy
|
||||
|
@ -136,7 +133,6 @@ 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 {
|
||||
u.fastNode = fast
|
||||
}
|
||||
|
@ -169,11 +165,11 @@ 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,
|
||||
"testUrl": u.testUrl,
|
||||
"expectedStatus": u.expectedStatus,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -104,12 +104,6 @@ func (hc *HealthCheck) registerHealthCheckTask(url string, expectedStatus utils.
|
|||
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
|
||||
|
@ -148,6 +142,10 @@ func (hc *HealthCheck) stop() {
|
|||
}
|
||||
|
||||
func (hc *HealthCheck) check() {
|
||||
if len(hc.proxies) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
_, _, _ = hc.singleDo.Do(func() (struct{}, error) {
|
||||
id := utils.NewUUIDV4().String()
|
||||
log.Debugln("Start New Health Checking {%s}", id)
|
||||
|
@ -177,13 +175,8 @@ func (hc *HealthCheck) execute(b *batch.Batch[bool], url, uid string, option *ex
|
|||
}
|
||||
|
||||
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))
|
||||
|
@ -208,7 +201,7 @@ func (hc *HealthCheck) execute(b *batch.Batch[bool], url, uid string, option *ex
|
|||
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)
|
||||
_, _ = p.URLTest(ctx, url, expectedStatus)
|
||||
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
|
||||
})
|
||||
|
@ -223,6 +216,7 @@ func NewHealthCheck(proxies []C.Proxy, url string, interval uint, lazy bool, exp
|
|||
if len(url) == 0 {
|
||||
interval = 0
|
||||
expectedStatus = nil
|
||||
url = C.DefaultTestURL
|
||||
}
|
||||
|
||||
return &HealthCheck{
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/metacubex/mihomo/common/utils"
|
||||
"github.com/metacubex/mihomo/component/resource"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/constant/features"
|
||||
types "github.com/metacubex/mihomo/constant/provider"
|
||||
)
|
||||
|
||||
|
@ -25,16 +26,26 @@ type healthCheckSchema struct {
|
|||
ExpectedStatus string `provider:"expected-status,omitempty"`
|
||||
}
|
||||
|
||||
type OverrideSchema struct {
|
||||
UDP *bool `provider:"udp,omitempty"`
|
||||
Up *string `provider:"up,omitempty"`
|
||||
Down *string `provider:"down,omitempty"`
|
||||
DialerProxy *string `provider:"dialer-proxy,omitempty"`
|
||||
SkipCertVerify *bool `provider:"skip-cert-verify,omitempty"`
|
||||
}
|
||||
|
||||
type proxyProviderSchema struct {
|
||||
Type string `provider:"type"`
|
||||
Path string `provider:"path,omitempty"`
|
||||
URL string `provider:"url,omitempty"`
|
||||
Interval int `provider:"interval,omitempty"`
|
||||
Filter string `provider:"filter,omitempty"`
|
||||
ExcludeFilter string `provider:"exclude-filter,omitempty"`
|
||||
ExcludeType string `provider:"exclude-type,omitempty"`
|
||||
DialerProxy string `provider:"dialer-proxy,omitempty"`
|
||||
HealthCheck healthCheckSchema `provider:"health-check,omitempty"`
|
||||
Type string `provider:"type"`
|
||||
Path string `provider:"path,omitempty"`
|
||||
URL string `provider:"url,omitempty"`
|
||||
Interval int `provider:"interval,omitempty"`
|
||||
Filter string `provider:"filter,omitempty"`
|
||||
ExcludeFilter string `provider:"exclude-filter,omitempty"`
|
||||
ExcludeType string `provider:"exclude-type,omitempty"`
|
||||
DialerProxy string `provider:"dialer-proxy,omitempty"`
|
||||
|
||||
HealthCheck healthCheckSchema `provider:"health-check,omitempty"`
|
||||
Override OverrideSchema `provider:"override,omitempty"`
|
||||
}
|
||||
|
||||
func ParseProxyProvider(name string, mapping map[string]any) (types.ProxyProvider, error) {
|
||||
|
@ -68,7 +79,7 @@ func ParseProxyProvider(name string, mapping map[string]any) (types.ProxyProvide
|
|||
case "http":
|
||||
if schema.Path != "" {
|
||||
path := C.Path.Resolve(schema.Path)
|
||||
if !C.Path.IsSafePath(path) {
|
||||
if !features.CMFA && !C.Path.IsSafePath(path) {
|
||||
return nil, fmt.Errorf("%w: %s", errSubPath, path)
|
||||
}
|
||||
vehicle = resource.NewHTTPVehicle(schema.URL, path)
|
||||
|
@ -85,6 +96,7 @@ func ParseProxyProvider(name string, mapping map[string]any) (types.ProxyProvide
|
|||
excludeFilter := schema.ExcludeFilter
|
||||
excludeType := schema.ExcludeType
|
||||
dialerProxy := schema.DialerProxy
|
||||
override := schema.Override
|
||||
|
||||
return NewProxySetProvider(name, interval, filter, excludeFilter, excludeType, dialerProxy, vehicle, hc)
|
||||
return NewProxySetProvider(name, interval, filter, excludeFilter, excludeType, dialerProxy, override, vehicle, hc)
|
||||
}
|
||||
|
|
36
adapter/provider/patch_android.go
Normal file
36
adapter/provider/patch_android.go
Normal file
|
@ -0,0 +1,36 @@
|
|||
//go:build android && cmfa
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
suspended bool
|
||||
)
|
||||
|
||||
type UpdatableProvider interface {
|
||||
UpdatedAt() time.Time
|
||||
}
|
||||
|
||||
func (pp *proxySetProvider) UpdatedAt() time.Time {
|
||||
return pp.Fetcher.UpdatedAt
|
||||
}
|
||||
|
||||
func (pp *proxySetProvider) Close() error {
|
||||
pp.healthCheck.close()
|
||||
pp.Fetcher.Destroy()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cp *compatibleProvider) Close() error {
|
||||
cp.healthCheck.close()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Suspend(s bool) {
|
||||
suspended = s
|
||||
}
|
|
@ -24,6 +24,8 @@ import (
|
|||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
var ProxyProviderName = make(map[string]struct{})
|
||||
|
||||
const (
|
||||
ReservedName = "default"
|
||||
)
|
||||
|
@ -46,12 +48,18 @@ type proxySetProvider struct {
|
|||
}
|
||||
|
||||
func (pp *proxySetProvider) MarshalJSON() ([]byte, error) {
|
||||
expectedStatus := "*"
|
||||
if pp.healthCheck.expectedStatus != nil {
|
||||
expectedStatus = pp.healthCheck.expectedStatus.ToString()
|
||||
}
|
||||
|
||||
return json.Marshal(map[string]any{
|
||||
"name": pp.Name(),
|
||||
"type": pp.Type().String(),
|
||||
"vehicleType": pp.VehicleType().String(),
|
||||
"proxies": pp.Proxies(),
|
||||
"testUrl": pp.healthCheck.url,
|
||||
"expectedStatus": expectedStatus,
|
||||
"updatedAt": pp.UpdatedAt,
|
||||
"subscriptionInfo": pp.subscriptionInfo,
|
||||
})
|
||||
|
@ -120,7 +128,7 @@ func (pp *proxySetProvider) getSubscriptionInfo() {
|
|||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*90)
|
||||
defer cancel()
|
||||
resp, err := mihomoHttp.HttpRequest(ctx, pp.Vehicle().(*resource.HTTPVehicle).Url(),
|
||||
http.MethodGet, http.Header{"User-Agent": {"mihomo"}}, nil)
|
||||
http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -163,7 +171,7 @@ func stopProxyProvider(pd *ProxySetProvider) {
|
|||
_ = pd.Fetcher.Destroy()
|
||||
}
|
||||
|
||||
func NewProxySetProvider(name string, interval time.Duration, filter string, excludeFilter string, excludeType string, dialerProxy string, vehicle types.Vehicle, hc *HealthCheck) (*ProxySetProvider, error) {
|
||||
func NewProxySetProvider(name string, interval time.Duration, filter string, excludeFilter string, excludeType string, dialerProxy string, override OverrideSchema, vehicle types.Vehicle, hc *HealthCheck) (*ProxySetProvider, error) {
|
||||
excludeFilterReg, err := regexp2.Compile(excludeFilter, 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid excludeFilter regex: %w", err)
|
||||
|
@ -191,8 +199,9 @@ func NewProxySetProvider(name string, interval time.Duration, filter string, exc
|
|||
healthCheck: hc,
|
||||
}
|
||||
|
||||
fetcher := resource.NewFetcher[[]C.Proxy](name, interval, vehicle, proxiesParseAndFilter(filter, excludeFilter, excludeTypeArray, filterRegs, excludeFilterReg, dialerProxy), proxiesOnUpdate(pd))
|
||||
fetcher := resource.NewFetcher[[]C.Proxy](name, interval, vehicle, proxiesParseAndFilter(filter, excludeFilter, excludeTypeArray, filterRegs, excludeFilterReg, dialerProxy, override), proxiesOnUpdate(pd))
|
||||
pd.Fetcher = fetcher
|
||||
ProxyProviderName[name] = struct{}{}
|
||||
wrapper := &ProxySetProvider{pd}
|
||||
runtime.SetFinalizer(wrapper, stopProxyProvider)
|
||||
return wrapper, nil
|
||||
|
@ -211,12 +220,18 @@ type compatibleProvider struct {
|
|||
}
|
||||
|
||||
func (cp *compatibleProvider) MarshalJSON() ([]byte, error) {
|
||||
expectedStatus := "*"
|
||||
if cp.healthCheck.expectedStatus != nil {
|
||||
expectedStatus = cp.healthCheck.expectedStatus.ToString()
|
||||
}
|
||||
|
||||
return json.Marshal(map[string]any{
|
||||
"name": cp.Name(),
|
||||
"type": cp.Type().String(),
|
||||
"vehicleType": cp.VehicleType().String(),
|
||||
"proxies": cp.Proxies(),
|
||||
"testUrl": cp.healthCheck.url,
|
||||
"name": cp.Name(),
|
||||
"type": cp.Type().String(),
|
||||
"vehicleType": cp.VehicleType().String(),
|
||||
"proxies": cp.Proxies(),
|
||||
"testUrl": cp.healthCheck.url,
|
||||
"expectedStatus": expectedStatus,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -292,7 +307,7 @@ func proxiesOnUpdate(pd *proxySetProvider) func([]C.Proxy) {
|
|||
}
|
||||
}
|
||||
|
||||
func proxiesParseAndFilter(filter string, excludeFilter string, excludeTypeArray []string, filterRegs []*regexp2.Regexp, excludeFilterReg *regexp2.Regexp, dialerProxy string) resource.Parser[[]C.Proxy] {
|
||||
func proxiesParseAndFilter(filter string, excludeFilter string, excludeTypeArray []string, filterRegs []*regexp2.Regexp, excludeFilterReg *regexp2.Regexp, dialerProxy string, override OverrideSchema) resource.Parser[[]C.Proxy] {
|
||||
return func(buf []byte) ([]C.Proxy, error) {
|
||||
schema := &ProxySchema{}
|
||||
|
||||
|
@ -355,13 +370,32 @@ func proxiesParseAndFilter(filter string, excludeFilter string, excludeTypeArray
|
|||
if _, ok := proxiesSet[name]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(dialerProxy) > 0 {
|
||||
mapping["dialer-proxy"] = dialerProxy
|
||||
}
|
||||
|
||||
if override.UDP != nil {
|
||||
mapping["udp"] = *override.UDP
|
||||
}
|
||||
if override.Up != nil {
|
||||
mapping["up"] = *override.Up
|
||||
}
|
||||
if override.Down != nil {
|
||||
mapping["down"] = *override.Down
|
||||
}
|
||||
if override.DialerProxy != nil {
|
||||
mapping["dialer-proxy"] = *override.DialerProxy
|
||||
}
|
||||
if override.SkipCertVerify != nil {
|
||||
mapping["skip-cert-verify"] = *override.SkipCertVerify
|
||||
}
|
||||
|
||||
proxy, err := adapter.ParseProxy(mapping)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("proxy %d error: %w", idx, err)
|
||||
}
|
||||
|
||||
proxiesSet[name] = struct{}{}
|
||||
proxies = append(proxies, proxy)
|
||||
}
|
||||
|
|
|
@ -25,7 +25,11 @@ func NewSubscriptionInfo(userinfo string) (si *SubscriptionInfo, err error) {
|
|||
case "total":
|
||||
si.Total, err = strconv.ParseInt(value, 10, 64)
|
||||
case "expire":
|
||||
si.Expire, err = strconv.ParseInt(value, 10, 64)
|
||||
if value == "" {
|
||||
si.Expire = 0
|
||||
} else {
|
||||
si.Expire, err = strconv.ParseInt(value, 10, 64)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
|
|
229
common/arc/arc.go
Normal file
229
common/arc/arc.go
Normal file
|
@ -0,0 +1,229 @@
|
|||
package arc
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
list "github.com/bahlo/generic-list-go"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
//modify from https://github.com/alexanderGugel/arc
|
||||
|
||||
// Option is part of Functional Options Pattern
|
||||
type Option[K comparable, V any] func(*ARC[K, V])
|
||||
|
||||
func WithSize[K comparable, V any](maxSize int) Option[K, V] {
|
||||
return func(a *ARC[K, V]) {
|
||||
a.c = maxSize
|
||||
}
|
||||
}
|
||||
|
||||
type ARC[K comparable, V any] struct {
|
||||
p int
|
||||
c int
|
||||
t1 *list.List[*entry[K, V]]
|
||||
b1 *list.List[*entry[K, V]]
|
||||
t2 *list.List[*entry[K, V]]
|
||||
b2 *list.List[*entry[K, V]]
|
||||
mutex sync.Mutex
|
||||
len int
|
||||
cache map[K]*entry[K, V]
|
||||
staleReturn bool
|
||||
}
|
||||
|
||||
// New returns a new Adaptive Replacement Cache (ARC).
|
||||
func New[K comparable, V any](options ...Option[K, V]) *ARC[K, V] {
|
||||
arc := &ARC[K, V]{
|
||||
p: 0,
|
||||
t1: list.New[*entry[K, V]](),
|
||||
b1: list.New[*entry[K, V]](),
|
||||
t2: list.New[*entry[K, V]](),
|
||||
b2: list.New[*entry[K, V]](),
|
||||
len: 0,
|
||||
cache: make(map[K]*entry[K, V]),
|
||||
}
|
||||
|
||||
for _, option := range options {
|
||||
option(arc)
|
||||
}
|
||||
return arc
|
||||
}
|
||||
|
||||
// Set inserts a new key-value pair into the cache.
|
||||
// This optimizes future access to this entry (side effect).
|
||||
func (a *ARC[K, V]) Set(key K, value V) {
|
||||
a.mutex.Lock()
|
||||
defer a.mutex.Unlock()
|
||||
|
||||
a.set(key, value)
|
||||
}
|
||||
|
||||
func (a *ARC[K, V]) set(key K, value V) {
|
||||
a.setWithExpire(key, value, time.Unix(0, 0))
|
||||
}
|
||||
|
||||
// SetWithExpire stores any representation of a response for a given key and given expires.
|
||||
// The expires time will round to second.
|
||||
func (a *ARC[K, V]) SetWithExpire(key K, value V, expires time.Time) {
|
||||
a.mutex.Lock()
|
||||
defer a.mutex.Unlock()
|
||||
|
||||
a.setWithExpire(key, value, expires)
|
||||
}
|
||||
|
||||
func (a *ARC[K, V]) setWithExpire(key K, value V, expires time.Time) {
|
||||
ent, ok := a.cache[key]
|
||||
if ok != true {
|
||||
a.len++
|
||||
ent := &entry[K, V]{key: key, value: value, ghost: false, expires: expires.Unix()}
|
||||
a.req(ent)
|
||||
a.cache[key] = ent
|
||||
} else {
|
||||
if ent.ghost {
|
||||
a.len++
|
||||
}
|
||||
ent.value = value
|
||||
ent.ghost = false
|
||||
ent.expires = expires.Unix()
|
||||
a.req(ent)
|
||||
}
|
||||
}
|
||||
|
||||
// Get retrieves a previously via Set inserted entry.
|
||||
// This optimizes future access to this entry (side effect).
|
||||
func (a *ARC[K, V]) Get(key K) (value V, ok bool) {
|
||||
a.mutex.Lock()
|
||||
defer a.mutex.Unlock()
|
||||
|
||||
ent, ok := a.get(key)
|
||||
if ok {
|
||||
return ent.value, true
|
||||
}
|
||||
return lo.Empty[V](), false
|
||||
}
|
||||
|
||||
func (a *ARC[K, V]) get(key K) (e *entry[K, V], ok bool) {
|
||||
ent, ok := a.cache[key]
|
||||
if ok {
|
||||
a.req(ent)
|
||||
return ent, !ent.ghost
|
||||
}
|
||||
return ent, false
|
||||
}
|
||||
|
||||
// GetWithExpire returns any representation of a cached response,
|
||||
// a time.Time Give expected expires,
|
||||
// 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 (a *ARC[K, V]) GetWithExpire(key K) (V, time.Time, bool) {
|
||||
a.mutex.Lock()
|
||||
defer a.mutex.Unlock()
|
||||
|
||||
ent, ok := a.get(key)
|
||||
if !ok {
|
||||
return lo.Empty[V](), time.Time{}, false
|
||||
}
|
||||
|
||||
return ent.value, time.Unix(ent.expires, 0), true
|
||||
}
|
||||
|
||||
// Len determines the number of currently cached entries.
|
||||
// This method is side-effect free in the sense that it does not attempt to optimize random cache access.
|
||||
func (a *ARC[K, V]) Len() int {
|
||||
a.mutex.Lock()
|
||||
defer a.mutex.Unlock()
|
||||
|
||||
return a.len
|
||||
}
|
||||
|
||||
func (a *ARC[K, V]) req(ent *entry[K, V]) {
|
||||
if ent.ll == a.t1 || ent.ll == a.t2 {
|
||||
// Case I
|
||||
ent.setMRU(a.t2)
|
||||
} else if ent.ll == a.b1 {
|
||||
// Case II
|
||||
// Cache Miss in t1 and t2
|
||||
|
||||
// Adaptation
|
||||
var d int
|
||||
if a.b1.Len() >= a.b2.Len() {
|
||||
d = 1
|
||||
} else {
|
||||
d = a.b2.Len() / a.b1.Len()
|
||||
}
|
||||
|
||||
// a.p = min(a.p+d, a.c)
|
||||
a.p = a.p + d
|
||||
if a.c < a.p {
|
||||
a.p = a.c
|
||||
}
|
||||
|
||||
a.replace(ent)
|
||||
ent.setMRU(a.t2)
|
||||
} else if ent.ll == a.b2 {
|
||||
// Case III
|
||||
// Cache Miss in t1 and t2
|
||||
|
||||
// Adaptation
|
||||
var d int
|
||||
if a.b2.Len() >= a.b1.Len() {
|
||||
d = 1
|
||||
} else {
|
||||
d = a.b1.Len() / a.b2.Len()
|
||||
}
|
||||
//a.p = max(a.p-d, 0)
|
||||
a.p = a.p - d
|
||||
if a.p < 0 {
|
||||
a.p = 0
|
||||
}
|
||||
|
||||
a.replace(ent)
|
||||
ent.setMRU(a.t2)
|
||||
} else if ent.ll == nil {
|
||||
// Case IV
|
||||
|
||||
if a.t1.Len()+a.b1.Len() == a.c {
|
||||
// Case A
|
||||
if a.t1.Len() < a.c {
|
||||
a.delLRU(a.b1)
|
||||
a.replace(ent)
|
||||
} else {
|
||||
a.delLRU(a.t1)
|
||||
}
|
||||
} else if a.t1.Len()+a.b1.Len() < a.c {
|
||||
// Case B
|
||||
if a.t1.Len()+a.t2.Len()+a.b1.Len()+a.b2.Len() >= a.c {
|
||||
if a.t1.Len()+a.t2.Len()+a.b1.Len()+a.b2.Len() == 2*a.c {
|
||||
a.delLRU(a.b2)
|
||||
}
|
||||
a.replace(ent)
|
||||
}
|
||||
}
|
||||
|
||||
ent.setMRU(a.t1)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *ARC[K, V]) delLRU(list *list.List[*entry[K, V]]) {
|
||||
lru := list.Back()
|
||||
list.Remove(lru)
|
||||
a.len--
|
||||
delete(a.cache, lru.Value.key)
|
||||
}
|
||||
|
||||
func (a *ARC[K, V]) replace(ent *entry[K, V]) {
|
||||
if a.t1.Len() > 0 && ((a.t1.Len() > a.p) || (ent.ll == a.b2 && a.t1.Len() == a.p)) {
|
||||
lru := a.t1.Back().Value
|
||||
lru.value = lo.Empty[V]()
|
||||
lru.ghost = true
|
||||
a.len--
|
||||
lru.setMRU(a.b1)
|
||||
} else {
|
||||
lru := a.t2.Back().Value
|
||||
lru.value = lo.Empty[V]()
|
||||
lru.ghost = true
|
||||
a.len--
|
||||
lru.setMRU(a.b2)
|
||||
}
|
||||
}
|
59
common/arc/arc_test.go
Normal file
59
common/arc/arc_test.go
Normal file
|
@ -0,0 +1,59 @@
|
|||
package arc
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestBasic(t *testing.T) {
|
||||
cache := New[string, string](WithSize[string, string](3))
|
||||
if cache.Len() != 0 {
|
||||
t.Error("Empty cache should have length 0")
|
||||
}
|
||||
|
||||
cache.Set("Hello", "World")
|
||||
if cache.Len() != 1 {
|
||||
t.Error("Cache should have length 1")
|
||||
}
|
||||
|
||||
var val interface{}
|
||||
var ok bool
|
||||
|
||||
if val, ok = cache.Get("Hello"); val != "World" || ok != true {
|
||||
t.Error("Didn't set \"Hello\" to \"World\"")
|
||||
}
|
||||
|
||||
cache.Set("Hello", "World1")
|
||||
if cache.Len() != 1 {
|
||||
t.Error("Inserting the same entry multiple times shouldn't increase cache size")
|
||||
}
|
||||
|
||||
if val, ok = cache.Get("Hello"); val != "World1" || ok != true {
|
||||
t.Error("Didn't update \"Hello\" to \"World1\"")
|
||||
}
|
||||
|
||||
cache.Set("Hallo", "Welt")
|
||||
if cache.Len() != 2 {
|
||||
t.Error("Inserting two different entries should result into lenght=2")
|
||||
}
|
||||
|
||||
if val, ok = cache.Get("Hallo"); val != "Welt" || ok != true {
|
||||
t.Error("Didn't set \"Hallo\" to \"Welt\"")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBasicReplace(t *testing.T) {
|
||||
cache := New[string, string](WithSize[string, string](3))
|
||||
|
||||
cache.Set("Hello", "Hallo")
|
||||
cache.Set("World", "Welt")
|
||||
cache.Get("World")
|
||||
cache.Set("Cache", "Cache")
|
||||
cache.Set("Replace", "Ersetzen")
|
||||
|
||||
value, ok := cache.Get("World")
|
||||
if !ok || value != "Welt" {
|
||||
t.Error("ARC should have replaced \"Hello\"")
|
||||
}
|
||||
|
||||
if cache.Len() != 3 {
|
||||
t.Error("ARC should have a maximum size of 3")
|
||||
}
|
||||
}
|
32
common/arc/entry.go
Normal file
32
common/arc/entry.go
Normal file
|
@ -0,0 +1,32 @@
|
|||
package arc
|
||||
|
||||
import (
|
||||
list "github.com/bahlo/generic-list-go"
|
||||
)
|
||||
|
||||
type entry[K comparable, V any] struct {
|
||||
key K
|
||||
value V
|
||||
ll *list.List[*entry[K, V]]
|
||||
el *list.Element[*entry[K, V]]
|
||||
ghost bool
|
||||
expires int64
|
||||
}
|
||||
|
||||
func (e *entry[K, V]) setLRU(list *list.List[*entry[K, V]]) {
|
||||
e.detach()
|
||||
e.ll = list
|
||||
e.el = e.ll.PushBack(e)
|
||||
}
|
||||
|
||||
func (e *entry[K, V]) setMRU(list *list.List[*entry[K, V]]) {
|
||||
e.detach()
|
||||
e.ll = list
|
||||
e.el = e.ll.PushFront(e)
|
||||
}
|
||||
|
||||
func (e *entry[K, V]) detach() {
|
||||
if e.ll != nil {
|
||||
e.ll.Remove(e.el)
|
||||
}
|
||||
}
|
|
@ -11,8 +11,8 @@ func DefaultValue[T any]() T {
|
|||
}
|
||||
|
||||
type TypedValue[T any] struct {
|
||||
value atomic.Value
|
||||
_ noCopy
|
||||
value atomic.Value
|
||||
}
|
||||
|
||||
func (t *TypedValue[T]) Load() T {
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
package collections
|
||||
|
||||
import "sync"
|
||||
|
||||
type (
|
||||
stack struct {
|
||||
top *node
|
||||
length int
|
||||
lock *sync.RWMutex
|
||||
}
|
||||
|
||||
node struct {
|
||||
value interface{}
|
||||
prev *node
|
||||
}
|
||||
)
|
||||
|
||||
// NewStack Create a new stack
|
||||
func NewStack() *stack {
|
||||
return &stack{nil, 0, &sync.RWMutex{}}
|
||||
}
|
||||
|
||||
// Len Return the number of items in the stack
|
||||
func (this *stack) Len() int {
|
||||
return this.length
|
||||
}
|
||||
|
||||
// Peek View the top item on the stack
|
||||
func (this *stack) Peek() interface{} {
|
||||
if this.length == 0 {
|
||||
return nil
|
||||
}
|
||||
return this.top.value
|
||||
}
|
||||
|
||||
// Pop the top item of the stack and return it
|
||||
func (this *stack) Pop() interface{} {
|
||||
this.lock.Lock()
|
||||
defer this.lock.Unlock()
|
||||
if this.length == 0 {
|
||||
return nil
|
||||
}
|
||||
n := this.top
|
||||
this.top = n.prev
|
||||
this.length--
|
||||
return n.value
|
||||
}
|
||||
|
||||
// Push a value onto the top of the stack
|
||||
func (this *stack) Push(value interface{}) {
|
||||
this.lock.Lock()
|
||||
defer this.lock.Unlock()
|
||||
n := &node{value, this.top}
|
||||
this.top = n
|
||||
this.length++
|
||||
}
|
|
@ -142,6 +142,8 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
|
|||
tuic["udp-relay-mode"] = udpRelayMode
|
||||
}
|
||||
|
||||
proxies = append(proxies, tuic)
|
||||
|
||||
case "trojan":
|
||||
urlTrojan, err := url.Parse(line)
|
||||
if err != nil {
|
||||
|
|
|
@ -1,235 +0,0 @@
|
|||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package list implements a doubly linked list.
|
||||
//
|
||||
// To iterate over a list (where l is a *List):
|
||||
//
|
||||
// for e := l.Front(); e != nil; e = e.Next() {
|
||||
// // do something with e.Value
|
||||
// }
|
||||
package list
|
||||
|
||||
// Element is an element of a linked list.
|
||||
type Element[T any] struct {
|
||||
// Next and previous pointers in the doubly-linked list of elements.
|
||||
// To simplify the implementation, internally a list l is implemented
|
||||
// as a ring, such that &l.root is both the next element of the last
|
||||
// list element (l.Back()) and the previous element of the first list
|
||||
// element (l.Front()).
|
||||
next, prev *Element[T]
|
||||
|
||||
// The list to which this element belongs.
|
||||
list *List[T]
|
||||
|
||||
// The value stored with this element.
|
||||
Value T
|
||||
}
|
||||
|
||||
// Next returns the next list element or nil.
|
||||
func (e *Element[T]) Next() *Element[T] {
|
||||
if p := e.next; e.list != nil && p != &e.list.root {
|
||||
return p
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Prev returns the previous list element or nil.
|
||||
func (e *Element[T]) Prev() *Element[T] {
|
||||
if p := e.prev; e.list != nil && p != &e.list.root {
|
||||
return p
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// List represents a doubly linked list.
|
||||
// The zero value for List is an empty list ready to use.
|
||||
type List[T any] struct {
|
||||
root Element[T] // sentinel list element, only &root, root.prev, and root.next are used
|
||||
len int // current list length excluding (this) sentinel element
|
||||
}
|
||||
|
||||
// Init initializes or clears list l.
|
||||
func (l *List[T]) Init() *List[T] {
|
||||
l.root.next = &l.root
|
||||
l.root.prev = &l.root
|
||||
l.len = 0
|
||||
return l
|
||||
}
|
||||
|
||||
// New returns an initialized list.
|
||||
func New[T any]() *List[T] { return new(List[T]).Init() }
|
||||
|
||||
// Len returns the number of elements of list l.
|
||||
// The complexity is O(1).
|
||||
func (l *List[T]) Len() int { return l.len }
|
||||
|
||||
// Front returns the first element of list l or nil if the list is empty.
|
||||
func (l *List[T]) Front() *Element[T] {
|
||||
if l.len == 0 {
|
||||
return nil
|
||||
}
|
||||
return l.root.next
|
||||
}
|
||||
|
||||
// Back returns the last element of list l or nil if the list is empty.
|
||||
func (l *List[T]) Back() *Element[T] {
|
||||
if l.len == 0 {
|
||||
return nil
|
||||
}
|
||||
return l.root.prev
|
||||
}
|
||||
|
||||
// lazyInit lazily initializes a zero List value.
|
||||
func (l *List[T]) lazyInit() {
|
||||
if l.root.next == nil {
|
||||
l.Init()
|
||||
}
|
||||
}
|
||||
|
||||
// insert inserts e after at, increments l.len, and returns e.
|
||||
func (l *List[T]) insert(e, at *Element[T]) *Element[T] {
|
||||
e.prev = at
|
||||
e.next = at.next
|
||||
e.prev.next = e
|
||||
e.next.prev = e
|
||||
e.list = l
|
||||
l.len++
|
||||
return e
|
||||
}
|
||||
|
||||
// insertValue is a convenience wrapper for insert(&Element{Value: v}, at).
|
||||
func (l *List[T]) insertValue(v T, at *Element[T]) *Element[T] {
|
||||
return l.insert(&Element[T]{Value: v}, at)
|
||||
}
|
||||
|
||||
// remove removes e from its list, decrements l.len
|
||||
func (l *List[T]) remove(e *Element[T]) {
|
||||
e.prev.next = e.next
|
||||
e.next.prev = e.prev
|
||||
e.next = nil // avoid memory leaks
|
||||
e.prev = nil // avoid memory leaks
|
||||
e.list = nil
|
||||
l.len--
|
||||
}
|
||||
|
||||
// move moves e to next to at.
|
||||
func (l *List[T]) move(e, at *Element[T]) {
|
||||
if e == at {
|
||||
return
|
||||
}
|
||||
e.prev.next = e.next
|
||||
e.next.prev = e.prev
|
||||
|
||||
e.prev = at
|
||||
e.next = at.next
|
||||
e.prev.next = e
|
||||
e.next.prev = e
|
||||
}
|
||||
|
||||
// Remove removes e from l if e is an element of list l.
|
||||
// It returns the element value e.Value.
|
||||
// The element must not be nil.
|
||||
func (l *List[T]) Remove(e *Element[T]) T {
|
||||
if e.list == l {
|
||||
// if e.list == l, l must have been initialized when e was inserted
|
||||
// in l or l == nil (e is a zero Element) and l.remove will crash
|
||||
l.remove(e)
|
||||
}
|
||||
return e.Value
|
||||
}
|
||||
|
||||
// PushFront inserts a new element e with value v at the front of list l and returns e.
|
||||
func (l *List[T]) PushFront(v T) *Element[T] {
|
||||
l.lazyInit()
|
||||
return l.insertValue(v, &l.root)
|
||||
}
|
||||
|
||||
// PushBack inserts a new element e with value v at the back of list l and returns e.
|
||||
func (l *List[T]) PushBack(v T) *Element[T] {
|
||||
l.lazyInit()
|
||||
return l.insertValue(v, l.root.prev)
|
||||
}
|
||||
|
||||
// InsertBefore inserts a new element e with value v immediately before mark and returns e.
|
||||
// If mark is not an element of l, the list is not modified.
|
||||
// The mark must not be nil.
|
||||
func (l *List[T]) InsertBefore(v T, mark *Element[T]) *Element[T] {
|
||||
if mark.list != l {
|
||||
return nil
|
||||
}
|
||||
// see comment in List.Remove about initialization of l
|
||||
return l.insertValue(v, mark.prev)
|
||||
}
|
||||
|
||||
// InsertAfter inserts a new element e with value v immediately after mark and returns e.
|
||||
// If mark is not an element of l, the list is not modified.
|
||||
// The mark must not be nil.
|
||||
func (l *List[T]) InsertAfter(v T, mark *Element[T]) *Element[T] {
|
||||
if mark.list != l {
|
||||
return nil
|
||||
}
|
||||
// see comment in List.Remove about initialization of l
|
||||
return l.insertValue(v, mark)
|
||||
}
|
||||
|
||||
// MoveToFront moves element e to the front of list l.
|
||||
// If e is not an element of l, the list is not modified.
|
||||
// The element must not be nil.
|
||||
func (l *List[T]) MoveToFront(e *Element[T]) {
|
||||
if e.list != l || l.root.next == e {
|
||||
return
|
||||
}
|
||||
// see comment in List.Remove about initialization of l
|
||||
l.move(e, &l.root)
|
||||
}
|
||||
|
||||
// MoveToBack moves element e to the back of list l.
|
||||
// If e is not an element of l, the list is not modified.
|
||||
// The element must not be nil.
|
||||
func (l *List[T]) MoveToBack(e *Element[T]) {
|
||||
if e.list != l || l.root.prev == e {
|
||||
return
|
||||
}
|
||||
// see comment in List.Remove about initialization of l
|
||||
l.move(e, l.root.prev)
|
||||
}
|
||||
|
||||
// MoveBefore moves element e to its new position before mark.
|
||||
// If e or mark is not an element of l, or e == mark, the list is not modified.
|
||||
// The element and mark must not be nil.
|
||||
func (l *List[T]) MoveBefore(e, mark *Element[T]) {
|
||||
if e.list != l || e == mark || mark.list != l {
|
||||
return
|
||||
}
|
||||
l.move(e, mark.prev)
|
||||
}
|
||||
|
||||
// MoveAfter moves element e to its new position after mark.
|
||||
// If e or mark is not an element of l, or e == mark, the list is not modified.
|
||||
// The element and mark must not be nil.
|
||||
func (l *List[T]) MoveAfter(e, mark *Element[T]) {
|
||||
if e.list != l || e == mark || mark.list != l {
|
||||
return
|
||||
}
|
||||
l.move(e, mark)
|
||||
}
|
||||
|
||||
// PushBackList inserts a copy of another list at the back of list l.
|
||||
// The lists l and other may be the same. They must not be nil.
|
||||
func (l *List[T]) PushBackList(other *List[T]) {
|
||||
l.lazyInit()
|
||||
for i, e := other.Len(), other.Front(); i > 0; i, e = i-1, e.Next() {
|
||||
l.insertValue(e.Value, l.root.prev)
|
||||
}
|
||||
}
|
||||
|
||||
// PushFrontList inserts a copy of another list at the front of list l.
|
||||
// The lists l and other may be the same. They must not be nil.
|
||||
func (l *List[T]) PushFrontList(other *List[T]) {
|
||||
l.lazyInit()
|
||||
for i, e := other.Len(), other.Back(); i > 0; i, e = i-1, e.Prev() {
|
||||
l.insertValue(e.Value, &l.root)
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package cache
|
||||
package lru
|
||||
|
||||
// Modified by https://github.com/die-net/lrucache
|
||||
|
||||
|
@ -6,8 +6,7 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/metacubex/mihomo/common/generics/list"
|
||||
|
||||
list "github.com/bahlo/generic-list-go"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package cache
|
||||
package lru
|
||||
|
||||
import (
|
||||
"testing"
|
|
@ -84,9 +84,9 @@ func (c *BufferedConn) ReadCached() *buf.Buffer { // call in sing/common/bufio.C
|
|||
length := c.r.Buffered()
|
||||
b, _ := c.r.Peek(length)
|
||||
_, _ = c.r.Discard(length)
|
||||
c.r = nil // drop bufio.Reader to let gc can clean up its internal buf
|
||||
return buf.As(b)
|
||||
}
|
||||
c.r = nil // drop bufio.Reader to let gc can clean up its internal buf
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
34
common/net/bufconn_unsafe.go
Normal file
34
common/net/bufconn_unsafe.go
Normal file
|
@ -0,0 +1,34 @@
|
|||
package net
|
||||
|
||||
import (
|
||||
"io"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// bufioReader copy from stdlib bufio/bufio.go
|
||||
// This structure has remained unchanged from go1.5 to go1.21.
|
||||
type bufioReader struct {
|
||||
buf []byte
|
||||
rd io.Reader // reader provided by the client
|
||||
r, w int // buf read and write positions
|
||||
err error
|
||||
lastByte int // last byte read for UnreadByte; -1 means invalid
|
||||
lastRuneSize int // size of last rune read for UnreadRune; -1 means invalid
|
||||
}
|
||||
|
||||
func (c *BufferedConn) AppendData(buf []byte) (ok bool) {
|
||||
b := (*bufioReader)(unsafe.Pointer(c.r))
|
||||
pos := len(b.buf) - b.w - len(buf)
|
||||
if pos >= -b.r { // len(b.buf)-(b.w - b.r) >= len(buf)
|
||||
if pos < 0 { // len(b.buf)-b.w < len(buf)
|
||||
// Slide existing data to beginning.
|
||||
copy(b.buf, b.buf[b.r:b.w])
|
||||
b.w -= b.r
|
||||
b.r = 0
|
||||
}
|
||||
|
||||
b.w += copy(b.buf[b.w:], buf)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
67
common/net/earlyconn.go
Normal file
67
common/net/earlyconn.go
Normal file
|
@ -0,0 +1,67 @@
|
|||
package net
|
||||
|
||||
import (
|
||||
"net"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
|
||||
"github.com/metacubex/mihomo/common/buf"
|
||||
)
|
||||
|
||||
type earlyConn struct {
|
||||
ExtendedConn // only expose standard N.ExtendedConn function to outside
|
||||
resFunc func() error
|
||||
resOnce sync.Once
|
||||
resErr error
|
||||
}
|
||||
|
||||
func (conn *earlyConn) Response() error {
|
||||
conn.resOnce.Do(func() {
|
||||
conn.resErr = conn.resFunc()
|
||||
})
|
||||
return conn.resErr
|
||||
}
|
||||
|
||||
func (conn *earlyConn) Read(b []byte) (n int, err error) {
|
||||
err = conn.Response()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return conn.ExtendedConn.Read(b)
|
||||
}
|
||||
|
||||
func (conn *earlyConn) ReadBuffer(buffer *buf.Buffer) (err error) {
|
||||
err = conn.Response()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return conn.ExtendedConn.ReadBuffer(buffer)
|
||||
}
|
||||
|
||||
func (conn *earlyConn) Upstream() any {
|
||||
return conn.ExtendedConn
|
||||
}
|
||||
|
||||
func (conn *earlyConn) Success() bool {
|
||||
// atomic visit sync.Once.done
|
||||
return atomic.LoadUint32((*uint32)(unsafe.Pointer(&conn.resOnce))) == 1 && conn.resErr == nil
|
||||
}
|
||||
|
||||
func (conn *earlyConn) ReaderReplaceable() bool {
|
||||
return conn.Success()
|
||||
}
|
||||
|
||||
func (conn *earlyConn) ReaderPossiblyReplaceable() bool {
|
||||
return !conn.Success()
|
||||
}
|
||||
|
||||
func (conn *earlyConn) WriterReplaceable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
var _ ExtendedConn = (*earlyConn)(nil)
|
||||
|
||||
func NewEarlyConn(c net.Conn, f func() error) net.Conn {
|
||||
return &earlyConn{ExtendedConn: NewExtendedConn(c), resFunc: f}
|
||||
}
|
|
@ -12,22 +12,28 @@ var defaultAllocator = NewAllocator()
|
|||
|
||||
// Allocator for incoming frames, optimized to prevent overwriting after zeroing
|
||||
type Allocator struct {
|
||||
buffers []sync.Pool
|
||||
buffers [11]sync.Pool
|
||||
}
|
||||
|
||||
// NewAllocator initiates a []byte allocator for frames less than 65536 bytes,
|
||||
// the waste(memory fragmentation) of space allocation is guaranteed to be
|
||||
// no more than 50%.
|
||||
func NewAllocator() *Allocator {
|
||||
alloc := new(Allocator)
|
||||
alloc.buffers = make([]sync.Pool, 17) // 1B -> 64K
|
||||
for k := range alloc.buffers {
|
||||
i := k
|
||||
alloc.buffers[k].New = func() any {
|
||||
return make([]byte, 1<<uint32(i))
|
||||
}
|
||||
return &Allocator{
|
||||
buffers: [...]sync.Pool{ // 64B -> 64K
|
||||
{New: func() any { return new([1 << 6]byte) }},
|
||||
{New: func() any { return new([1 << 7]byte) }},
|
||||
{New: func() any { return new([1 << 8]byte) }},
|
||||
{New: func() any { return new([1 << 9]byte) }},
|
||||
{New: func() any { return new([1 << 10]byte) }},
|
||||
{New: func() any { return new([1 << 11]byte) }},
|
||||
{New: func() any { return new([1 << 12]byte) }},
|
||||
{New: func() any { return new([1 << 13]byte) }},
|
||||
{New: func() any { return new([1 << 14]byte) }},
|
||||
{New: func() any { return new([1 << 15]byte) }},
|
||||
{New: func() any { return new([1 << 16]byte) }},
|
||||
},
|
||||
}
|
||||
return alloc
|
||||
}
|
||||
|
||||
// Get a []byte from pool with most appropriate cap
|
||||
|
@ -40,12 +46,42 @@ func (alloc *Allocator) Get(size int) []byte {
|
|||
case size > 65536:
|
||||
return make([]byte, size)
|
||||
default:
|
||||
bits := msb(size)
|
||||
if size == 1<<bits {
|
||||
return alloc.buffers[bits].Get().([]byte)[:size]
|
||||
var index uint16
|
||||
if size > 64 {
|
||||
index = msb(size)
|
||||
if size != 1<<index {
|
||||
index += 1
|
||||
}
|
||||
index -= 6
|
||||
}
|
||||
|
||||
return alloc.buffers[bits+1].Get().([]byte)[:size]
|
||||
buffer := alloc.buffers[index].Get()
|
||||
switch index {
|
||||
case 0:
|
||||
return buffer.(*[1 << 6]byte)[:size]
|
||||
case 1:
|
||||
return buffer.(*[1 << 7]byte)[:size]
|
||||
case 2:
|
||||
return buffer.(*[1 << 8]byte)[:size]
|
||||
case 3:
|
||||
return buffer.(*[1 << 9]byte)[:size]
|
||||
case 4:
|
||||
return buffer.(*[1 << 10]byte)[:size]
|
||||
case 5:
|
||||
return buffer.(*[1 << 11]byte)[:size]
|
||||
case 6:
|
||||
return buffer.(*[1 << 12]byte)[:size]
|
||||
case 7:
|
||||
return buffer.(*[1 << 13]byte)[:size]
|
||||
case 8:
|
||||
return buffer.(*[1 << 14]byte)[:size]
|
||||
case 9:
|
||||
return buffer.(*[1 << 15]byte)[:size]
|
||||
case 10:
|
||||
return buffer.(*[1 << 16]byte)[:size]
|
||||
default:
|
||||
panic("invalid pool index")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -55,15 +91,45 @@ 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 {
|
||||
return errors.New("allocator Put() incorrect buffer size")
|
||||
}
|
||||
if cap(buf) < 1<<6 {
|
||||
return nil
|
||||
}
|
||||
bits -= 6
|
||||
buf = buf[:cap(buf)]
|
||||
|
||||
//nolint
|
||||
//lint:ignore SA6002 ignore temporarily
|
||||
alloc.buffers[bits].Put(buf)
|
||||
switch bits {
|
||||
case 0:
|
||||
alloc.buffers[bits].Put((*[1 << 6]byte)(buf))
|
||||
case 1:
|
||||
alloc.buffers[bits].Put((*[1 << 7]byte)(buf))
|
||||
case 2:
|
||||
alloc.buffers[bits].Put((*[1 << 8]byte)(buf))
|
||||
case 3:
|
||||
alloc.buffers[bits].Put((*[1 << 9]byte)(buf))
|
||||
case 4:
|
||||
alloc.buffers[bits].Put((*[1 << 10]byte)(buf))
|
||||
case 5:
|
||||
alloc.buffers[bits].Put((*[1 << 11]byte)(buf))
|
||||
case 6:
|
||||
alloc.buffers[bits].Put((*[1 << 12]byte)(buf))
|
||||
case 7:
|
||||
alloc.buffers[bits].Put((*[1 << 13]byte)(buf))
|
||||
case 8:
|
||||
alloc.buffers[bits].Put((*[1 << 14]byte)(buf))
|
||||
case 9:
|
||||
alloc.buffers[bits].Put((*[1 << 15]byte)(buf))
|
||||
case 10:
|
||||
alloc.buffers[bits].Put((*[1 << 16]byte)(buf))
|
||||
default:
|
||||
panic("invalid pool index")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -13,8 +13,8 @@ func TestAllocGet(t *testing.T) {
|
|||
assert.Equal(t, 1, len(alloc.Get(1)))
|
||||
assert.Equal(t, 2, len(alloc.Get(2)))
|
||||
assert.Equal(t, 3, len(alloc.Get(3)))
|
||||
assert.Equal(t, 4, cap(alloc.Get(3)))
|
||||
assert.Equal(t, 4, cap(alloc.Get(4)))
|
||||
assert.Equal(t, 64, cap(alloc.Get(3)))
|
||||
assert.Equal(t, 64, cap(alloc.Get(4)))
|
||||
assert.Equal(t, 1023, len(alloc.Get(1023)))
|
||||
assert.Equal(t, 1024, cap(alloc.Get(1023)))
|
||||
assert.Equal(t, 1024, len(alloc.Get(1024)))
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package util
|
||||
package utils
|
||||
|
||||
import "github.com/samber/lo"
|
||||
|
|
@ -75,3 +75,26 @@ func (ranges IntRanges[T]) Check(status T) bool {
|
|||
|
||||
return false
|
||||
}
|
||||
|
||||
func (ranges IntRanges[T]) ToString() string {
|
||||
if len(ranges) == 0 {
|
||||
return "*"
|
||||
}
|
||||
|
||||
terms := make([]string, len(ranges))
|
||||
for i, r := range ranges {
|
||||
start := r.Start()
|
||||
end := r.End()
|
||||
|
||||
var term string
|
||||
if start == end {
|
||||
term = strconv.Itoa(int(start))
|
||||
} else {
|
||||
term = strconv.Itoa(int(start)) + "-" + strconv.Itoa(int(end))
|
||||
}
|
||||
|
||||
terms[i] = term
|
||||
}
|
||||
|
||||
return strings.Join(terms, "/")
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/metacubex/mihomo/component/resolver"
|
||||
"github.com/metacubex/mihomo/constant/features"
|
||||
)
|
||||
|
||||
type dialFunc func(ctx context.Context, network string, ips []netip.Addr, port string, opt *option) (net.Conn, error)
|
||||
|
@ -70,6 +71,10 @@ func DialContext(ctx context.Context, network, address string, options ...Option
|
|||
}
|
||||
|
||||
func ListenPacket(ctx context.Context, network, address string, options ...Option) (net.PacketConn, error) {
|
||||
if features.CMFA && DefaultSocketHook != nil {
|
||||
return listenPacketHooked(ctx, network, address)
|
||||
}
|
||||
|
||||
cfg := applyOptions(options...)
|
||||
|
||||
lc := &net.ListenConfig{}
|
||||
|
@ -114,6 +119,10 @@ func GetTcpConcurrent() bool {
|
|||
}
|
||||
|
||||
func dialContext(ctx context.Context, network string, destination netip.Addr, port string, opt *option) (net.Conn, error) {
|
||||
if features.CMFA && DefaultSocketHook != nil {
|
||||
return dialContextHooked(ctx, network, destination, port)
|
||||
}
|
||||
|
||||
address := net.JoinHostPort(destination.String(), port)
|
||||
|
||||
netDialer := opt.netDialer
|
||||
|
|
39
component/dialer/patch_android.go
Normal file
39
component/dialer/patch_android.go
Normal file
|
@ -0,0 +1,39 @@
|
|||
//go:build android && cmfa
|
||||
|
||||
package dialer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/netip"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type SocketControl func(network, address string, conn syscall.RawConn) error
|
||||
|
||||
var DefaultSocketHook SocketControl
|
||||
|
||||
func dialContextHooked(ctx context.Context, network string, destination netip.Addr, port string) (net.Conn, error) {
|
||||
dialer := &net.Dialer{
|
||||
Control: DefaultSocketHook,
|
||||
}
|
||||
|
||||
conn, err := dialer.DialContext(ctx, network, net.JoinHostPort(destination.String(), port))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if t, ok := conn.(*net.TCPConn); ok {
|
||||
t.SetKeepAlive(false)
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func listenPacketHooked(ctx context.Context, network, address string) (net.PacketConn, error) {
|
||||
lc := &net.ListenConfig{
|
||||
Control: DefaultSocketHook,
|
||||
}
|
||||
|
||||
return lc.ListenPacket(ctx, network, address)
|
||||
}
|
22
component/dialer/patch_common.go
Normal file
22
component/dialer/patch_common.go
Normal file
|
@ -0,0 +1,22 @@
|
|||
//go:build !(android && cmfa)
|
||||
|
||||
package dialer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/netip"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type SocketControl func(network, address string, conn syscall.RawConn) error
|
||||
|
||||
var DefaultSocketHook SocketControl
|
||||
|
||||
func dialContextHooked(ctx context.Context, network string, destination netip.Addr, port string) (net.Conn, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func listenPacketHooked(ctx context.Context, network, address string) (net.PacketConn, error) {
|
||||
return nil, nil
|
||||
}
|
|
@ -3,12 +3,12 @@ package fakeip
|
|||
import (
|
||||
"net/netip"
|
||||
|
||||
"github.com/metacubex/mihomo/common/cache"
|
||||
"github.com/metacubex/mihomo/common/lru"
|
||||
)
|
||||
|
||||
type memoryStore struct {
|
||||
cacheIP *cache.LruCache[string, netip.Addr]
|
||||
cacheHost *cache.LruCache[netip.Addr, string]
|
||||
cacheIP *lru.LruCache[string, netip.Addr]
|
||||
cacheHost *lru.LruCache[netip.Addr, string]
|
||||
}
|
||||
|
||||
// GetByHost implements store.GetByHost
|
||||
|
@ -73,7 +73,7 @@ func (m *memoryStore) FlushFakeIP() error {
|
|||
|
||||
func newMemoryStore(size int) *memoryStore {
|
||||
return &memoryStore{
|
||||
cacheIP: cache.New[string, netip.Addr](cache.WithSize[string, netip.Addr](size)),
|
||||
cacheHost: cache.New[netip.Addr, string](cache.WithSize[netip.Addr, string](size)),
|
||||
cacheIP: lru.New[string, netip.Addr](lru.WithSize[string, netip.Addr](size)),
|
||||
cacheHost: lru.New[netip.Addr, string](lru.WithSize[netip.Addr, string](size)),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,8 +10,8 @@ import (
|
|||
"github.com/metacubex/mihomo/component/profile/cachefile"
|
||||
"github.com/metacubex/mihomo/component/trie"
|
||||
|
||||
"github.com/sagernet/bbolt"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
func createPools(options Options) ([]*Pool, string, error) {
|
||||
|
|
|
@ -44,7 +44,7 @@ func InitGeoSite() error {
|
|||
func downloadGeoSite(path string) (err error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*90)
|
||||
defer cancel()
|
||||
resp, err := mihomoHttp.HttpRequest(ctx, C.GeoSiteUrl, http.MethodGet, http.Header{"User-Agent": {"mihomo"}}, nil)
|
||||
resp, err := mihomoHttp.HttpRequest(ctx, C.GeoSiteUrl, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ func downloadGeoSite(path string) (err error) {
|
|||
func downloadGeoIP(path string) (err error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*90)
|
||||
defer cancel()
|
||||
resp, err := mihomoHttp.HttpRequest(ctx, C.GeoIpUrl, http.MethodGet, http.Header{"User-Agent": {"mihomo"}}, nil)
|
||||
resp, err := mihomoHttp.HttpRequest(ctx, C.GeoIpUrl, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package strmatcher
|
||||
|
||||
import (
|
||||
"github.com/metacubex/mihomo/common/generics/list"
|
||||
list "github.com/bahlo/generic-list-go"
|
||||
)
|
||||
|
||||
const validCharCount = 53
|
||||
|
|
|
@ -79,7 +79,7 @@ func Instance() Reader {
|
|||
func DownloadMMDB(path string) (err error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*90)
|
||||
defer cancel()
|
||||
resp, err := mihomoHttp.HttpRequest(ctx, C.MmdbUrl, http.MethodGet, http.Header{"User-Agent": {"mihomo"}}, nil)
|
||||
resp, err := mihomoHttp.HttpRequest(ctx, C.MmdbUrl, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
18
component/mmdb/patch_android.go
Normal file
18
component/mmdb/patch_android.go
Normal file
|
@ -0,0 +1,18 @@
|
|||
//go:build android && cmfa
|
||||
|
||||
package mmdb
|
||||
|
||||
import "github.com/oschwald/maxminddb-golang"
|
||||
|
||||
func InstallOverride(override *maxminddb.Reader) {
|
||||
newReader := Reader{Reader: override}
|
||||
switch override.Metadata.DatabaseType {
|
||||
case "sing-geoip":
|
||||
reader.databaseType = typeSing
|
||||
case "Meta-geoip0":
|
||||
reader.databaseType = typeMetaV0
|
||||
default:
|
||||
reader.databaseType = typeMaxmind
|
||||
}
|
||||
reader = newReader
|
||||
}
|
|
@ -6,7 +6,7 @@ import (
|
|||
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
|
||||
"github.com/puzpuzpuz/xsync/v2"
|
||||
"github.com/puzpuzpuz/xsync/v3"
|
||||
)
|
||||
|
||||
type Table struct {
|
||||
|
@ -25,8 +25,8 @@ func (t *Table) Set(key string, e C.PacketConn, w C.WriteBackProxy) {
|
|||
t.mapping.Store(key, &Entry{
|
||||
PacketConn: e,
|
||||
WriteBackProxy: w,
|
||||
LocalUDPConnMap: xsync.NewMapOf[*net.UDPConn](),
|
||||
LocalLockMap: xsync.NewMapOf[*sync.Cond](),
|
||||
LocalUDPConnMap: xsync.NewMapOf[string, *net.UDPConn](),
|
||||
LocalLockMap: xsync.NewMapOf[string, *sync.Cond](),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -116,7 +116,7 @@ func makeLock() *sync.Cond {
|
|||
// New return *Cache
|
||||
func New() *Table {
|
||||
return &Table{
|
||||
mapping: xsync.NewMapOf[*Entry](),
|
||||
lockMap: xsync.NewMapOf[*sync.Cond](),
|
||||
mapping: xsync.NewMapOf[string, *Entry](),
|
||||
lockMap: xsync.NewMapOf[string, *sync.Cond](),
|
||||
}
|
||||
}
|
||||
|
|
16
component/process/process_android.go
Normal file
16
component/process/process_android.go
Normal file
|
@ -0,0 +1,16 @@
|
|||
//go:build android && cmfa
|
||||
|
||||
package process
|
||||
|
||||
import "github.com/metacubex/mihomo/constant"
|
||||
|
||||
type PackageNameResolver func(metadata *constant.Metadata) (string, error)
|
||||
|
||||
var DefaultPackageNameResolver PackageNameResolver
|
||||
|
||||
func FindPackageName(metadata *constant.Metadata) (string, error) {
|
||||
if resolver := DefaultPackageNameResolver; resolver != nil {
|
||||
return resolver(metadata)
|
||||
}
|
||||
return "", ErrPlatformNotSupport
|
||||
}
|
9
component/process/process_common.go
Normal file
9
component/process/process_common.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
//go:build !(android && cmfa)
|
||||
|
||||
package process
|
||||
|
||||
import "github.com/metacubex/mihomo/constant"
|
||||
|
||||
func FindPackageName(metadata *constant.Metadata) (string, error) {
|
||||
return "", nil
|
||||
}
|
|
@ -9,7 +9,7 @@ import (
|
|||
C "github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/log"
|
||||
|
||||
"go.etcd.io/bbolt"
|
||||
"github.com/sagernet/bbolt"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
|
@ -13,6 +13,10 @@ import (
|
|||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
const (
|
||||
minInterval = time.Minute * 5
|
||||
)
|
||||
|
||||
var (
|
||||
fileMode os.FileMode = 0o666
|
||||
dirMode os.FileMode = 0o755
|
||||
|
@ -24,8 +28,7 @@ type Fetcher[V any] struct {
|
|||
resourceType string
|
||||
name string
|
||||
vehicle types.Vehicle
|
||||
UpdatedAt *time.Time
|
||||
ticker *time.Ticker
|
||||
UpdatedAt time.Time
|
||||
done chan struct{}
|
||||
hash [16]byte
|
||||
parser Parser[V]
|
||||
|
@ -56,7 +59,7 @@ func (f *Fetcher[V]) Initial() (V, error) {
|
|||
if stat, fErr := os.Stat(f.vehicle.Path()); fErr == nil {
|
||||
buf, err = os.ReadFile(f.vehicle.Path())
|
||||
modTime := stat.ModTime()
|
||||
f.UpdatedAt = &modTime
|
||||
f.UpdatedAt = modTime
|
||||
isLocal = true
|
||||
if f.interval != 0 && modTime.Add(f.interval).Before(time.Now()) {
|
||||
log.Warnln("[Provider] %s not updated for a long time, force refresh", f.Name())
|
||||
|
@ -64,6 +67,7 @@ func (f *Fetcher[V]) Initial() (V, error) {
|
|||
}
|
||||
} else {
|
||||
buf, err = f.vehicle.Read()
|
||||
f.UpdatedAt = time.Now()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
@ -113,7 +117,7 @@ func (f *Fetcher[V]) Initial() (V, error) {
|
|||
f.hash = md5.Sum(buf)
|
||||
|
||||
// pull contents automatically
|
||||
if f.ticker != nil {
|
||||
if f.interval > 0 {
|
||||
go f.pullLoop()
|
||||
}
|
||||
|
||||
|
@ -129,7 +133,7 @@ func (f *Fetcher[V]) Update() (V, bool, error) {
|
|||
now := time.Now()
|
||||
hash := md5.Sum(buf)
|
||||
if bytes.Equal(f.hash[:], hash[:]) {
|
||||
f.UpdatedAt = &now
|
||||
f.UpdatedAt = now
|
||||
_ = os.Chtimes(f.vehicle.Path(), now, now)
|
||||
return lo.Empty[V](), true, nil
|
||||
}
|
||||
|
@ -145,23 +149,31 @@ func (f *Fetcher[V]) Update() (V, bool, error) {
|
|||
}
|
||||
}
|
||||
|
||||
f.UpdatedAt = &now
|
||||
f.UpdatedAt = now
|
||||
f.hash = hash
|
||||
|
||||
return contents, false, nil
|
||||
}
|
||||
|
||||
func (f *Fetcher[V]) Destroy() error {
|
||||
if f.ticker != nil {
|
||||
if f.interval > 0 {
|
||||
f.done <- struct{}{}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Fetcher[V]) pullLoop() {
|
||||
initialInterval := f.interval - time.Since(f.UpdatedAt)
|
||||
if initialInterval < minInterval {
|
||||
initialInterval = minInterval
|
||||
}
|
||||
|
||||
timer := time.NewTimer(initialInterval)
|
||||
defer timer.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-f.ticker.C:
|
||||
case <-timer.C:
|
||||
timer.Reset(f.interval)
|
||||
elm, same, err := f.Update()
|
||||
if err != nil {
|
||||
log.Errorln("[Provider] %s pull error: %s", f.Name(), err.Error())
|
||||
|
@ -178,7 +190,6 @@ func (f *Fetcher[V]) pullLoop() {
|
|||
f.OnUpdate(elm)
|
||||
}
|
||||
case <-f.done:
|
||||
f.ticker.Stop()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -197,17 +208,12 @@ func safeWrite(path string, buf []byte) error {
|
|||
}
|
||||
|
||||
func NewFetcher[V any](name string, interval time.Duration, vehicle types.Vehicle, parser Parser[V], onUpdate func(V)) *Fetcher[V] {
|
||||
var ticker *time.Ticker
|
||||
if interval != 0 {
|
||||
ticker = time.NewTicker(interval)
|
||||
}
|
||||
|
||||
return &Fetcher[V]{
|
||||
name: name,
|
||||
ticker: ticker,
|
||||
vehicle: vehicle,
|
||||
parser: parser,
|
||||
done: make(chan struct{}, 1),
|
||||
done: make(chan struct{}, 8),
|
||||
OnUpdate: onUpdate,
|
||||
interval: interval,
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/metacubex/mihomo/common/cache"
|
||||
"github.com/metacubex/mihomo/common/lru"
|
||||
N "github.com/metacubex/mihomo/common/net"
|
||||
"github.com/metacubex/mihomo/component/trie"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
|
@ -29,7 +29,7 @@ type SnifferDispatcher struct {
|
|||
sniffers map[sniffer.Sniffer]SnifferConfig
|
||||
forceDomain *trie.DomainSet
|
||||
skipSNI *trie.DomainSet
|
||||
skipList *cache.LruCache[string, uint8]
|
||||
skipList *lru.LruCache[string, uint8]
|
||||
rwMux sync.RWMutex
|
||||
forceDnsMapping bool
|
||||
parsePureIp bool
|
||||
|
@ -202,7 +202,7 @@ func NewSnifferDispatcher(snifferConfig map[sniffer.Type]SnifferConfig,
|
|||
enable: true,
|
||||
forceDomain: forceDomain,
|
||||
skipSNI: skipSNI,
|
||||
skipList: cache.New(cache.WithSize[string, uint8](128), cache.WithAge[string, uint8](600)),
|
||||
skipList: lru.New(lru.WithSize[string, uint8](128), lru.WithAge[string, uint8](600)),
|
||||
forceDnsMapping: forceDnsMapping,
|
||||
parsePureIp: parsePureIp,
|
||||
sniffers: make(map[sniffer.Sniffer]SnifferConfig, 0),
|
||||
|
|
126
config/config.go
126
config/config.go
|
@ -29,6 +29,7 @@ import (
|
|||
tlsC "github.com/metacubex/mihomo/component/tls"
|
||||
"github.com/metacubex/mihomo/component/trie"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/constant/features"
|
||||
providerTypes "github.com/metacubex/mihomo/constant/provider"
|
||||
snifferTypes "github.com/metacubex/mihomo/constant/sniffer"
|
||||
"github.com/metacubex/mihomo/dns"
|
||||
|
@ -39,6 +40,7 @@ import (
|
|||
RP "github.com/metacubex/mihomo/rules/provider"
|
||||
T "github.com/metacubex/mihomo/tunnel"
|
||||
|
||||
orderedmap "github.com/wk8/go-ordered-map/v2"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
|
@ -112,9 +114,10 @@ type DNS struct {
|
|||
Listen string `yaml:"listen"`
|
||||
EnhancedMode C.DNSMode `yaml:"enhanced-mode"`
|
||||
DefaultNameserver []dns.NameServer `yaml:"default-nameserver"`
|
||||
CacheAlgorithm string `yaml:"cache-algorithm"`
|
||||
FakeIPRange *fakeip.Pool
|
||||
Hosts *trie.DomainTrie[resolver.HostValue]
|
||||
NameServerPolicy map[string][]dns.NameServer
|
||||
NameServerPolicy *orderedmap.OrderedMap[string, []dns.NameServer]
|
||||
ProxyServerNameserver []dns.NameServer
|
||||
}
|
||||
|
||||
|
@ -193,29 +196,35 @@ type RawNTP struct {
|
|||
}
|
||||
|
||||
type RawDNS struct {
|
||||
Enable bool `yaml:"enable"`
|
||||
PreferH3 bool `yaml:"prefer-h3"`
|
||||
IPv6 bool `yaml:"ipv6"`
|
||||
IPv6Timeout uint `yaml:"ipv6-timeout"`
|
||||
UseHosts bool `yaml:"use-hosts"`
|
||||
NameServer []string `yaml:"nameserver"`
|
||||
Fallback []string `yaml:"fallback"`
|
||||
FallbackFilter RawFallbackFilter `yaml:"fallback-filter"`
|
||||
Listen string `yaml:"listen"`
|
||||
EnhancedMode C.DNSMode `yaml:"enhanced-mode"`
|
||||
FakeIPRange string `yaml:"fake-ip-range"`
|
||||
FakeIPFilter []string `yaml:"fake-ip-filter"`
|
||||
DefaultNameserver []string `yaml:"default-nameserver"`
|
||||
NameServerPolicy map[string]any `yaml:"nameserver-policy"`
|
||||
ProxyServerNameserver []string `yaml:"proxy-server-nameserver"`
|
||||
Enable bool `yaml:"enable" json:"enable"`
|
||||
PreferH3 bool `yaml:"prefer-h3" json:"prefer-h3"`
|
||||
IPv6 bool `yaml:"ipv6" json:"ipv6"`
|
||||
IPv6Timeout uint `yaml:"ipv6-timeout" json:"ipv6-timeout"`
|
||||
UseHosts bool `yaml:"use-hosts" json:"use-hosts"`
|
||||
NameServer []string `yaml:"nameserver" json:"nameserver"`
|
||||
Fallback []string `yaml:"fallback" json:"fallback"`
|
||||
FallbackFilter RawFallbackFilter `yaml:"fallback-filter" json:"fallback-filter"`
|
||||
Listen string `yaml:"listen" json:"listen"`
|
||||
EnhancedMode C.DNSMode `yaml:"enhanced-mode" json:"enhanced-mode"`
|
||||
FakeIPRange string `yaml:"fake-ip-range" json:"fake-ip-range"`
|
||||
FakeIPFilter []string `yaml:"fake-ip-filter" json:"fake-ip-filter"`
|
||||
DefaultNameserver []string `yaml:"default-nameserver" json:"default-nameserver"`
|
||||
CacheAlgorithm string `yaml:"cache-algorithm" json:"cache-algorithm"`
|
||||
NameServerPolicy *orderedmap.OrderedMap[string, any] `yaml:"nameserver-policy" json:"nameserver-policy"`
|
||||
ProxyServerNameserver []string `yaml:"proxy-server-nameserver" json:"proxy-server-nameserver"`
|
||||
}
|
||||
|
||||
type RawFallbackFilter struct {
|
||||
GeoIP bool `yaml:"geoip"`
|
||||
GeoIPCode string `yaml:"geoip-code"`
|
||||
IPCIDR []string `yaml:"ipcidr"`
|
||||
Domain []string `yaml:"domain"`
|
||||
GeoSite []string `yaml:"geosite"`
|
||||
GeoIP bool `yaml:"geoip" json:"geoip"`
|
||||
GeoIPCode string `yaml:"geoip-code" json:"geoip-code"`
|
||||
IPCIDR []string `yaml:"ipcidr" json:"ipcidr"`
|
||||
Domain []string `yaml:"domain" json:"domain"`
|
||||
GeoSite []string `yaml:"geosite" json:"geosite"`
|
||||
}
|
||||
|
||||
type RawClashForAndroid struct {
|
||||
AppendSystemDNS bool `yaml:"append-system-dns" json:"append-system-dns"`
|
||||
UiSubtitlePattern string `yaml:"ui-subtitle-pattern" json:"ui-subtitle-pattern"`
|
||||
}
|
||||
|
||||
type RawTun struct {
|
||||
|
@ -263,23 +272,23 @@ type RawTuicServer struct {
|
|||
}
|
||||
|
||||
type RawConfig struct {
|
||||
Port int `yaml:"port"`
|
||||
SocksPort int `yaml:"socks-port"`
|
||||
RedirPort int `yaml:"redir-port"`
|
||||
TProxyPort int `yaml:"tproxy-port"`
|
||||
MixedPort int `yaml:"mixed-port"`
|
||||
Port int `yaml:"port" json:"port"`
|
||||
SocksPort int `yaml:"socks-port" json:"socks-port"`
|
||||
RedirPort int `yaml:"redir-port" json:"redir-port"`
|
||||
TProxyPort int `yaml:"tproxy-port" json:"tproxy-port"`
|
||||
MixedPort int `yaml:"mixed-port" json:"mixed-port"`
|
||||
ShadowSocksConfig string `yaml:"ss-config"`
|
||||
VmessConfig string `yaml:"vmess-config"`
|
||||
InboundTfo bool `yaml:"inbound-tfo"`
|
||||
InboundMPTCP bool `yaml:"inbound-mptcp"`
|
||||
Authentication []string `yaml:"authentication"`
|
||||
Authentication []string `yaml:"authentication" json:"authentication"`
|
||||
SkipAuthPrefixes []netip.Prefix `yaml:"skip-auth-prefixes"`
|
||||
AllowLan bool `yaml:"allow-lan"`
|
||||
BindAddress string `yaml:"bind-address"`
|
||||
Mode T.TunnelMode `yaml:"mode"`
|
||||
UnifiedDelay bool `yaml:"unified-delay"`
|
||||
LogLevel log.LogLevel `yaml:"log-level"`
|
||||
IPv6 bool `yaml:"ipv6"`
|
||||
AllowLan bool `yaml:"allow-lan" json:"allow-lan"`
|
||||
BindAddress string `yaml:"bind-address" json:"bind-address"`
|
||||
Mode T.TunnelMode `yaml:"mode" json:"mode"`
|
||||
UnifiedDelay bool `yaml:"unified-delay" json:"unified-delay"`
|
||||
LogLevel log.LogLevel `yaml:"log-level" json:"log-level"`
|
||||
IPv6 bool `yaml:"ipv6" json:"ipv6"`
|
||||
ExternalController string `yaml:"external-controller"`
|
||||
ExternalControllerTLS string `yaml:"external-controller-tls"`
|
||||
ExternalUI string `yaml:"external-ui"`
|
||||
|
@ -289,20 +298,20 @@ type RawConfig struct {
|
|||
Interface string `yaml:"interface-name"`
|
||||
RoutingMark int `yaml:"routing-mark"`
|
||||
Tunnels []LC.Tunnel `yaml:"tunnels"`
|
||||
GeodataMode bool `yaml:"geodata-mode"`
|
||||
GeodataLoader string `yaml:"geodata-loader"`
|
||||
GeodataMode bool `yaml:"geodata-mode" json:"geodata-mode"`
|
||||
GeodataLoader string `yaml:"geodata-loader" json:"geodata-loader"`
|
||||
TCPConcurrent bool `yaml:"tcp-concurrent" json:"tcp-concurrent"`
|
||||
FindProcessMode P.FindProcessMode `yaml:"find-process-mode" json:"find-process-mode"`
|
||||
GlobalClientFingerprint string `yaml:"global-client-fingerprint"`
|
||||
GlobalUA string `yaml:"global-ua"`
|
||||
KeepAliveInterval int `yaml:"keep-alive-interval"`
|
||||
|
||||
Sniffer RawSniffer `yaml:"sniffer"`
|
||||
Sniffer RawSniffer `yaml:"sniffer" json:"sniffer"`
|
||||
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"`
|
||||
Hosts map[string]any `yaml:"hosts" json:"hosts"`
|
||||
NTP RawNTP `yaml:"ntp" json:"ntp"`
|
||||
DNS RawDNS `yaml:"dns" json:"dns"`
|
||||
Tun RawTun `yaml:"tun"`
|
||||
TuicServer RawTuicServer `yaml:"tuic-server"`
|
||||
EBpf EBpf `yaml:"ebpf"`
|
||||
|
@ -316,6 +325,8 @@ type RawConfig struct {
|
|||
SubRules map[string][]string `yaml:"sub-rules"`
|
||||
RawTLS TLS `yaml:"tls"`
|
||||
Listeners []map[string]any `yaml:"listeners"`
|
||||
|
||||
ClashForAndroid RawClashForAndroid `yaml:"clash-for-android" json:"clash-for-android"`
|
||||
}
|
||||
|
||||
type GeoXUrl struct {
|
||||
|
@ -381,7 +392,7 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) {
|
|||
ProxyGroup: []map[string]any{},
|
||||
TCPConcurrent: false,
|
||||
FindProcessMode: P.FindProcessStrict,
|
||||
GlobalUA: "mihomo",
|
||||
GlobalUA: "clash.meta",
|
||||
Tun: RawTun{
|
||||
Enable: false,
|
||||
Device: "",
|
||||
|
@ -545,7 +556,7 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) {
|
|||
config.DNS = dnsCfg
|
||||
|
||||
err = parseTun(rawCfg.Tun, config.General)
|
||||
if err != nil {
|
||||
if !features.CMFA && err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -664,6 +675,7 @@ func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[
|
|||
|
||||
proxies["DIRECT"] = adapter.NewProxy(outbound.NewDirect())
|
||||
proxies["REJECT"] = adapter.NewProxy(outbound.NewReject())
|
||||
proxies["REJECT-DROP"] = adapter.NewProxy(outbound.NewRejectDrop())
|
||||
proxies["COMPATIBLE"] = adapter.NewProxy(outbound.NewCompatible())
|
||||
proxies["PASS"] = adapter.NewProxy(outbound.NewPass())
|
||||
proxyList = append(proxyList, "DIRECT", "REJECT")
|
||||
|
@ -917,9 +929,9 @@ func parseHosts(cfg *RawConfig) (*trie.DomainTrie[resolver.HostValue], error) {
|
|||
|
||||
if len(cfg.Hosts) != 0 {
|
||||
for domain, anyValue := range cfg.Hosts {
|
||||
if str, ok := anyValue.(string); ok && str == "mihomo" {
|
||||
if str, ok := anyValue.(string); ok && str == "lan" {
|
||||
if addrs, err := net.InterfaceAddrs(); err != nil {
|
||||
log.Errorln("insert mihomo to host error: %s", err)
|
||||
log.Errorln("insert lan to host error: %s", err)
|
||||
} else {
|
||||
ips := make([]netip.Addr, 0)
|
||||
for _, addr := range addrs {
|
||||
|
@ -1085,12 +1097,13 @@ func parsePureDNSServer(server string) string {
|
|||
}
|
||||
}
|
||||
}
|
||||
func parseNameServerPolicy(nsPolicy map[string]any, ruleProviders map[string]providerTypes.RuleProvider, preferH3 bool) (map[string][]dns.NameServer, error) {
|
||||
policy := map[string][]dns.NameServer{}
|
||||
updatedPolicy := make(map[string]interface{})
|
||||
func parseNameServerPolicy(nsPolicy *orderedmap.OrderedMap[string, any], ruleProviders map[string]providerTypes.RuleProvider, preferH3 bool) (*orderedmap.OrderedMap[string, []dns.NameServer], error) {
|
||||
policy := orderedmap.New[string, []dns.NameServer]()
|
||||
updatedPolicy := orderedmap.New[string, any]()
|
||||
re := regexp.MustCompile(`[a-zA-Z0-9\-]+\.[a-zA-Z]{2,}(\.[a-zA-Z]{2,})?`)
|
||||
|
||||
for k, v := range nsPolicy {
|
||||
for pair := nsPolicy.Oldest(); pair != nil; pair = pair.Next() {
|
||||
k, v := pair.Key, pair.Value
|
||||
if strings.Contains(k, ",") {
|
||||
if strings.Contains(k, "geosite:") {
|
||||
subkeys := strings.Split(k, ":")
|
||||
|
@ -1098,7 +1111,7 @@ func parseNameServerPolicy(nsPolicy map[string]any, ruleProviders map[string]pro
|
|||
subkeys = strings.Split(subkeys[0], ",")
|
||||
for _, subkey := range subkeys {
|
||||
newKey := "geosite:" + subkey
|
||||
updatedPolicy[newKey] = v
|
||||
updatedPolicy.Store(newKey, v)
|
||||
}
|
||||
} else if strings.Contains(k, "rule-set:") {
|
||||
subkeys := strings.Split(k, ":")
|
||||
|
@ -1106,20 +1119,21 @@ func parseNameServerPolicy(nsPolicy map[string]any, ruleProviders map[string]pro
|
|||
subkeys = strings.Split(subkeys[0], ",")
|
||||
for _, subkey := range subkeys {
|
||||
newKey := "rule-set:" + subkey
|
||||
updatedPolicy[newKey] = v
|
||||
updatedPolicy.Store(newKey, v)
|
||||
}
|
||||
} else if re.MatchString(k) {
|
||||
subkeys := strings.Split(k, ",")
|
||||
for _, subkey := range subkeys {
|
||||
updatedPolicy[subkey] = v
|
||||
updatedPolicy.Store(subkey, v)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
updatedPolicy[k] = v
|
||||
updatedPolicy.Store(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
for domain, server := range updatedPolicy {
|
||||
for pair := updatedPolicy.Oldest(); pair != nil; pair = pair.Next() {
|
||||
domain, server := pair.Key, pair.Value
|
||||
servers, err := utils.ToStringSlice(server)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -1144,7 +1158,7 @@ func parseNameServerPolicy(nsPolicy map[string]any, ruleProviders map[string]pro
|
|||
}
|
||||
}
|
||||
}
|
||||
policy[domain] = nameservers
|
||||
policy.Store(domain, nameservers)
|
||||
}
|
||||
|
||||
return policy, nil
|
||||
|
@ -1330,6 +1344,12 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul
|
|||
dnsCfg.Hosts = hosts
|
||||
}
|
||||
|
||||
if cfg.CacheAlgorithm == "" || cfg.CacheAlgorithm == "lru" {
|
||||
dnsCfg.CacheAlgorithm = "lru"
|
||||
} else {
|
||||
dnsCfg.CacheAlgorithm = "arc"
|
||||
}
|
||||
|
||||
return dnsCfg, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -14,12 +14,13 @@ import (
|
|||
"github.com/metacubex/mihomo/adapter/outboundgroup"
|
||||
"github.com/metacubex/mihomo/common/structure"
|
||||
mihomoHttp "github.com/metacubex/mihomo/component/http"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
)
|
||||
|
||||
func downloadForBytes(url string) ([]byte, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*90)
|
||||
defer cancel()
|
||||
resp, err := mihomoHttp.HttpRequest(ctx, url, http.MethodGet, http.Header{"User-Agent": {"mihomo"}}, nil)
|
||||
resp, err := mihomoHttp.HttpRequest(ctx, url, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
const (
|
||||
Direct AdapterType = iota
|
||||
Reject
|
||||
RejectDrop
|
||||
Compatible
|
||||
Pass
|
||||
|
||||
|
@ -42,10 +43,11 @@ const (
|
|||
)
|
||||
|
||||
const (
|
||||
DefaultTCPTimeout = 5 * time.Second
|
||||
DefaultUDPTimeout = DefaultTCPTimeout
|
||||
DefaultTLSTimeout = DefaultTCPTimeout
|
||||
DefaultMaxHealthCheckUrlNum = 16
|
||||
DefaultTCPTimeout = 5 * time.Second
|
||||
DefaultDropTime = 12 * DefaultTCPTimeout
|
||||
DefaultUDPTimeout = DefaultTCPTimeout
|
||||
DefaultTLSTimeout = DefaultTCPTimeout
|
||||
DefaultTestURL = "https://cp.cloudflare.com/generate_204"
|
||||
)
|
||||
|
||||
var ErrNotSupport = errors.New("no support")
|
||||
|
@ -147,21 +149,13 @@ type DelayHistory struct {
|
|||
|
||||
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, expectedStatus utils.IntRanges[uint16]) (uint16, error)
|
||||
|
||||
// Deprecated: use DialContext instead.
|
||||
Dial(metadata *Metadata) (Conn, error)
|
||||
|
@ -179,6 +173,8 @@ func (at AdapterType) String() string {
|
|||
return "Direct"
|
||||
case Reject:
|
||||
return "Reject"
|
||||
case RejectDrop:
|
||||
return "RejectDrop"
|
||||
case Compatible:
|
||||
return "Compatible"
|
||||
case Pass:
|
||||
|
|
5
constant/features/cmfa.go
Normal file
5
constant/features/cmfa.go
Normal file
|
@ -0,0 +1,5 @@
|
|||
//go:build cmfa
|
||||
|
||||
package features
|
||||
|
||||
const CMFA = true
|
5
constant/features/cmfa_stub.go
Normal file
5
constant/features/cmfa_stub.go
Normal file
|
@ -0,0 +1,5 @@
|
|||
//go:build !cmfa
|
||||
|
||||
package features
|
||||
|
||||
const CMFA = false
|
|
@ -1,6 +1,5 @@
|
|||
//go:build with_low_memory
|
||||
|
||||
package features
|
||||
|
||||
func init() {
|
||||
TAGS = append(TAGS, "with_low_memory")
|
||||
}
|
||||
const WithLowMemory = true
|
||||
|
|
5
constant/features/low_memory_stub.go
Normal file
5
constant/features/low_memory_stub.go
Normal file
|
@ -0,0 +1,5 @@
|
|||
//go:build !with_low_memory
|
||||
|
||||
package features
|
||||
|
||||
const WithLowMemory = false
|
|
@ -2,6 +2,4 @@
|
|||
|
||||
package features
|
||||
|
||||
func init() {
|
||||
TAGS = append(TAGS, "no_fake_tcp")
|
||||
}
|
||||
const NoFakeTCP = true
|
||||
|
|
5
constant/features/no_fake_tcp_stub.go
Normal file
5
constant/features/no_fake_tcp_stub.go
Normal file
|
@ -0,0 +1,5 @@
|
|||
//go:build !no_fake_tcp
|
||||
|
||||
package features
|
||||
|
||||
const NoFakeTCP = false
|
|
@ -1,3 +1,17 @@
|
|||
package features
|
||||
|
||||
var TAGS = make([]string, 0, 0)
|
||||
func Tags() (tags []string) {
|
||||
if CMFA {
|
||||
tags = append(tags, "cmfa")
|
||||
}
|
||||
if WithLowMemory {
|
||||
tags = append(tags, "with_low_memory")
|
||||
}
|
||||
if NoFakeTCP {
|
||||
tags = append(tags, "no_fake_tcp")
|
||||
}
|
||||
if WithGVisor {
|
||||
tags = append(tags, "with_gvisor")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
|
@ -2,6 +2,4 @@
|
|||
|
||||
package features
|
||||
|
||||
func init() {
|
||||
TAGS = append(TAGS, "with_gvisor")
|
||||
}
|
||||
const WithGVisor = true
|
||||
|
|
5
constant/features/with_gvisor_stub.go
Normal file
5
constant/features/with_gvisor_stub.go
Normal file
|
@ -0,0 +1,5 @@
|
|||
//go:build !with_gvisor
|
||||
|
||||
package features
|
||||
|
||||
const WithGVisor = false
|
|
@ -147,6 +147,9 @@ type Metadata struct {
|
|||
SpecialProxy string `json:"specialProxy"`
|
||||
SpecialRules string `json:"specialRules"`
|
||||
RemoteDst string `json:"remoteDestination"`
|
||||
|
||||
RawSrcAddr net.Addr `json:"-"`
|
||||
RawDstAddr net.Addr `json:"-"`
|
||||
// Only domain rule
|
||||
SniffHost string `json:"sniffHost"`
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//go:build !(android && cmfa)
|
||||
|
||||
package dns
|
||||
|
||||
import (
|
||||
|
|
|
@ -3,7 +3,7 @@ package dns
|
|||
import (
|
||||
"net/netip"
|
||||
|
||||
"github.com/metacubex/mihomo/common/cache"
|
||||
"github.com/metacubex/mihomo/common/lru"
|
||||
"github.com/metacubex/mihomo/component/fakeip"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
)
|
||||
|
@ -11,7 +11,7 @@ import (
|
|||
type ResolverEnhancer struct {
|
||||
mode C.DNSMode
|
||||
fakePool *fakeip.Pool
|
||||
mapping *cache.LruCache[netip.Addr, string]
|
||||
mapping *lru.LruCache[netip.Addr, string]
|
||||
}
|
||||
|
||||
func (h *ResolverEnhancer) FakeIPEnabled() bool {
|
||||
|
@ -105,11 +105,11 @@ func (h *ResolverEnhancer) StoreFakePoolState() {
|
|||
|
||||
func NewEnhancer(cfg Config) *ResolverEnhancer {
|
||||
var fakePool *fakeip.Pool
|
||||
var mapping *cache.LruCache[netip.Addr, string]
|
||||
var mapping *lru.LruCache[netip.Addr, string]
|
||||
|
||||
if cfg.EnhancedMode != C.DNSNormal {
|
||||
fakePool = cfg.Pool
|
||||
mapping = cache.New(cache.WithSize[netip.Addr, string](4096))
|
||||
mapping = lru.New(lru.WithSize[netip.Addr, string](4096))
|
||||
}
|
||||
|
||||
return &ResolverEnhancer{
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/metacubex/mihomo/common/cache"
|
||||
"github.com/metacubex/mihomo/common/lru"
|
||||
"github.com/metacubex/mihomo/common/nnip"
|
||||
"github.com/metacubex/mihomo/component/fakeip"
|
||||
R "github.com/metacubex/mihomo/component/resolver"
|
||||
|
@ -21,7 +21,7 @@ type (
|
|||
middleware func(next handler) handler
|
||||
)
|
||||
|
||||
func withHosts(hosts R.Hosts, mapping *cache.LruCache[netip.Addr, string]) middleware {
|
||||
func withHosts(hosts R.Hosts, mapping *lru.LruCache[netip.Addr, string]) middleware {
|
||||
return func(next handler) handler {
|
||||
return func(ctx *context.DNSContext, r *D.Msg) (*D.Msg, error) {
|
||||
q := r.Question[0]
|
||||
|
@ -98,7 +98,7 @@ func withHosts(hosts R.Hosts, mapping *cache.LruCache[netip.Addr, string]) middl
|
|||
}
|
||||
}
|
||||
|
||||
func withMapping(mapping *cache.LruCache[netip.Addr, string]) middleware {
|
||||
func withMapping(mapping *lru.LruCache[netip.Addr, string]) middleware {
|
||||
return func(next handler) handler {
|
||||
return func(ctx *context.DNSContext, r *D.Msg) (*D.Msg, error) {
|
||||
q := r.Question[0]
|
||||
|
|
81
dns/patch_android.go
Normal file
81
dns/patch_android.go
Normal file
|
@ -0,0 +1,81 @@
|
|||
//go:build android && cmfa
|
||||
|
||||
package dns
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
D "github.com/miekg/dns"
|
||||
|
||||
"github.com/metacubex/mihomo/common/lru"
|
||||
"github.com/metacubex/mihomo/component/dhcp"
|
||||
"github.com/metacubex/mihomo/component/resolver"
|
||||
)
|
||||
|
||||
const SystemDNSPlaceholder = "system"
|
||||
|
||||
var systemResolver *Resolver
|
||||
var isolateHandler handler
|
||||
|
||||
var _ dnsClient = (*dhcpClient)(nil)
|
||||
|
||||
type dhcpClient struct {
|
||||
enable bool
|
||||
}
|
||||
|
||||
func (d *dhcpClient) Address() string {
|
||||
return SystemDNSPlaceholder
|
||||
}
|
||||
|
||||
func (d *dhcpClient) Exchange(m *D.Msg) (msg *D.Msg, err error) {
|
||||
return d.ExchangeContext(context.Background(), m)
|
||||
}
|
||||
|
||||
func (d *dhcpClient) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) {
|
||||
if s := systemResolver; s != nil {
|
||||
return s.ExchangeContext(ctx, m)
|
||||
}
|
||||
|
||||
return nil, dhcp.ErrNotFound
|
||||
}
|
||||
|
||||
func ServeDNSWithDefaultServer(msg *D.Msg) (*D.Msg, error) {
|
||||
if h := isolateHandler; h != nil {
|
||||
return handlerWithContext(context.Background(), h, msg)
|
||||
}
|
||||
|
||||
return nil, D.ErrTime
|
||||
}
|
||||
|
||||
func FlushCacheWithDefaultResolver() {
|
||||
if r := resolver.DefaultResolver; r != nil {
|
||||
r.(*Resolver).lruCache = lru.New[string, *D.Msg](lru.WithSize[string, *D.Msg](4096), lru.WithStale[string, *D.Msg](true))
|
||||
}
|
||||
}
|
||||
|
||||
func UpdateSystemDNS(addr []string) {
|
||||
if len(addr) == 0 {
|
||||
systemResolver = nil
|
||||
}
|
||||
|
||||
ns := make([]NameServer, 0, len(addr))
|
||||
for _, d := range addr {
|
||||
ns = append(ns, NameServer{Addr: d})
|
||||
}
|
||||
|
||||
systemResolver = NewResolver(Config{Main: ns})
|
||||
}
|
||||
|
||||
func UpdateIsolateHandler(resolver *Resolver, mapper *ResolverEnhancer) {
|
||||
if resolver == nil {
|
||||
isolateHandler = nil
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
isolateHandler = NewHandler(resolver, mapper)
|
||||
}
|
||||
|
||||
func newDHCPClient(ifaceName string) *dhcpClient {
|
||||
return &dhcpClient{enable: ifaceName == SystemDNSPlaceholder}
|
||||
}
|
6
dns/patch_common.go
Normal file
6
dns/patch_common.go
Normal file
|
@ -0,0 +1,6 @@
|
|||
//go:build !(android && cmfa)
|
||||
|
||||
package dns
|
||||
|
||||
func UpdateIsolateHandler(resolver *Resolver, mapper *ResolverEnhancer) {
|
||||
}
|
|
@ -1,30 +1,50 @@
|
|||
package dns
|
||||
|
||||
type Policy struct {
|
||||
data []dnsClient
|
||||
import (
|
||||
"github.com/metacubex/mihomo/component/trie"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/constant/provider"
|
||||
)
|
||||
|
||||
type dnsPolicy interface {
|
||||
Match(domain string) []dnsClient
|
||||
}
|
||||
|
||||
func (p *Policy) GetData() []dnsClient {
|
||||
return p.data
|
||||
type domainTriePolicy struct {
|
||||
*trie.DomainTrie[[]dnsClient]
|
||||
}
|
||||
|
||||
func (p *Policy) Compare(p2 *Policy) int {
|
||||
if p2 == nil {
|
||||
return 1
|
||||
func (p domainTriePolicy) Match(domain string) []dnsClient {
|
||||
record := p.DomainTrie.Search(domain)
|
||||
if record != nil {
|
||||
return record.Data()
|
||||
}
|
||||
l1 := len(p.data)
|
||||
l2 := len(p2.data)
|
||||
if l1 == l2 {
|
||||
return 0
|
||||
}
|
||||
if l1 > l2 {
|
||||
return 1
|
||||
}
|
||||
return -1
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewPolicy(data []dnsClient) *Policy {
|
||||
return &Policy{
|
||||
data: data,
|
||||
}
|
||||
type geositePolicy struct {
|
||||
matcher fallbackDomainFilter
|
||||
inverse bool
|
||||
dnsClients []dnsClient
|
||||
}
|
||||
|
||||
func (p geositePolicy) Match(domain string) []dnsClient {
|
||||
matched := p.matcher.Match(domain)
|
||||
if matched != p.inverse {
|
||||
return p.dnsClients
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type domainSetPolicy struct {
|
||||
domainSetProvider provider.RuleProvider
|
||||
dnsClients []dnsClient
|
||||
}
|
||||
|
||||
func (p domainSetPolicy) Match(domain string) []dnsClient {
|
||||
metadata := &C.Metadata{Host: domain}
|
||||
if ok := p.domainSetProvider.Match(metadata); ok {
|
||||
return p.dnsClients
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
228
dns/resolver.go
228
dns/resolver.go
|
@ -7,7 +7,8 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/metacubex/mihomo/common/cache"
|
||||
"github.com/metacubex/mihomo/common/arc"
|
||||
"github.com/metacubex/mihomo/common/lru"
|
||||
"github.com/metacubex/mihomo/component/fakeip"
|
||||
"github.com/metacubex/mihomo/component/geodata/router"
|
||||
"github.com/metacubex/mihomo/component/resolver"
|
||||
|
@ -18,6 +19,8 @@ import (
|
|||
|
||||
D "github.com/miekg/dns"
|
||||
"github.com/samber/lo"
|
||||
orderedmap "github.com/wk8/go-ordered-map/v2"
|
||||
"golang.org/x/exp/maps"
|
||||
"golang.org/x/sync/singleflight"
|
||||
)
|
||||
|
||||
|
@ -26,22 +29,16 @@ type dnsClient interface {
|
|||
Address() string
|
||||
}
|
||||
|
||||
type dnsCache interface {
|
||||
GetWithExpire(key string) (*D.Msg, time.Time, bool)
|
||||
SetWithExpire(key string, value *D.Msg, expire time.Time)
|
||||
}
|
||||
|
||||
type result struct {
|
||||
Msg *D.Msg
|
||||
Error error
|
||||
}
|
||||
|
||||
type geositePolicyRecord struct {
|
||||
matcher fallbackDomainFilter
|
||||
policy *Policy
|
||||
inversedMatching bool
|
||||
}
|
||||
|
||||
type domainSetPolicyRecord struct {
|
||||
domainSetProvider provider.RuleProvider
|
||||
policy *Policy
|
||||
}
|
||||
|
||||
type Resolver struct {
|
||||
ipv6 bool
|
||||
ipv6Timeout time.Duration
|
||||
|
@ -51,10 +48,8 @@ type Resolver struct {
|
|||
fallbackDomainFilters []fallbackDomainFilter
|
||||
fallbackIPFilters []fallbackIPFilter
|
||||
group singleflight.Group
|
||||
lruCache *cache.LruCache[string, *D.Msg]
|
||||
policy *trie.DomainTrie[*Policy]
|
||||
domainSetPolicy []domainSetPolicyRecord
|
||||
geositePolicy []geositePolicyRecord
|
||||
cache dnsCache
|
||||
policy []dnsPolicy
|
||||
proxyServer []dnsClient
|
||||
}
|
||||
|
||||
|
@ -151,8 +146,9 @@ func (r *Resolver) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, e
|
|||
}()
|
||||
|
||||
q := m.Question[0]
|
||||
cacheM, expireTime, hit := r.lruCache.GetWithExpire(q.String())
|
||||
cacheM, expireTime, hit := r.cache.GetWithExpire(q.String())
|
||||
if hit {
|
||||
log.Debugln("[DNS] cache hit for %s, expire at %s", q.Name, expireTime.Format("2006-01-02 15:04:05"))
|
||||
now := time.Now()
|
||||
msg = cacheM.Copy()
|
||||
if expireTime.Before(now) {
|
||||
|
@ -192,7 +188,7 @@ func (r *Resolver) exchangeWithoutCache(ctx context.Context, m *D.Msg) (msg *D.M
|
|||
msg.Extra = lo.Filter(msg.Extra, func(rr D.RR, index int) bool {
|
||||
return rr.Header().Rrtype != D.TypeOPT
|
||||
})
|
||||
putMsgToCache(r.lruCache, q.String(), q, msg)
|
||||
putMsgToCache(r.cache, q.String(), q, msg)
|
||||
}
|
||||
}()
|
||||
|
||||
|
@ -258,22 +254,9 @@ func (r *Resolver) matchPolicy(m *D.Msg) []dnsClient {
|
|||
return nil
|
||||
}
|
||||
|
||||
record := r.policy.Search(domain)
|
||||
if record != nil {
|
||||
p := record.Data()
|
||||
return p.GetData()
|
||||
}
|
||||
|
||||
for _, geositeRecord := range r.geositePolicy {
|
||||
matched := geositeRecord.matcher.Match(domain)
|
||||
if matched != geositeRecord.inversedMatching {
|
||||
return geositeRecord.policy.GetData()
|
||||
}
|
||||
}
|
||||
metadata := &C.Metadata{Host: domain}
|
||||
for _, domainSetRecord := range r.domainSetPolicy {
|
||||
if ok := domainSetRecord.domainSetProvider.Match(metadata); ok {
|
||||
return domainSetRecord.policy.GetData()
|
||||
for _, policy := range r.policy {
|
||||
if dnsClients := policy.Match(domain); len(dnsClients) > 0 {
|
||||
return dnsClients
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
@ -395,6 +378,23 @@ type NameServer struct {
|
|||
PreferH3 bool
|
||||
}
|
||||
|
||||
func (ns NameServer) Equal(ns2 NameServer) bool {
|
||||
defer func() {
|
||||
// C.ProxyAdapter compare maybe panic, just ignore
|
||||
recover()
|
||||
}()
|
||||
if ns.Net == ns2.Net &&
|
||||
ns.Addr == ns2.Addr &&
|
||||
ns.Interface == ns2.Interface &&
|
||||
ns.ProxyAdapter == ns2.ProxyAdapter &&
|
||||
ns.ProxyName == ns2.ProxyName &&
|
||||
maps.Equal(ns.Params, ns2.Params) &&
|
||||
ns.PreferH3 == ns2.PreferH3 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type FallbackFilter struct {
|
||||
GeoIP bool
|
||||
GeoIPCode string
|
||||
|
@ -404,76 +404,139 @@ type FallbackFilter struct {
|
|||
}
|
||||
|
||||
type Config struct {
|
||||
Main, Fallback []NameServer
|
||||
Default []NameServer
|
||||
ProxyServer []NameServer
|
||||
IPv6 bool
|
||||
IPv6Timeout uint
|
||||
EnhancedMode C.DNSMode
|
||||
FallbackFilter FallbackFilter
|
||||
Pool *fakeip.Pool
|
||||
Hosts *trie.DomainTrie[resolver.HostValue]
|
||||
Policy map[string][]NameServer
|
||||
DomainSetPolicy map[provider.RuleProvider][]NameServer
|
||||
GeositePolicy map[router.DomainMatcher][]NameServer
|
||||
Main, Fallback []NameServer
|
||||
Default []NameServer
|
||||
ProxyServer []NameServer
|
||||
IPv6 bool
|
||||
IPv6Timeout uint
|
||||
EnhancedMode C.DNSMode
|
||||
FallbackFilter FallbackFilter
|
||||
Pool *fakeip.Pool
|
||||
Hosts *trie.DomainTrie[resolver.HostValue]
|
||||
Policy *orderedmap.OrderedMap[string, []NameServer]
|
||||
RuleProviders map[string]provider.RuleProvider
|
||||
CacheAlgorithm string
|
||||
}
|
||||
|
||||
func NewResolver(config Config) *Resolver {
|
||||
var cache dnsCache
|
||||
if config.CacheAlgorithm == "lru" {
|
||||
cache = lru.New(lru.WithSize[string, *D.Msg](4096), lru.WithStale[string, *D.Msg](true))
|
||||
} else {
|
||||
cache = arc.New(arc.WithSize[string, *D.Msg](4096))
|
||||
}
|
||||
defaultResolver := &Resolver{
|
||||
main: transform(config.Default, nil),
|
||||
lruCache: cache.New(cache.WithSize[string, *D.Msg](4096), cache.WithStale[string, *D.Msg](true)),
|
||||
cache: cache,
|
||||
ipv6Timeout: time.Duration(config.IPv6Timeout) * time.Millisecond,
|
||||
}
|
||||
|
||||
var nameServerCache []struct {
|
||||
NameServer
|
||||
dnsClient
|
||||
}
|
||||
cacheTransform := func(nameserver []NameServer) (result []dnsClient) {
|
||||
LOOP:
|
||||
for _, ns := range nameserver {
|
||||
for _, nsc := range nameServerCache {
|
||||
if nsc.NameServer.Equal(ns) {
|
||||
result = append(result, nsc.dnsClient)
|
||||
continue LOOP
|
||||
}
|
||||
}
|
||||
// not in cache
|
||||
dc := transform([]NameServer{ns}, defaultResolver)
|
||||
if len(dc) > 0 {
|
||||
dc := dc[0]
|
||||
nameServerCache = append(nameServerCache, struct {
|
||||
NameServer
|
||||
dnsClient
|
||||
}{NameServer: ns, dnsClient: dc})
|
||||
result = append(result, dc)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if config.CacheAlgorithm == "" || config.CacheAlgorithm == "lru" {
|
||||
cache = lru.New(lru.WithSize[string, *D.Msg](4096), lru.WithStale[string, *D.Msg](true))
|
||||
} else {
|
||||
cache = arc.New(arc.WithSize[string, *D.Msg](4096))
|
||||
}
|
||||
r := &Resolver{
|
||||
ipv6: config.IPv6,
|
||||
main: transform(config.Main, defaultResolver),
|
||||
lruCache: cache.New(cache.WithSize[string, *D.Msg](4096), cache.WithStale[string, *D.Msg](true)),
|
||||
main: cacheTransform(config.Main),
|
||||
cache: cache,
|
||||
hosts: config.Hosts,
|
||||
ipv6Timeout: time.Duration(config.IPv6Timeout) * time.Millisecond,
|
||||
}
|
||||
|
||||
if len(config.Fallback) != 0 {
|
||||
r.fallback = transform(config.Fallback, defaultResolver)
|
||||
r.fallback = cacheTransform(config.Fallback)
|
||||
}
|
||||
|
||||
if len(config.ProxyServer) != 0 {
|
||||
r.proxyServer = transform(config.ProxyServer, defaultResolver)
|
||||
r.proxyServer = cacheTransform(config.ProxyServer)
|
||||
}
|
||||
|
||||
if len(config.Policy) != 0 {
|
||||
r.policy = trie.New[*Policy]()
|
||||
for domain, nameserver := range config.Policy {
|
||||
if strings.HasPrefix(strings.ToLower(domain), "geosite:") {
|
||||
groupname := domain[8:]
|
||||
inverse := false
|
||||
if strings.HasPrefix(groupname, "!") {
|
||||
inverse = true
|
||||
groupname = groupname[1:]
|
||||
}
|
||||
log.Debugln("adding geosite policy: %s inversed %t", groupname, inverse)
|
||||
matcher, err := NewGeoSite(groupname)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
r.geositePolicy = append(r.geositePolicy, geositePolicyRecord{
|
||||
matcher: matcher,
|
||||
policy: NewPolicy(transform(nameserver, defaultResolver)),
|
||||
inversedMatching: inverse,
|
||||
})
|
||||
} else {
|
||||
_ = r.policy.Insert(domain, NewPolicy(transform(nameserver, defaultResolver)))
|
||||
if config.Policy.Len() != 0 {
|
||||
r.policy = make([]dnsPolicy, 0)
|
||||
|
||||
var triePolicy *trie.DomainTrie[[]dnsClient]
|
||||
insertTriePolicy := func() {
|
||||
if triePolicy != nil {
|
||||
triePolicy.Optimize()
|
||||
r.policy = append(r.policy, domainTriePolicy{triePolicy})
|
||||
triePolicy = nil
|
||||
}
|
||||
}
|
||||
r.policy.Optimize()
|
||||
}
|
||||
if len(config.DomainSetPolicy) > 0 {
|
||||
for p, n := range config.DomainSetPolicy {
|
||||
r.domainSetPolicy = append(r.domainSetPolicy, domainSetPolicyRecord{
|
||||
domainSetProvider: p,
|
||||
policy: NewPolicy(transform(n, defaultResolver)),
|
||||
})
|
||||
|
||||
for pair := config.Policy.Oldest(); pair != nil; pair = pair.Next() {
|
||||
domain, nameserver := pair.Key, pair.Value
|
||||
domain = strings.ToLower(domain)
|
||||
|
||||
if temp := strings.Split(domain, ":"); len(temp) == 2 {
|
||||
prefix := temp[0]
|
||||
key := temp[1]
|
||||
switch strings.ToLower(prefix) {
|
||||
case "rule-set":
|
||||
if p, ok := config.RuleProviders[key]; ok {
|
||||
insertTriePolicy()
|
||||
r.policy = append(r.policy, domainSetPolicy{
|
||||
domainSetProvider: p,
|
||||
dnsClients: cacheTransform(nameserver),
|
||||
})
|
||||
continue
|
||||
} else {
|
||||
log.Warnln("can't found ruleset policy: %s", key)
|
||||
}
|
||||
case "geosite":
|
||||
inverse := false
|
||||
if strings.HasPrefix(key, "!") {
|
||||
inverse = true
|
||||
key = key[1:]
|
||||
}
|
||||
log.Debugln("adding geosite policy: %s inversed %t", key, inverse)
|
||||
matcher, err := NewGeoSite(key)
|
||||
if err != nil {
|
||||
log.Warnln("adding geosite policy %s error: %s", key, err)
|
||||
continue
|
||||
}
|
||||
insertTriePolicy()
|
||||
r.policy = append(r.policy, geositePolicy{
|
||||
matcher: matcher,
|
||||
inverse: inverse,
|
||||
dnsClients: cacheTransform(nameserver),
|
||||
})
|
||||
continue // skip triePolicy new
|
||||
}
|
||||
}
|
||||
if triePolicy == nil {
|
||||
triePolicy = trie.New[[]dnsClient]()
|
||||
}
|
||||
_ = triePolicy.Insert(domain, cacheTransform(nameserver))
|
||||
}
|
||||
insertTriePolicy()
|
||||
}
|
||||
|
||||
fallbackIPFilters := []fallbackIPFilter{}
|
||||
|
@ -506,9 +569,8 @@ func NewProxyServerHostResolver(old *Resolver) *Resolver {
|
|||
r := &Resolver{
|
||||
ipv6: old.ipv6,
|
||||
main: old.proxyServer,
|
||||
lruCache: old.lruCache,
|
||||
cache: old.cache,
|
||||
hosts: old.hosts,
|
||||
policy: trie.New[*Policy](),
|
||||
ipv6Timeout: old.ipv6Timeout,
|
||||
}
|
||||
return r
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"net"
|
||||
|
||||
"github.com/metacubex/mihomo/common/sockopt"
|
||||
"github.com/metacubex/mihomo/constant/features"
|
||||
"github.com/metacubex/mihomo/context"
|
||||
"github.com/metacubex/mihomo/log"
|
||||
|
||||
|
@ -49,6 +50,10 @@ func (s *Server) SetHandler(handler handler) {
|
|||
}
|
||||
|
||||
func ReCreateServer(addr string, resolver *Resolver, mapper *ResolverEnhancer) {
|
||||
if features.CMFA {
|
||||
UpdateIsolateHandler(resolver, mapper)
|
||||
}
|
||||
|
||||
if addr == address && resolver != nil {
|
||||
handler := NewHandler(resolver, mapper)
|
||||
server.SetHandler(handler)
|
||||
|
|
|
@ -11,7 +11,6 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/metacubex/mihomo/common/cache"
|
||||
N "github.com/metacubex/mihomo/common/net"
|
||||
"github.com/metacubex/mihomo/common/nnip"
|
||||
"github.com/metacubex/mihomo/common/picker"
|
||||
|
@ -51,7 +50,7 @@ func updateTTL(records []D.RR, ttl uint32) {
|
|||
}
|
||||
}
|
||||
|
||||
func putMsgToCache(c *cache.LruCache[string, *D.Msg], key string, q D.Question, msg *D.Msg) {
|
||||
func putMsgToCache(c dnsCache, key string, q D.Question, msg *D.Msg) {
|
||||
// skip dns cache for acme challenge
|
||||
if q.Qtype == D.TypeTXT && strings.HasPrefix(q.Name, "_acme-challenge.") {
|
||||
log.Debugln("[DNS] dns cache ignored because of acme challenge for: %s", q.Name)
|
||||
|
|
|
@ -78,7 +78,7 @@ hosts:
|
|||
# '.dev': 127.0.0.1
|
||||
# 'alpha.mihomo.dev': '::1'
|
||||
# test.com: [1.1.1.1, 2.2.2.2]
|
||||
# mihomo.lan: mihomo # mihomo 为特别字段,将加入本地所有网卡的地址
|
||||
# home.lan: lan # lan 为特别字段,将加入本地所有网卡的地址
|
||||
# baidu.com: google.com # 只允许配置一个别名
|
||||
|
||||
profile: # 存储 select 选择记录
|
||||
|
@ -183,6 +183,7 @@ tunnels: # one line config
|
|||
|
||||
# DNS配置
|
||||
dns:
|
||||
cache-algorithm: arc
|
||||
enable: false # 关闭将使用系统 DNS
|
||||
prefer-h3: true # 开启 DoH 支持 HTTP/3,将并发尝试
|
||||
listen: 0.0.0.0:53 # 开启 DNS 服务器监听
|
||||
|
|
50
go.mod
50
go.mod
|
@ -5,51 +5,53 @@ go 1.20
|
|||
require (
|
||||
github.com/3andne/restls-client-go v0.1.6
|
||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da
|
||||
github.com/cilium/ebpf v0.12.0
|
||||
github.com/bahlo/generic-list-go v0.2.0
|
||||
github.com/cilium/ebpf v0.12.3
|
||||
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/go-chi/cors v1.2.1
|
||||
github.com/go-chi/render v1.0.3
|
||||
github.com/gobwas/ws v1.3.0
|
||||
github.com/gobwas/ws v1.3.1
|
||||
github.com/gofrs/uuid/v5 v5.0.0
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230908212754-65c27093e38a
|
||||
github.com/insomniacslk/dhcp v0.0.0-20231016090811-6a2c8fbdcc1c
|
||||
github.com/jpillora/backoff v1.0.0
|
||||
github.com/klauspost/cpuid/v2 v2.2.5
|
||||
github.com/klauspost/cpuid/v2 v2.2.6
|
||||
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.39.1-0.20231019030608-fd969d66f16b
|
||||
github.com/metacubex/sing-quic v0.0.0-20231008050747-a684db516966
|
||||
github.com/metacubex/quic-go v0.40.1-0.20231130135418-0c1b47cf9394
|
||||
github.com/metacubex/sing-quic v0.0.0-20231130141855-0022295e524b
|
||||
github.com/metacubex/sing-shadowsocks v0.2.5
|
||||
github.com/metacubex/sing-shadowsocks2 v0.1.4
|
||||
github.com/metacubex/sing-tun v0.1.15-0.20231103033938-170591e8d5bd
|
||||
github.com/metacubex/sing-vmess v0.1.9-0.20230921005247-a0488d7dac74
|
||||
github.com/metacubex/sing-wireguard v0.0.0-20231001110902-321836559170
|
||||
github.com/miekg/dns v1.1.56
|
||||
github.com/miekg/dns v1.1.57
|
||||
github.com/mroth/weightedrand/v2 v2.1.0
|
||||
github.com/openacid/low v0.1.21
|
||||
github.com/oschwald/maxminddb-golang v1.12.0
|
||||
github.com/puzpuzpuz/xsync/v2 v2.5.1
|
||||
github.com/puzpuzpuz/xsync/v3 v3.0.2
|
||||
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97
|
||||
github.com/sagernet/sing v0.2.14
|
||||
github.com/sagernet/sing-mux v0.1.3
|
||||
github.com/sagernet/sing v0.2.18-0.20231108041402-4fbbd193203c
|
||||
github.com/sagernet/sing-mux v0.1.5-0.20231109075101-6b086ed6bb07
|
||||
github.com/sagernet/sing-shadowtls v0.1.4
|
||||
github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6
|
||||
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2
|
||||
github.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f
|
||||
github.com/samber/lo v1.38.1
|
||||
github.com/shirou/gopsutil/v3 v3.23.9
|
||||
github.com/shirou/gopsutil/v3 v3.23.10
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8
|
||||
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.14.0
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d
|
||||
golang.org/x/net v0.17.0
|
||||
golang.org/x/sync v0.4.0
|
||||
golang.org/x/sys v0.13.0
|
||||
golang.org/x/crypto v0.16.0
|
||||
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa
|
||||
golang.org/x/net v0.18.0
|
||||
golang.org/x/sync v0.5.0
|
||||
golang.org/x/sys v0.15.0
|
||||
google.golang.org/protobuf v1.31.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
lukechampine.com/blake3 v1.2.1
|
||||
|
@ -60,6 +62,7 @@ require (
|
|||
github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 // indirect
|
||||
github.com/ajg/form v1.5.1 // indirect
|
||||
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||
github.com/buger/jsonparser v1.1.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9 // indirect
|
||||
github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 // indirect
|
||||
|
@ -72,12 +75,13 @@ require (
|
|||
github.com/gobwas/httphead v0.1.0 // indirect
|
||||
github.com/gobwas/pool v0.2.1 // indirect
|
||||
github.com/google/btree v1.1.2 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // 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/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mdlayher/socket v0.4.1 // indirect
|
||||
github.com/metacubex/gvisor v0.0.0-20231001104248-0f672c3fb8d8 // indirect
|
||||
github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // indirect
|
||||
|
@ -86,7 +90,7 @@ require (
|
|||
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.4 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.4.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
|
||||
|
@ -102,10 +106,10 @@ require (
|
|||
gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect
|
||||
go.uber.org/mock v0.3.0 // indirect
|
||||
go4.org/netipx v0.0.0-20230824141953-6213f710f925 // indirect
|
||||
golang.org/x/mod v0.13.0 // indirect
|
||||
golang.org/x/text v0.13.0 // indirect
|
||||
golang.org/x/mod v0.14.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
golang.org/x/tools v0.14.0 // indirect
|
||||
golang.org/x/tools v0.15.0 // indirect
|
||||
)
|
||||
|
||||
replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20231001053806-1230641572b9
|
||||
replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20231118023733-957d84f17d2c
|
||||
|
|
97
go.sum
97
go.sum
|
@ -10,12 +10,16 @@ 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/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
|
||||
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
|
||||
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
|
||||
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
|
||||
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.12.0 h1:oQEuIQIXgYhe1v7sYUG0P9vtJTYZLLdA6tiQmrOB1mo=
|
||||
github.com/cilium/ebpf v0.12.0/go.mod h1:u9H29/Iq+8cy70YqI6p5pfADkFl3vdnV2qXDg5JL0Zo=
|
||||
github.com/cilium/ebpf v0.12.3 h1:8ht6F9MquybnY97at+VDZb3eQQr8ev79RueWeVaEcG4=
|
||||
github.com/cilium/ebpf v0.12.3/go.mod h1:TctK1ivibvI3znr66ljgi4hqOT8EYQjz1KWBfb1UVgM=
|
||||
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/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
@ -53,8 +57,8 @@ github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU
|
|||
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
|
||||
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
|
||||
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||
github.com/gobwas/ws v1.3.0 h1:sbeU3Y4Qzlb+MOzIe6mQGf7QR4Hkv6ZD0qhGkBFL2O0=
|
||||
github.com/gobwas/ws v1.3.0/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
|
||||
github.com/gobwas/ws v1.3.1 h1:Qi34dfLMWJbiKaNbDVzM9x27nZBjmkaW6i4+Ku+pGVU=
|
||||
github.com/gobwas/ws v1.3.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
|
||||
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/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
|
@ -64,16 +68,18 @@ 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/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
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/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-20230908212754-65c27093e38a h1:S33o3djA1nPRd+d/bf7jbbXytXuK/EoXow7+aa76grQ=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230908212754-65c27093e38a/go.mod h1:zmdm3sTSDP3vOOX3CEWRkkRHtKr1DxBx+J1OQFoDQQs=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20231016090811-6a2c8fbdcc1c h1:PgxFEySCI41sH0mB7/2XswdXbUykQsRUGod8Rn+NubM=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20231016090811-6a2c8fbdcc1c/go.mod h1:3A9PQ1cunSDF/1rbTq99Ts4pVnycWg+vlPkfeD2NLFI=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
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=
|
||||
|
@ -81,14 +87,16 @@ github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2E
|
|||
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/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
|
||||
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc=
|
||||
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
|
||||
github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=
|
||||
github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
|
||||
|
@ -97,12 +105,12 @@ github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 h1:cjd4biTvO
|
|||
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759/go.mod h1:UHOv2xu+RIgLwpXca7TLrXleEd4oR3sPatW6IF8wU88=
|
||||
github.com/metacubex/gvisor v0.0.0-20231001104248-0f672c3fb8d8 h1:npBvaPAT145UY8682AzpUMWpdIxJti/WPLjy7gCiYYs=
|
||||
github.com/metacubex/gvisor v0.0.0-20231001104248-0f672c3fb8d8/go.mod h1:ZR6Gas7P1GcADCVBc1uOrA0bLQqDDyp70+63fD/BE2c=
|
||||
github.com/metacubex/quic-go v0.39.1-0.20231019030608-fd969d66f16b h1:uZ++sW8yg7Fr/Wvmmrb/V+SfxvRs0iMC+2+u2bRmO8g=
|
||||
github.com/metacubex/quic-go v0.39.1-0.20231019030608-fd969d66f16b/go.mod h1:4pe6cY+nAMFU/Uxn1rfnxNIowsaJGDQ3uyy4VuiPkP4=
|
||||
github.com/metacubex/sing v0.0.0-20231001053806-1230641572b9 h1:F0+IuW0tZ96QHEmrebXAdYnz7ab7Gz4l5yYC4g6Cg8k=
|
||||
github.com/metacubex/sing v0.0.0-20231001053806-1230641572b9/go.mod h1:GQ673iPfUnkbK/dIPkfd1Xh1MjOGo36gkl/mkiHY7Jg=
|
||||
github.com/metacubex/sing-quic v0.0.0-20231008050747-a684db516966 h1:wbOsbU3kfD5LRuJIntJwEPmgGSQukof8CgLNypi8az8=
|
||||
github.com/metacubex/sing-quic v0.0.0-20231008050747-a684db516966/go.mod h1:GU7g2AZesXItk4CspDP8Dc7eGtlA2GVDihyCwsUXRSo=
|
||||
github.com/metacubex/quic-go v0.40.1-0.20231130135418-0c1b47cf9394 h1:dIT+KB2hknBCrwVAXPeY9tpzzkOZP5m40yqUteRT6/Y=
|
||||
github.com/metacubex/quic-go v0.40.1-0.20231130135418-0c1b47cf9394/go.mod h1:F/t8VnA47xoia8ABlNA4InkZjssvFJ5p6E6jKdbkgAs=
|
||||
github.com/metacubex/sing v0.0.0-20231118023733-957d84f17d2c h1:SZwaf42NVCIDaHw5X2HOnNcEiK9ao6yO+N4zYyKfXe8=
|
||||
github.com/metacubex/sing v0.0.0-20231118023733-957d84f17d2c/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
|
||||
github.com/metacubex/sing-quic v0.0.0-20231130141855-0022295e524b h1:7XXoEePvxfkQN9b2wB8UXU3uzb9uL8syEFF7A9VAKKQ=
|
||||
github.com/metacubex/sing-quic v0.0.0-20231130141855-0022295e524b/go.mod h1:Gu5/zqZDd5G1AUtoV2yjAPWOEy7zwbU2DBUjdxJh0Kw=
|
||||
github.com/metacubex/sing-shadowsocks v0.2.5 h1:O2RRSHlKGEpAVG/OHJQxyHqDy8uvvdCW/oW2TDBOIhc=
|
||||
github.com/metacubex/sing-shadowsocks v0.2.5/go.mod h1:Xz2uW9BEYGEoA8B4XEpoxt7ERHClFCwsMAvWaruoyMo=
|
||||
github.com/metacubex/sing-shadowsocks2 v0.1.4 h1:OOCf8lgsVcpTOJUeaFAMzyKVebaQOBnKirDdUdBoKIE=
|
||||
|
@ -113,8 +121,8 @@ github.com/metacubex/sing-vmess v0.1.9-0.20230921005247-a0488d7dac74 h1:FtupiyFk
|
|||
github.com/metacubex/sing-vmess v0.1.9-0.20230921005247-a0488d7dac74/go.mod h1:8EWBZpc+qNvf5gmvjAtMHK1/DpcWqzfcBL842K00BsM=
|
||||
github.com/metacubex/sing-wireguard v0.0.0-20231001110902-321836559170 h1:DBGA0hmrP4pVIwLiXUONdphjcppED+plmVaKf1oqkwk=
|
||||
github.com/metacubex/sing-wireguard v0.0.0-20231001110902-321836559170/go.mod h1:/VbJfbdLnANE+SKXyMk/96sTRrD4GdFLh5mkegqqFcY=
|
||||
github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE=
|
||||
github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY=
|
||||
github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM=
|
||||
github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk=
|
||||
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/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 h1:1102pQc2SEPp5+xrS26wEaeb26sZy6k9/ZXlZN+eXE4=
|
||||
|
@ -137,19 +145,21 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
|||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
||||
github.com/puzpuzpuz/xsync/v2 v2.5.1 h1:mVGYAvzDSu52+zaGyNjC+24Xw2bQi3kTr4QJ6N9pIIU=
|
||||
github.com/puzpuzpuz/xsync/v2 v2.5.1/go.mod h1:gD2H2krq/w52MfPLE+Uy64TzJDVY7lP2znR9qmR35kU=
|
||||
github.com/puzpuzpuz/xsync/v3 v3.0.2 h1:3yESHrRFYr6xzkz61LLkvNiPFXxJEAABanTQpKbAaew=
|
||||
github.com/puzpuzpuz/xsync/v3 v3.0.2/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
|
||||
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.4 h1:MfFAPULvst4yoMgY9QmtpYmfij/em7O8UUi+bNVm7Cg=
|
||||
github.com/quic-go/qtls-go1-20 v0.3.4/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
|
||||
github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs=
|
||||
github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a h1:+NkI2670SQpQWvkkD2QgdTuzQG263YZ+2emfpeyGqW0=
|
||||
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a/go.mod h1:63s7jpZqcDAIpj8oI/1v4Izok+npJOHACFCU6+huCkM=
|
||||
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 h1:fAf7PZa2A55mCeh0KKM02f1k2Y4vEmxuZZ/51ahkkLA=
|
||||
github.com/sagernet/sing-mux v0.1.3/go.mod h1:wGeIeiiFLx4HUM5LAg65wrNZ/X1muOimqK0PEhNbPi0=
|
||||
github.com/sagernet/sing-mux v0.1.5-0.20231109075101-6b086ed6bb07 h1:ncKb5tVOsCQgCsv6UpsA0jinbNb5OQ5GMPJlyQP3EHM=
|
||||
github.com/sagernet/sing-mux v0.1.5-0.20231109075101-6b086ed6bb07/go.mod h1:u/MZf32xPG8jEKe3t+xUV67EBnKtDtCaPhsJQOQGUYU=
|
||||
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/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as=
|
||||
|
@ -164,8 +174,8 @@ 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.9 h1:ZI5bWVeu2ep4/DIxB4U9okeYJ7zp/QLTO4auRb/ty/E=
|
||||
github.com/shirou/gopsutil/v3 v3.23.9/go.mod h1:x/NWSb71eMcjFIO0vhyGW5nZ7oSIgVjrCnADckb85GA=
|
||||
github.com/shirou/gopsutil/v3 v3.23.10 h1:/N42opWlYzegYaVkWejXWJpbzKv2JDy3mrgGzKsh9hM=
|
||||
github.com/shirou/gopsutil/v3 v3.23.10/go.mod h1:JIE26kpucQi+innVlAUnIEOSBhBUkirr5b44yr55+WE=
|
||||
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=
|
||||
|
@ -199,14 +209,14 @@ github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17
|
|||
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/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
|
||||
github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
|
||||
github.com/yusufpapurcu/wmi v1.2.3/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/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo=
|
||||
|
@ -215,21 +225,21 @@ go4.org/netipx v0.0.0-20230824141953-6213f710f925 h1:eeQDDVKFkx0g4Hyy8pHgmZaK0Eq
|
|||
go4.org/netipx v0.0.0-20230824141953-6213f710f925/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
|
||||
golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
|
||||
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
|
||||
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
|
||||
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.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
|
||||
golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
|
||||
golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
|
||||
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
|
||||
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -245,17 +255,18 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
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-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc=
|
||||
golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
|
||||
golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8=
|
||||
golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk=
|
||||
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=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
|
|
|
@ -7,12 +7,9 @@ import (
|
|||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/metacubex/mihomo/ntp"
|
||||
|
||||
"github.com/metacubex/mihomo/adapter"
|
||||
"github.com/metacubex/mihomo/adapter/inbound"
|
||||
"github.com/metacubex/mihomo/adapter/outboundgroup"
|
||||
|
@ -28,6 +25,7 @@ import (
|
|||
"github.com/metacubex/mihomo/component/trie"
|
||||
"github.com/metacubex/mihomo/config"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/constant/features"
|
||||
"github.com/metacubex/mihomo/constant/provider"
|
||||
"github.com/metacubex/mihomo/dns"
|
||||
"github.com/metacubex/mihomo/listener"
|
||||
|
@ -36,6 +34,7 @@ import (
|
|||
"github.com/metacubex/mihomo/listener/inner"
|
||||
"github.com/metacubex/mihomo/listener/tproxy"
|
||||
"github.com/metacubex/mihomo/log"
|
||||
"github.com/metacubex/mihomo/ntp"
|
||||
"github.com/metacubex/mihomo/tunnel"
|
||||
)
|
||||
|
||||
|
@ -172,7 +171,9 @@ func updateListeners(general *config.General, listeners map[string]C.InboundList
|
|||
listener.ReCreateHTTP(general.Port, tunnel.Tunnel)
|
||||
listener.ReCreateSocks(general.SocksPort, tunnel.Tunnel)
|
||||
listener.ReCreateRedir(general.RedirPort, tunnel.Tunnel)
|
||||
listener.ReCreateAutoRedir(general.EBpf.AutoRedir, tunnel.Tunnel)
|
||||
if !features.CMFA {
|
||||
listener.ReCreateAutoRedir(general.EBpf.AutoRedir, tunnel.Tunnel)
|
||||
}
|
||||
listener.ReCreateTProxy(general.TProxyPort, tunnel.Tunnel)
|
||||
listener.ReCreateMixed(general.MixedPort, tunnel.Tunnel)
|
||||
listener.ReCreateShadowSocks(general.ShadowSocksConfig, tunnel.Tunnel)
|
||||
|
@ -208,25 +209,6 @@ func updateDNS(c *config.DNS, ruleProvider map[string]provider.RuleProvider, gen
|
|||
dns.ReCreateServer("", nil, nil)
|
||||
return
|
||||
}
|
||||
policy := make(map[string][]dns.NameServer)
|
||||
domainSetPolicies := make(map[provider.RuleProvider][]dns.NameServer)
|
||||
for key, nameservers := range c.NameServerPolicy {
|
||||
temp := strings.Split(key, ":")
|
||||
if len(temp) == 2 {
|
||||
prefix := temp[0]
|
||||
key := temp[1]
|
||||
switch strings.ToLower(prefix) {
|
||||
case "rule-set":
|
||||
if p, ok := ruleProvider[key]; ok {
|
||||
domainSetPolicies[p] = nameservers
|
||||
}
|
||||
case "geosite":
|
||||
// TODO:
|
||||
}
|
||||
} else {
|
||||
policy[key] = nameservers
|
||||
}
|
||||
}
|
||||
cfg := dns.Config{
|
||||
Main: c.NameServer,
|
||||
Fallback: c.Fallback,
|
||||
|
@ -242,10 +224,11 @@ func updateDNS(c *config.DNS, ruleProvider map[string]provider.RuleProvider, gen
|
|||
Domain: c.FallbackFilter.Domain,
|
||||
GeoSite: c.FallbackFilter.GeoSite,
|
||||
},
|
||||
Default: c.DefaultNameserver,
|
||||
Policy: c.NameServerPolicy,
|
||||
ProxyServer: c.ProxyServerNameserver,
|
||||
DomainSetPolicy: domainSetPolicies,
|
||||
Default: c.DefaultNameserver,
|
||||
Policy: c.NameServerPolicy,
|
||||
ProxyServer: c.ProxyServerNameserver,
|
||||
RuleProviders: ruleProvider,
|
||||
CacheAlgorithm: c.CacheAlgorithm,
|
||||
}
|
||||
|
||||
r := dns.NewResolver(cfg)
|
||||
|
@ -330,6 +313,9 @@ func loadProxyProvider(proxyProviders map[string]provider.ProxyProvider) {
|
|||
go func() {
|
||||
defer func() { <-ch; wg.Done() }()
|
||||
loadProvider(proxyProvider)
|
||||
if proxyProvider.VehicleType() == provider.Compatible {
|
||||
go proxyProvider.HealthCheck()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
|
|
|
@ -125,7 +125,7 @@ func getProxyDelay(w http.ResponseWriter, r *http.Request) {
|
|||
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*time.Duration(timeout))
|
||||
defer cancel()
|
||||
|
||||
delay, err := proxy.URLTest(ctx, url, expectedStatus, C.ExtraHistory)
|
||||
delay, err := proxy.URLTest(ctx, url, expectedStatus)
|
||||
if ctx.Err() != nil {
|
||||
render.Status(r, http.StatusGatewayTimeout)
|
||||
render.JSON(w, r, ErrRequestTimeout)
|
||||
|
|
|
@ -89,7 +89,7 @@ func Start(addr string, tlsAddr string, secret string,
|
|||
r.Mount("/connections", connectionRouter())
|
||||
r.Mount("/providers/proxies", proxyProviderRouter())
|
||||
r.Mount("/providers/rules", ruleProviderRouter())
|
||||
r.Mount("/cache", cacheRouter())
|
||||
r.Mount("/lru", cacheRouter())
|
||||
r.Mount("/dns", dnsRouter())
|
||||
r.Mount("/restart", restartRouter())
|
||||
r.Mount("/upgrade", upgradeRouter())
|
||||
|
|
|
@ -17,6 +17,7 @@ import (
|
|||
|
||||
mihomoHttp "github.com/metacubex/mihomo/component/http"
|
||||
"github.com/metacubex/mihomo/constant"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/log"
|
||||
|
||||
"github.com/klauspost/cpuid/v2"
|
||||
|
@ -231,7 +232,7 @@ const MaxPackageFileSize = 32 * 1024 * 1024
|
|||
func downloadPackageFile() (err error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*90)
|
||||
defer cancel()
|
||||
resp, err := mihomoHttp.HttpRequest(ctx, packageURL, http.MethodGet, http.Header{"User-Agent": {"mihomo"}}, nil)
|
||||
resp, err := mihomoHttp.HttpRequest(ctx, packageURL, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("http request failed: %w", err)
|
||||
}
|
||||
|
@ -412,7 +413,7 @@ func copyFile(src, dst string) error {
|
|||
func getLatestVersion() (version string, err error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||
defer cancel()
|
||||
resp, err := mihomoHttp.HttpRequest(ctx, versionURL, http.MethodGet, http.Header{"User-Agent": {"mihomo"}}, nil)
|
||||
resp, err := mihomoHttp.HttpRequest(ctx, versionURL, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("get Latest Version fail: %w", err)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
package config
|
||||
|
||||
import "encoding/json"
|
||||
import (
|
||||
"github.com/metacubex/mihomo/listener/sing"
|
||||
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type Hysteria2Server struct {
|
||||
Enable bool `yaml:"enable" json:"enable"`
|
||||
|
@ -17,6 +21,7 @@ type Hysteria2Server struct {
|
|||
IgnoreClientBandwidth bool `yaml:"ignore-client-bandwidth" json:"ignore-client-bandwidth,omitempty"`
|
||||
Masquerade string `yaml:"masquerade" json:"masquerade,omitempty"`
|
||||
CWND int `yaml:"cwnd" json:"cwnd,omitempty"`
|
||||
MuxOption sing.MuxOption `yaml:"mux-option" json:"mux-option,omitempty"`
|
||||
}
|
||||
|
||||
func (h Hysteria2Server) String() string {
|
||||
|
|
|
@ -1,15 +1,18 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"github.com/metacubex/mihomo/listener/sing"
|
||||
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type ShadowsocksServer struct {
|
||||
Enable bool
|
||||
Listen string
|
||||
Password string
|
||||
Cipher string
|
||||
Udp bool
|
||||
Enable bool
|
||||
Listen string
|
||||
Password string
|
||||
Cipher string
|
||||
Udp bool
|
||||
MuxOption sing.MuxOption `yaml:"mux-option" json:"mux-option,omitempty"`
|
||||
}
|
||||
|
||||
func (t ShadowsocksServer) String() string {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"github.com/metacubex/mihomo/listener/sing"
|
||||
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
|
@ -18,6 +20,7 @@ type TuicServer struct {
|
|||
MaxUdpRelayPacketSize int `yaml:"max-udp-relay-packet-size" json:"max-udp-relay-packet-size,omitempty"`
|
||||
MaxDatagramFrameSize int `yaml:"max-datagram-frame-size" json:"max-datagram-frame-size,omitempty"`
|
||||
CWND int `yaml:"cwnd" json:"cwnd,omitempty"`
|
||||
MuxOption sing.MuxOption `yaml:"mux-option" json:"mux-option,omitempty"`
|
||||
}
|
||||
|
||||
func (t TuicServer) String() string {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"github.com/metacubex/mihomo/listener/sing"
|
||||
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
|
@ -17,6 +19,7 @@ type VmessServer struct {
|
|||
WsPath string
|
||||
Certificate string
|
||||
PrivateKey string
|
||||
MuxOption sing.MuxOption `yaml:"mux-option" json:"mux-option,omitempty"`
|
||||
}
|
||||
|
||||
func (t VmessServer) String() string {
|
||||
|
|
9
listener/http/patch_android.go
Normal file
9
listener/http/patch_android.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
//go:build android && cmfa
|
||||
|
||||
package http
|
||||
|
||||
import "net"
|
||||
|
||||
func (l *Listener) Listener() net.Listener {
|
||||
return l.listener
|
||||
}
|
|
@ -7,21 +7,21 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/metacubex/mihomo/adapter/inbound"
|
||||
"github.com/metacubex/mihomo/common/cache"
|
||||
"github.com/metacubex/mihomo/common/lru"
|
||||
N "github.com/metacubex/mihomo/common/net"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
authStore "github.com/metacubex/mihomo/listener/auth"
|
||||
"github.com/metacubex/mihomo/log"
|
||||
)
|
||||
|
||||
func HandleConn(c net.Conn, tunnel C.Tunnel, cache *cache.LruCache[string, bool], additions ...inbound.Addition) {
|
||||
func HandleConn(c net.Conn, tunnel C.Tunnel, cache *lru.LruCache[string, bool], additions ...inbound.Addition) {
|
||||
client := newClient(c, tunnel, additions...)
|
||||
defer client.CloseIdleConnections()
|
||||
|
||||
conn := N.NewBufferedConn(c)
|
||||
|
||||
keepAlive := true
|
||||
trusted := cache == nil // disable authenticate if cache is nil
|
||||
trusted := cache == nil // disable authenticate if lru is nil
|
||||
|
||||
for keepAlive {
|
||||
request, err := ReadRequest(conn.Reader())
|
||||
|
@ -98,7 +98,7 @@ func HandleConn(c net.Conn, tunnel C.Tunnel, cache *cache.LruCache[string, bool]
|
|||
_ = conn.Close()
|
||||
}
|
||||
|
||||
func authenticate(request *http.Request, cache *cache.LruCache[string, bool]) *http.Response {
|
||||
func authenticate(request *http.Request, cache *lru.LruCache[string, bool]) *http.Response {
|
||||
authenticator := authStore.Authenticator()
|
||||
if inbound.SkipAuthRemoteAddress(request.RemoteAddr) {
|
||||
authenticator = nil
|
||||
|
|
|
@ -4,8 +4,9 @@ import (
|
|||
"net"
|
||||
|
||||
"github.com/metacubex/mihomo/adapter/inbound"
|
||||
"github.com/metacubex/mihomo/common/cache"
|
||||
"github.com/metacubex/mihomo/common/lru"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/constant/features"
|
||||
)
|
||||
|
||||
type Listener struct {
|
||||
|
@ -47,9 +48,9 @@ func NewWithAuthenticate(addr string, tunnel C.Tunnel, authenticate bool, additi
|
|||
return nil, err
|
||||
}
|
||||
|
||||
var c *cache.LruCache[string, bool]
|
||||
var c *lru.LruCache[string, bool]
|
||||
if authenticate {
|
||||
c = cache.New[string, bool](cache.WithAge[string, bool](30))
|
||||
c = lru.New[string, bool](lru.WithAge[string, bool](30))
|
||||
}
|
||||
|
||||
hl := &Listener{
|
||||
|
@ -65,6 +66,11 @@ func NewWithAuthenticate(addr string, tunnel C.Tunnel, authenticate bool, additi
|
|||
}
|
||||
continue
|
||||
}
|
||||
if features.CMFA {
|
||||
if t, ok := conn.(*net.TCPConn); ok {
|
||||
t.SetKeepAlive(false)
|
||||
}
|
||||
}
|
||||
go HandleConn(conn, tunnel, c, additions...)
|
||||
}
|
||||
}()
|
||||
|
|
|
@ -21,6 +21,7 @@ type Hysteria2Option struct {
|
|||
IgnoreClientBandwidth bool `inbound:"ignore-client-bandwidth,omitempty"`
|
||||
Masquerade string `inbound:"masquerade,omitempty"`
|
||||
CWND int `inbound:"cwnd,omitempty"`
|
||||
MuxOption MuxOption `inbound:"mux-option,omitempty"`
|
||||
}
|
||||
|
||||
func (o Hysteria2Option) Equal(config C.InboundConfig) bool {
|
||||
|
@ -57,6 +58,7 @@ func NewHysteria2(options *Hysteria2Option) (*Hysteria2, error) {
|
|||
IgnoreClientBandwidth: options.IgnoreClientBandwidth,
|
||||
Masquerade: options.Masquerade,
|
||||
CWND: options.CWND,
|
||||
MuxOption: options.MuxOption.Build(),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
|
25
listener/inbound/mux.go
Normal file
25
listener/inbound/mux.go
Normal file
|
@ -0,0 +1,25 @@
|
|||
package inbound
|
||||
|
||||
import "github.com/metacubex/mihomo/listener/sing"
|
||||
|
||||
type MuxOption struct {
|
||||
Padding bool `inbound:"padding,omitempty"`
|
||||
Brutal BrutalOptions `inbound:"brutal,omitempty"`
|
||||
}
|
||||
|
||||
type BrutalOptions struct {
|
||||
Enabled bool `inbound:"enabled,omitempty"`
|
||||
Up string `inbound:"up,omitempty"`
|
||||
Down string `inbound:"down,omitempty"`
|
||||
}
|
||||
|
||||
func (m MuxOption) Build() sing.MuxOption {
|
||||
return sing.MuxOption{
|
||||
Padding: m.Padding,
|
||||
Brutal: sing.BrutalOptions{
|
||||
Enabled: m.Brutal.Enabled,
|
||||
Up: m.Brutal.Up,
|
||||
Down: m.Brutal.Down,
|
||||
},
|
||||
}
|
||||
}
|
|
@ -9,9 +9,10 @@ import (
|
|||
|
||||
type ShadowSocksOption struct {
|
||||
BaseOption
|
||||
Password string `inbound:"password"`
|
||||
Cipher string `inbound:"cipher"`
|
||||
UDP bool `inbound:"udp,omitempty"`
|
||||
Password string `inbound:"password"`
|
||||
Cipher string `inbound:"cipher"`
|
||||
UDP bool `inbound:"udp,omitempty"`
|
||||
MuxOption MuxOption `inbound:"mux-option,omitempty"`
|
||||
}
|
||||
|
||||
func (o ShadowSocksOption) Equal(config C.InboundConfig) bool {
|
||||
|
@ -34,11 +35,12 @@ func NewShadowSocks(options *ShadowSocksOption) (*ShadowSocks, error) {
|
|||
Base: base,
|
||||
config: options,
|
||||
ss: LC.ShadowsocksServer{
|
||||
Enable: true,
|
||||
Listen: base.RawAddress(),
|
||||
Password: options.Password,
|
||||
Cipher: options.Cipher,
|
||||
Udp: options.UDP,
|
||||
Enable: true,
|
||||
Listen: base.RawAddress(),
|
||||
Password: options.Password,
|
||||
Cipher: options.Cipher,
|
||||
Udp: options.UDP,
|
||||
MuxOption: options.MuxOption.Build(),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ type TuicOption struct {
|
|||
ALPN []string `inbound:"alpn,omitempty"`
|
||||
MaxUdpRelayPacketSize int `inbound:"max-udp-relay-packet-size,omitempty"`
|
||||
CWND int `inbound:"cwnd,omitempty"`
|
||||
MuxOption MuxOption `inbound:"mux-option,omitempty"`
|
||||
}
|
||||
|
||||
func (o TuicOption) Equal(config C.InboundConfig) bool {
|
||||
|
@ -53,6 +54,7 @@ func NewTuic(options *TuicOption) (*Tuic, error) {
|
|||
ALPN: options.ALPN,
|
||||
MaxUdpRelayPacketSize: options.MaxUdpRelayPacketSize,
|
||||
CWND: options.CWND,
|
||||
MuxOption: options.MuxOption.Build(),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ type VmessOption struct {
|
|||
WsPath string `inbound:"ws-path,omitempty"`
|
||||
Certificate string `inbound:"certificate,omitempty"`
|
||||
PrivateKey string `inbound:"private-key,omitempty"`
|
||||
MuxOption MuxOption `inbound:"mux-option,omitempty"`
|
||||
}
|
||||
|
||||
type VmessUser struct {
|
||||
|
@ -55,6 +56,7 @@ func NewVmess(options *VmessOption) (*Vmess, error) {
|
|||
WsPath: options.WsPath,
|
||||
Certificate: options.Certificate,
|
||||
PrivateKey: options.PrivateKey,
|
||||
MuxOption: options.MuxOption.Build(),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"net"
|
||||
|
||||
"github.com/metacubex/mihomo/adapter/inbound"
|
||||
"github.com/metacubex/mihomo/common/cache"
|
||||
"github.com/metacubex/mihomo/common/lru"
|
||||
N "github.com/metacubex/mihomo/common/net"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/listener/http"
|
||||
|
@ -16,7 +16,7 @@ import (
|
|||
type Listener struct {
|
||||
listener net.Listener
|
||||
addr string
|
||||
cache *cache.LruCache[string, bool]
|
||||
cache *lru.LruCache[string, bool]
|
||||
closed bool
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,7 @@ func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener
|
|||
ml := &Listener{
|
||||
listener: l,
|
||||
addr: addr,
|
||||
cache: cache.New[string, bool](cache.WithAge[string, bool](30)),
|
||||
cache: lru.New[string, bool](lru.WithAge[string, bool](30)),
|
||||
}
|
||||
go func() {
|
||||
for {
|
||||
|
@ -69,7 +69,7 @@ func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener
|
|||
return ml, nil
|
||||
}
|
||||
|
||||
func handleConn(conn net.Conn, tunnel C.Tunnel, cache *cache.LruCache[string, bool], additions ...inbound.Addition) {
|
||||
func handleConn(conn net.Conn, tunnel C.Tunnel, cache *lru.LruCache[string, bool], additions ...inbound.Addition) {
|
||||
N.TCPKeepAlive(conn)
|
||||
|
||||
bufConn := N.NewBufferedConn(conn)
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/metacubex/mihomo/adapter/inbound"
|
||||
"github.com/metacubex/mihomo/adapter/outbound"
|
||||
N "github.com/metacubex/mihomo/common/net"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/log"
|
||||
|
@ -26,11 +27,28 @@ import (
|
|||
|
||||
const UDPTimeout = 5 * time.Minute
|
||||
|
||||
type ListenerHandler struct {
|
||||
type ListenerConfig struct {
|
||||
Tunnel C.Tunnel
|
||||
Type C.Type
|
||||
Additions []inbound.Addition
|
||||
UDPTimeout time.Duration
|
||||
MuxOption MuxOption
|
||||
}
|
||||
|
||||
type MuxOption struct {
|
||||
Padding bool `yaml:"padding" json:"padding,omitempty"`
|
||||
Brutal BrutalOptions `yaml:"brutal" json:"brutal,omitempty"`
|
||||
}
|
||||
|
||||
type BrutalOptions struct {
|
||||
Enabled bool `yaml:"enabled" json:"enabled"`
|
||||
Up string `yaml:"up" json:"up,omitempty"`
|
||||
Down string `yaml:"down" json:"down,omitempty"`
|
||||
}
|
||||
|
||||
type ListenerHandler struct {
|
||||
ListenerConfig
|
||||
muxService *mux.Service
|
||||
}
|
||||
|
||||
func UpstreamMetadata(metadata M.Metadata) M.Metadata {
|
||||
|
@ -48,22 +66,40 @@ func ConvertMetadata(metadata *C.Metadata) M.Metadata {
|
|||
}
|
||||
}
|
||||
|
||||
func NewListenerHandler(lc ListenerConfig) (h *ListenerHandler, err error) {
|
||||
h = &ListenerHandler{ListenerConfig: lc}
|
||||
h.muxService, err = mux.NewService(mux.ServiceOptions{
|
||||
NewStreamContext: func(ctx context.Context, conn net.Conn) context.Context {
|
||||
return ctx
|
||||
},
|
||||
Logger: log.SingLogger,
|
||||
Handler: h,
|
||||
Padding: lc.MuxOption.Padding,
|
||||
Brutal: mux.BrutalOptions{
|
||||
Enabled: lc.MuxOption.Brutal.Enabled,
|
||||
SendBPS: outbound.StringToBps(lc.MuxOption.Brutal.Up),
|
||||
ReceiveBPS: outbound.StringToBps(lc.MuxOption.Brutal.Down),
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
func (h *ListenerHandler) IsSpecialFqdn(fqdn string) bool {
|
||||
switch fqdn {
|
||||
case mux.Destination.Fqdn:
|
||||
case vmess.MuxDestination.Fqdn:
|
||||
case uot.MagicAddress:
|
||||
case uot.LegacyMagicAddress:
|
||||
case mux.Destination.Fqdn,
|
||||
vmess.MuxDestination.Fqdn,
|
||||
uot.MagicAddress,
|
||||
uot.LegacyMagicAddress:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (h *ListenerHandler) ParseSpecialFqdn(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||
switch metadata.Destination.Fqdn {
|
||||
case mux.Destination.Fqdn:
|
||||
return mux.HandleConnection(ctx, h, log.SingLogger, conn, UpstreamMetadata(metadata))
|
||||
return h.muxService.NewConnection(ctx, conn, UpstreamMetadata(metadata))
|
||||
case vmess.MuxDestination.Fqdn:
|
||||
return vmess.HandleMuxConnection(ctx, conn, h)
|
||||
case uot.MagicAddress:
|
||||
|
|
|
@ -42,10 +42,14 @@ func New(config LC.Hysteria2Server, tunnel C.Tunnel, additions ...inbound.Additi
|
|||
}
|
||||
}
|
||||
|
||||
h := &sing.ListenerHandler{
|
||||
h, err := sing.NewListenerHandler(sing.ListenerConfig{
|
||||
Tunnel: tunnel,
|
||||
Type: C.HYSTERIA2,
|
||||
Additions: additions,
|
||||
MuxOption: config.MuxOption,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sl = &Listener{false, config, nil, nil}
|
||||
|
|
|
@ -50,10 +50,14 @@ func New(config LC.ShadowsocksServer, tunnel C.Tunnel, additions ...inbound.Addi
|
|||
|
||||
udpTimeout := int64(sing.UDPTimeout.Seconds())
|
||||
|
||||
h := &sing.ListenerHandler{
|
||||
h, err := sing.NewListenerHandler(sing.ListenerConfig{
|
||||
Tunnel: tunnel,
|
||||
Type: C.SHADOWSOCKS,
|
||||
Additions: additions,
|
||||
MuxOption: config.MuxOption,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sl = &Listener{false, config, nil, nil, nil}
|
||||
|
|
|
@ -26,7 +26,7 @@ const DefaultDnsReadTimeout = time.Second * 10
|
|||
const DefaultDnsRelayTimeout = time.Second * 5
|
||||
|
||||
type ListenerHandler struct {
|
||||
sing.ListenerHandler
|
||||
*sing.ListenerHandler
|
||||
DnsAdds []netip.AddrPort
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue