Compare commits
63 Commits
Author | SHA1 | Date | |
---|---|---|---|
190c930294 | |||
d578ca788c | |||
9602d42d7d | |||
55c7c4edb3 | |||
20cb4d0643 | |||
91b4176557 | |||
f03f33840e | |||
8ff7e180a4 | |||
3827e00b54 | |||
c52e689d0d | |||
8e959bd245 | |||
298ca42369 | |||
ed9b9ce3c5 | |||
1298d2f8b6 | |||
6e84f685ce | |||
1ad87cfec9 | |||
582c0763ba | |||
9e9f459c0e | |||
fa3e0c726e | |||
3b038310ab | |||
6709936a8f | |||
04e5d02ab9 | |||
1af39cb228 | |||
c95735f083 | |||
fa2e6be05d | |||
4092a7c84b | |||
58d299c737 | |||
11ddac2b5f | |||
1f95c74f1e | |||
c9616f70b7 | |||
a197fbd4b5 | |||
708b8beadf | |||
0e1601e5b6 | |||
c7355510a2 | |||
1faa172944 | |||
7a8c98cd90 | |||
13e907bbd0 | |||
39e7832676 | |||
9b999e72ce | |||
e1a61503e4 | |||
9272d02149 | |||
067c02aba1 | |||
d0268bb9a2 | |||
fb4872ff7f | |||
2044458df9 | |||
d6df026550 | |||
7858ca6cc5 | |||
ac36473d13 | |||
72fb153fe0 | |||
527a602eba | |||
a71fd3b4df | |||
2ebc0383b5 | |||
7431001ed6 | |||
149b4b5b43 | |||
c0eb9aac1c | |||
79469fc8d6 | |||
948700eed6 | |||
3ab82849d4 | |||
0f43a19fdb | |||
cc1c1340a3 | |||
fe25ae83df | |||
c787bbe0e5 | |||
7aff9aac82 |
2
.github/workflows/prerelease.yml
vendored
2
.github/workflows/prerelease.yml
vendored
@ -43,7 +43,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
NAME: Clash.Meta
|
NAME: Clash.Meta
|
||||||
BINDIR: bin
|
BINDIR: bin
|
||||||
run: make -j releases
|
run: make -j$(($(nproc) + 1)) releases
|
||||||
|
|
||||||
- name: Delete current release assets
|
- name: Delete current release assets
|
||||||
uses: andreaswilli/delete-release-assets-action@v2.0.0
|
uses: andreaswilli/delete-release-assets-action@v2.0.0
|
||||||
|
64
.github/workflows/product.yaml
vendored
Normal file
64
.github/workflows/product.yaml
vendored
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
name: Build
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- release
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
Build:
|
||||||
|
runs-on: self-hosted
|
||||||
|
steps:
|
||||||
|
- name: Get latest go version
|
||||||
|
id: version
|
||||||
|
run: |
|
||||||
|
echo ::set-output name=go_version::$(curl -s https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json | grep -oE '"version": "[0-9]{1}.[0-9]{1,}(.[0-9]{1,})?"' | head -1 | cut -d':' -f2 | sed 's/ //g; s/"//g')
|
||||||
|
- name: Setup Go
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: ${{ steps.version.outputs.go_version }}
|
||||||
|
|
||||||
|
- name: Check out code into the Go module directory
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Cache go module
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: ~/go/pkg/mod
|
||||||
|
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-go-
|
||||||
|
- name: Test
|
||||||
|
run: |
|
||||||
|
go test ./...
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
if: success()
|
||||||
|
env:
|
||||||
|
NAME: Clash.Meta
|
||||||
|
BINDIR: bin
|
||||||
|
run: make -j$(($(nproc) + 1)) linux-amd64v2 windows-amd64v2 darwin-arm64 linux-amd64v3 windows-amd64v3 linux-arm64
|
||||||
|
|
||||||
|
- name: Delete current release assets
|
||||||
|
uses: andreaswilli/delete-release-assets-action@v2.0.0
|
||||||
|
with:
|
||||||
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
tag: test
|
||||||
|
deleteOnlyFromDrafts: false
|
||||||
|
|
||||||
|
- name: Tag Repo
|
||||||
|
uses: richardsimko/update-tag@v1
|
||||||
|
with:
|
||||||
|
tag_name: test
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Upload Alpha
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
if: ${{ success() }}
|
||||||
|
with:
|
||||||
|
tag: test
|
||||||
|
tag_name: test
|
||||||
|
files: bin/*
|
||||||
|
prerelease: true
|
||||||
|
generate_release_notes: true
|
2
.github/workflows/release.yaml
vendored
2
.github/workflows/release.yaml
vendored
@ -33,7 +33,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
NAME: Clash.Meta
|
NAME: Clash.Meta
|
||||||
BINDIR: bin
|
BINDIR: bin
|
||||||
run: make -j releases
|
run: make -j$(($(nproc) + 1)) releases
|
||||||
|
|
||||||
- name: Upload Release
|
- name: Upload Release
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v1
|
||||||
|
6
Makefile
6
Makefile
@ -47,9 +47,9 @@ WINDOWS_ARCH_LIST = \
|
|||||||
windows-arm64 \
|
windows-arm64 \
|
||||||
windows-arm32v7
|
windows-arm32v7
|
||||||
|
|
||||||
all:linux-amd64 linux-arm64\
|
all:linux-amd64v3 linux-arm64\
|
||||||
darwin-amd64 darwin-arm64\
|
darwin-amd64v3 darwin-arm64\
|
||||||
windows-amd64 windows-arm64\
|
windows-amd64v3 windows-arm64\
|
||||||
|
|
||||||
docker:
|
docker:
|
||||||
GOAMD64=v3 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@
|
GOAMD64=v3 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@
|
||||||
|
@ -4,6 +4,9 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/Dreamacro/clash/common/queue"
|
||||||
|
"github.com/Dreamacro/clash/component/dialer"
|
||||||
|
C "github.com/Dreamacro/clash/constant"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
@ -11,10 +14,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/common/queue"
|
|
||||||
"github.com/Dreamacro/clash/component/dialer"
|
|
||||||
C "github.com/Dreamacro/clash/constant"
|
|
||||||
|
|
||||||
"go.uber.org/atomic"
|
"go.uber.org/atomic"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ import (
|
|||||||
// NewHTTPS receive CONNECT request and return ConnContext
|
// NewHTTPS receive CONNECT request and return ConnContext
|
||||||
func NewHTTPS(request *http.Request, conn net.Conn) *context.ConnContext {
|
func NewHTTPS(request *http.Request, conn net.Conn) *context.ConnContext {
|
||||||
metadata := parseHTTPAddr(request)
|
metadata := parseHTTPAddr(request)
|
||||||
metadata.Type = C.HTTPCONNECT
|
metadata.Type = C.HTTPS
|
||||||
if ip, port, err := parseAddr(conn.RemoteAddr().String()); err == nil {
|
if ip, port, err := parseAddr(conn.RemoteAddr().String()); err == nil {
|
||||||
metadata.SrcIP = ip
|
metadata.SrcIP = ip
|
||||||
metadata.SrcPort = port
|
metadata.SrcPort = port
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"github.com/gofrs/uuid"
|
"github.com/gofrs/uuid"
|
||||||
"net"
|
"net"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/component/dialer"
|
"github.com/Dreamacro/clash/component/dialer"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
@ -50,6 +51,10 @@ func (b *Base) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
|
|||||||
return c, errors.New("no support")
|
return c, errors.New("no support")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *Base) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) {
|
||||||
|
return nil, errors.New("no support")
|
||||||
|
}
|
||||||
|
|
||||||
// ListenPacketContext implements C.ProxyAdapter
|
// ListenPacketContext implements C.ProxyAdapter
|
||||||
func (b *Base) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) {
|
func (b *Base) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) {
|
||||||
return nil, errors.New("no support")
|
return nil, errors.New("no support")
|
||||||
@ -128,7 +133,12 @@ func NewBase(opt BaseOption) *Base {
|
|||||||
|
|
||||||
type conn struct {
|
type conn struct {
|
||||||
net.Conn
|
net.Conn
|
||||||
chain C.Chain
|
chain C.Chain
|
||||||
|
actualRemoteDestination string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *conn) RemoteDestination() string {
|
||||||
|
return c.actualRemoteDestination
|
||||||
}
|
}
|
||||||
|
|
||||||
// Chains implements C.Connection
|
// Chains implements C.Connection
|
||||||
@ -142,12 +152,17 @@ func (c *conn) AppendToChains(a C.ProxyAdapter) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewConn(c net.Conn, a C.ProxyAdapter) C.Conn {
|
func NewConn(c net.Conn, a C.ProxyAdapter) C.Conn {
|
||||||
return &conn{c, []string{a.Name()}}
|
return &conn{c, []string{a.Name()}, parseRemoteDestination(a.Addr())}
|
||||||
}
|
}
|
||||||
|
|
||||||
type packetConn struct {
|
type packetConn struct {
|
||||||
net.PacketConn
|
net.PacketConn
|
||||||
chain C.Chain
|
chain C.Chain
|
||||||
|
actualRemoteDestination string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *packetConn) RemoteDestination() string {
|
||||||
|
return c.actualRemoteDestination
|
||||||
}
|
}
|
||||||
|
|
||||||
// Chains implements C.Connection
|
// Chains implements C.Connection
|
||||||
@ -161,5 +176,17 @@ func (c *packetConn) AppendToChains(a C.ProxyAdapter) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newPacketConn(pc net.PacketConn, a C.ProxyAdapter) C.PacketConn {
|
func newPacketConn(pc net.PacketConn, a C.ProxyAdapter) C.PacketConn {
|
||||||
return &packetConn{pc, []string{a.Name()}}
|
return &packetConn{pc, []string{a.Name()}, parseRemoteDestination(a.Addr())}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseRemoteDestination(addr string) string {
|
||||||
|
if dst, _, err := net.SplitHostPort(addr); err == nil {
|
||||||
|
return dst
|
||||||
|
} else {
|
||||||
|
if addrError, ok := err.(*net.AddrError); ok && strings.Contains(addrError.Err, "missing port") {
|
||||||
|
return dst
|
||||||
|
} else {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,15 +3,19 @@ package outboundgroup
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"github.com/Dreamacro/clash/adapter/outbound"
|
"github.com/Dreamacro/clash/adapter/outbound"
|
||||||
"github.com/Dreamacro/clash/component/dialer"
|
"github.com/Dreamacro/clash/component/dialer"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
"github.com/Dreamacro/clash/constant/provider"
|
"github.com/Dreamacro/clash/constant/provider"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Fallback struct {
|
type Fallback struct {
|
||||||
*GroupBase
|
*GroupBase
|
||||||
disableUDP bool
|
disableUDP bool
|
||||||
|
testUrl string
|
||||||
|
selected string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Fallback) Now() string {
|
func (f *Fallback) Now() string {
|
||||||
@ -75,13 +79,40 @@ func (f *Fallback) Unwrap(metadata *C.Metadata) C.Proxy {
|
|||||||
|
|
||||||
func (f *Fallback) findAliveProxy(touch bool) C.Proxy {
|
func (f *Fallback) findAliveProxy(touch bool) C.Proxy {
|
||||||
proxies := f.GetProxies(touch)
|
proxies := f.GetProxies(touch)
|
||||||
for _, proxy := range proxies {
|
al := proxies[0]
|
||||||
if proxy.Alive() {
|
for i := len(proxies) - 1; i > -1; i-- {
|
||||||
|
proxy := proxies[i]
|
||||||
|
if proxy.Name() == f.selected && proxy.Alive() {
|
||||||
return proxy
|
return proxy
|
||||||
}
|
}
|
||||||
|
if proxy.Alive() {
|
||||||
|
al = proxy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return al
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Fallback) Set(name string) error {
|
||||||
|
var p C.Proxy
|
||||||
|
for _, proxy := range f.GetProxies(false) {
|
||||||
|
if proxy.Name() == name {
|
||||||
|
p = proxy
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return proxies[0]
|
if p == nil {
|
||||||
|
return errors.New("proxy not exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
f.selected = name
|
||||||
|
if !p.Alive() {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*time.Duration(5000))
|
||||||
|
defer cancel()
|
||||||
|
_, _ = p.URLTest(ctx, f.testUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFallback(option *GroupCommonOption, providers []provider.ProxyProvider) *Fallback {
|
func NewFallback(option *GroupCommonOption, providers []provider.ProxyProvider) *Fallback {
|
||||||
@ -97,5 +128,6 @@ func NewFallback(option *GroupCommonOption, providers []provider.ProxyProvider)
|
|||||||
providers,
|
providers,
|
||||||
}),
|
}),
|
||||||
disableUDP: option.DisableUDP,
|
disableUDP: option.DisableUDP,
|
||||||
|
testUrl: option.URL,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package outboundgroup
|
package outboundgroup
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
"github.com/Dreamacro/clash/adapter/outbound"
|
"github.com/Dreamacro/clash/adapter/outbound"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
"github.com/Dreamacro/clash/constant/provider"
|
"github.com/Dreamacro/clash/constant/provider"
|
||||||
@ -105,6 +107,34 @@ func (gb *GroupBase) GetProxies(touch bool) []C.Proxy {
|
|||||||
return proxies
|
return proxies
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (gb *GroupBase) URLTest(ctx context.Context, url string) (map[string]uint16, error) {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
var lock sync.Mutex
|
||||||
|
mp := map[string]uint16{}
|
||||||
|
proxies := gb.GetProxies(false)
|
||||||
|
for _, proxy := range proxies {
|
||||||
|
proxy := proxy
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
delay, err := proxy.URLTest(ctx, url)
|
||||||
|
lock.Lock()
|
||||||
|
if err == nil {
|
||||||
|
mp[proxy.Name()] = delay
|
||||||
|
}
|
||||||
|
lock.Unlock()
|
||||||
|
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
if len(mp) == 0 {
|
||||||
|
return mp, fmt.Errorf("get delay: all proxies timeout")
|
||||||
|
} else {
|
||||||
|
return mp, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (gb *GroupBase) onDialFailed() {
|
func (gb *GroupBase) onDialFailed() {
|
||||||
if gb.failedTesting.Load() {
|
if gb.failedTesting.Load() {
|
||||||
return
|
return
|
||||||
@ -116,14 +146,14 @@ func (gb *GroupBase) onDialFailed() {
|
|||||||
|
|
||||||
gb.failedTimes++
|
gb.failedTimes++
|
||||||
if gb.failedTimes == 1 {
|
if gb.failedTimes == 1 {
|
||||||
log.Warnln("ProxyGroup: %s first failed", gb.Name())
|
log.Debugln("ProxyGroup: %s first failed", gb.Name())
|
||||||
gb.failedTime = time.Now()
|
gb.failedTime = time.Now()
|
||||||
} else {
|
} else {
|
||||||
if time.Since(gb.failedTime) > gb.failedTimeoutInterval() {
|
if time.Since(gb.failedTime) > gb.failedTimeoutInterval() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Warnln("ProxyGroup: %s failed count: %d", gb.Name(), gb.failedTimes)
|
log.Debugln("ProxyGroup: %s failed count: %d", gb.Name(), gb.failedTimes)
|
||||||
if gb.failedTimes >= gb.maxFailedTimes() {
|
if gb.failedTimes >= gb.maxFailedTimes() {
|
||||||
gb.failedTesting.Store(true)
|
gb.failedTesting.Store(true)
|
||||||
log.Warnln("because %s failed multiple times, active health check", gb.Name())
|
log.Warnln("because %s failed multiple times, active health check", gb.Name())
|
||||||
|
@ -151,29 +151,35 @@ func strategyConsistentHashing() strategyFn {
|
|||||||
|
|
||||||
func strategyStickySessions() strategyFn {
|
func strategyStickySessions() strategyFn {
|
||||||
ttl := time.Minute * 10
|
ttl := time.Minute * 10
|
||||||
|
maxRetry := 5
|
||||||
c := cache.New[uint64, int](1 * time.Second)
|
lruCache := cache.NewLRUCache[uint64, int](
|
||||||
|
cache.WithAge[uint64, int](int64(ttl.Seconds())),
|
||||||
|
cache.WithSize[uint64, int](1000))
|
||||||
return func(proxies []C.Proxy, metadata *C.Metadata) C.Proxy {
|
return func(proxies []C.Proxy, metadata *C.Metadata) C.Proxy {
|
||||||
key := uint64(murmur3.Sum32([]byte(getKeyWithSrcAndDst(metadata))))
|
key := uint64(murmur3.Sum32([]byte(getKeyWithSrcAndDst(metadata))))
|
||||||
length := len(proxies)
|
length := len(proxies)
|
||||||
idx, expireTime := c.GetWithExpire(key)
|
idx, has := lruCache.Get(key)
|
||||||
if expireTime.IsZero() {
|
if !has {
|
||||||
idx = int(jumpHash(key+uint64(time.Now().UnixMilli()), int32(length)))
|
idx = int(jumpHash(key+uint64(time.Now().UnixNano()), int32(length)))
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < length; i++ {
|
nowIdx := idx
|
||||||
nowIdx := (idx + 1) % length
|
for i := 1; i < maxRetry; i++ {
|
||||||
proxy := proxies[nowIdx]
|
proxy := proxies[nowIdx]
|
||||||
if proxy.Alive() {
|
if proxy.Alive() {
|
||||||
if nowIdx != idx {
|
if nowIdx != idx {
|
||||||
c.Put(key, idx, -1)
|
lruCache.Delete(key)
|
||||||
c.Put(key, nowIdx, ttl)
|
lruCache.Set(key, nowIdx)
|
||||||
}
|
}
|
||||||
|
|
||||||
return proxy
|
return proxy
|
||||||
|
} else {
|
||||||
|
nowIdx = int(jumpHash(key+uint64(time.Now().UnixNano()), int32(length)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lruCache.Delete(key)
|
||||||
|
lruCache.Set(key, 0)
|
||||||
return proxies[0]
|
return proxies[0]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,7 +76,9 @@ func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, provide
|
|||||||
providersMap[groupName] = pd
|
providersMap[groupName] = pd
|
||||||
} else {
|
} else {
|
||||||
if groupOption.URL == "" || groupOption.Interval == 0 {
|
if groupOption.URL == "" || groupOption.Interval == 0 {
|
||||||
return nil, errMissHealthCheck
|
//return nil, errMissHealthCheck
|
||||||
|
groupOption.URL = "http://www.gstatic.com/generate_204"
|
||||||
|
groupOption.Interval = 300
|
||||||
}
|
}
|
||||||
|
|
||||||
hc := provider.NewHealthCheck(ps, groupOption.URL, uint(groupOption.Interval), groupOption.Lazy)
|
hc := provider.NewHealthCheck(ps, groupOption.URL, uint(groupOption.Interval), groupOption.Lazy)
|
||||||
|
@ -51,3 +51,7 @@ func tcpKeepAlive(c net.Conn) {
|
|||||||
_ = tcp.SetKeepAlivePeriod(30 * time.Second)
|
_ = tcp.SetKeepAlivePeriod(30 * time.Second)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SelectAble interface {
|
||||||
|
Set(string) error
|
||||||
|
}
|
||||||
|
@ -26,6 +26,7 @@ type fetcher struct {
|
|||||||
done chan struct{}
|
done chan struct{}
|
||||||
hash [16]byte
|
hash [16]byte
|
||||||
parser parser
|
parser parser
|
||||||
|
interval time.Duration
|
||||||
onUpdate func(any)
|
onUpdate func(any)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,6 +57,12 @@ func (f *fetcher) Initial() (any, error) {
|
|||||||
modTime := stat.ModTime()
|
modTime := stat.ModTime()
|
||||||
f.updatedAt = &modTime
|
f.updatedAt = &modTime
|
||||||
isLocal = true
|
isLocal = true
|
||||||
|
if f.interval != 0 && modTime.Add(f.interval).Before(time.Now()) {
|
||||||
|
defer func() {
|
||||||
|
log.Infoln("[Provider] %s's proxies not updated for a long time, force refresh", f.Name())
|
||||||
|
go f.update()
|
||||||
|
}()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
buf, err = f.vehicle.Read()
|
buf, err = f.vehicle.Read()
|
||||||
}
|
}
|
||||||
@ -137,21 +144,11 @@ func (f *fetcher) pullLoop() {
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-f.ticker.C:
|
case <-f.ticker.C:
|
||||||
elm, same, err := f.Update()
|
same, err := f.update()
|
||||||
if err != nil {
|
if same || err != nil {
|
||||||
log.Warnln("[Provider] %s pull error: %s", f.Name(), err.Error())
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if same {
|
|
||||||
log.Debugln("[Provider] %s's proxies doesn't change", f.Name())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Infoln("[Provider] %s's proxies update", f.Name())
|
|
||||||
if f.onUpdate != nil {
|
|
||||||
f.onUpdate(elm)
|
|
||||||
}
|
|
||||||
case <-f.done:
|
case <-f.done:
|
||||||
f.ticker.Stop()
|
f.ticker.Stop()
|
||||||
return
|
return
|
||||||
@ -159,6 +156,26 @@ func (f *fetcher) pullLoop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *fetcher) update() (same bool, err error) {
|
||||||
|
elm, same, err := f.Update()
|
||||||
|
if err != nil {
|
||||||
|
log.Warnln("[Provider] %s pull error: %s", f.Name(), err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if same {
|
||||||
|
log.Debugln("[Provider] %s's proxies doesn't change", f.Name())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.onUpdate != nil {
|
||||||
|
f.onUpdate(elm)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infoln("[Provider] %s's proxies update", f.Name())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func safeWrite(path string, buf []byte) error {
|
func safeWrite(path string, buf []byte) error {
|
||||||
dir := filepath.Dir(path)
|
dir := filepath.Dir(path)
|
||||||
|
|
||||||
@ -184,5 +201,6 @@ func newFetcher(name string, interval time.Duration, vehicle types.Vehicle, pars
|
|||||||
parser: parser,
|
parser: parser,
|
||||||
done: make(chan struct{}, 1),
|
done: make(chan struct{}, 1),
|
||||||
onUpdate: onUpdate,
|
onUpdate: onUpdate,
|
||||||
|
interval: interval,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ func (pp *proxySetProvider) Initial() error {
|
|||||||
|
|
||||||
pp.onUpdate(elm)
|
pp.onUpdate(elm)
|
||||||
if pp.healthCheck.auto() {
|
if pp.healthCheck.auto() {
|
||||||
go pp.healthCheck.process()
|
defer func() { go pp.healthCheck.process() }()
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -2,16 +2,12 @@ package provider
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"github.com/Dreamacro/clash/listener/inner"
|
netHttp "github.com/Dreamacro/clash/component/http"
|
||||||
|
types "github.com/Dreamacro/clash/constant/provider"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
netHttp "github.com/Dreamacro/clash/common/net"
|
|
||||||
types "github.com/Dreamacro/clash/constant/provider"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type FileVehicle struct {
|
type FileVehicle struct {
|
||||||
@ -50,52 +46,16 @@ func (h *HTTPVehicle) Path() string {
|
|||||||
func (h *HTTPVehicle) Read() ([]byte, error) {
|
func (h *HTTPVehicle) Read() ([]byte, error) {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
resp, err := netHttp.HttpRequest(ctx, h.url, http.MethodGet, nil, nil)
|
||||||
uri, err := url.Parse(h.url)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodGet, uri.String(), nil)
|
|
||||||
req.Header.Set("User-Agent", netHttp.UA)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if user := uri.User; user != nil {
|
|
||||||
password, _ := user.Password()
|
|
||||||
req.SetBasicAuth(user.Username(), password)
|
|
||||||
}
|
|
||||||
|
|
||||||
req = req.WithContext(ctx)
|
|
||||||
|
|
||||||
transport := &http.Transport{
|
|
||||||
// from http.DefaultTransport
|
|
||||||
MaxIdleConns: 100,
|
|
||||||
IdleConnTimeout: 30 * time.Second,
|
|
||||||
TLSHandshakeTimeout: 10 * time.Second,
|
|
||||||
ExpectContinueTimeout: 1 * time.Second,
|
|
||||||
DialContext: func(ctx context.Context, network, address string) (net.Conn, error) {
|
|
||||||
conn := inner.HandleTcp(address, uri.Hostname())
|
|
||||||
return conn, nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
client := http.Client{Transport: transport}
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
buf, err := io.ReadAll(resp.Body)
|
buf, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return buf, nil
|
return buf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
package net
|
|
||||||
|
|
||||||
const (
|
|
||||||
UA = "Clash"
|
|
||||||
)
|
|
@ -15,6 +15,7 @@ var (
|
|||||||
dialMux sync.Mutex
|
dialMux sync.Mutex
|
||||||
actualSingleDialContext = singleDialContext
|
actualSingleDialContext = singleDialContext
|
||||||
actualDualStackDialContext = dualStackDialContext
|
actualDualStackDialContext = dualStackDialContext
|
||||||
|
tcpConcurrent = false
|
||||||
DisableIPv6 = false
|
DisableIPv6 = false
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -76,17 +77,22 @@ func ListenPacket(ctx context.Context, network, address string, options ...Optio
|
|||||||
|
|
||||||
func SetDial(concurrent bool) {
|
func SetDial(concurrent bool) {
|
||||||
dialMux.Lock()
|
dialMux.Lock()
|
||||||
|
tcpConcurrent = concurrent
|
||||||
if concurrent {
|
if concurrent {
|
||||||
actualSingleDialContext = concurrentSingleDialContext
|
actualSingleDialContext = concurrentSingleDialContext
|
||||||
actualDualStackDialContext = concurrentDualStackDialContext
|
actualDualStackDialContext = concurrentDualStackDialContext
|
||||||
} else {
|
} else {
|
||||||
actualSingleDialContext = singleDialContext
|
actualSingleDialContext = singleDialContext
|
||||||
actualDualStackDialContext = concurrentDualStackDialContext
|
actualDualStackDialContext = dualStackDialContext
|
||||||
}
|
}
|
||||||
|
|
||||||
dialMux.Unlock()
|
dialMux.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetDial() bool {
|
||||||
|
return tcpConcurrent
|
||||||
|
}
|
||||||
|
|
||||||
func dialContext(ctx context.Context, network string, destination netip.Addr, port string, opt *option) (net.Conn, error) {
|
func dialContext(ctx context.Context, network string, destination netip.Addr, port string, opt *option) (net.Conn, error) {
|
||||||
dialer := &net.Dialer{}
|
dialer := &net.Dialer{}
|
||||||
if opt.interfaceName != "" {
|
if opt.interfaceName != "" {
|
||||||
|
@ -30,7 +30,7 @@ func (l *loader) LoadGeoSiteWithAttr(file string, siteWithAttr string) ([]*route
|
|||||||
return nil, fmt.Errorf("empty listname in rule: %s", siteWithAttr)
|
return nil, fmt.Errorf("empty listname in rule: %s", siteWithAttr)
|
||||||
}
|
}
|
||||||
|
|
||||||
domains, err := l.LoadSite(file, list)
|
domains, err := l.LoadSiteByPath(file, list)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -59,7 +59,7 @@ func (l *loader) LoadGeoSiteWithAttr(file string, siteWithAttr string) ([]*route
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *loader) LoadGeoIP(country string) ([]*router.CIDR, error) {
|
func (l *loader) LoadGeoIP(country string) ([]*router.CIDR, error) {
|
||||||
return l.LoadIP(C.GeoipName, country)
|
return l.LoadIPByPath(C.GeoipName, country)
|
||||||
}
|
}
|
||||||
|
|
||||||
var loaders map[string]func() LoaderImplementation
|
var loaders map[string]func() LoaderImplementation
|
||||||
|
@ -5,8 +5,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type LoaderImplementation interface {
|
type LoaderImplementation interface {
|
||||||
LoadSite(filename, list string) ([]*router.Domain, error)
|
LoadSiteByPath(filename, list string) ([]*router.Domain, error)
|
||||||
LoadIP(filename, country string) ([]*router.CIDR, error)
|
LoadSiteByBytes(geositeBytes []byte, list string) ([]*router.Domain, error)
|
||||||
|
LoadIPByPath(filename, country string) ([]*router.CIDR, error)
|
||||||
|
LoadIPByBytes(geoipBytes []byte, country string) ([]*router.CIDR, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Loader interface {
|
type Loader interface {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package memconservative
|
package memconservative
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
@ -13,7 +14,7 @@ type memConservativeLoader struct {
|
|||||||
geositecache GeoSiteCache
|
geositecache GeoSiteCache
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *memConservativeLoader) LoadIP(filename, country string) ([]*router.CIDR, error) {
|
func (m *memConservativeLoader) LoadIPByPath(filename, country string) ([]*router.CIDR, error) {
|
||||||
defer runtime.GC()
|
defer runtime.GC()
|
||||||
geoip, err := m.geoipcache.Unmarshal(filename, country)
|
geoip, err := m.geoipcache.Unmarshal(filename, country)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -22,7 +23,11 @@ func (m *memConservativeLoader) LoadIP(filename, country string) ([]*router.CIDR
|
|||||||
return geoip.Cidr, nil
|
return geoip.Cidr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *memConservativeLoader) LoadSite(filename, list string) ([]*router.Domain, error) {
|
func (m *memConservativeLoader) LoadIPByBytes(geoipBytes []byte, country string) ([]*router.CIDR, error) {
|
||||||
|
return nil, errors.New("memConservative do not support LoadIPByBytes")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memConservativeLoader) LoadSiteByPath(filename, list string) ([]*router.Domain, error) {
|
||||||
defer runtime.GC()
|
defer runtime.GC()
|
||||||
geosite, err := m.geositecache.Unmarshal(filename, list)
|
geosite, err := m.geositecache.Unmarshal(filename, list)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -31,6 +36,10 @@ func (m *memConservativeLoader) LoadSite(filename, list string) ([]*router.Domai
|
|||||||
return geosite.Domain, nil
|
return geosite.Domain, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *memConservativeLoader) LoadSiteByBytes(geositeBytes []byte, list string) ([]*router.Domain, error) {
|
||||||
|
return nil, errors.New("memConservative do not support LoadSiteByBytes")
|
||||||
|
}
|
||||||
|
|
||||||
func newMemConservativeLoader() geodata.LoaderImplementation {
|
func newMemConservativeLoader() geodata.LoaderImplementation {
|
||||||
return &memConservativeLoader{make(map[string]*router.GeoIP), make(map[string]*router.GeoSite)}
|
return &memConservativeLoader{make(map[string]*router.GeoIP), make(map[string]*router.GeoSite)}
|
||||||
}
|
}
|
||||||
|
@ -29,11 +29,7 @@ func ReadAsset(file string) ([]byte, error) {
|
|||||||
return ReadFile(C.Path.GetAssetLocation(file))
|
return ReadFile(C.Path.GetAssetLocation(file))
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadIP(filename, country string) ([]*router.CIDR, error) {
|
func loadIP(geoipBytes []byte, country string) ([]*router.CIDR, error) {
|
||||||
geoipBytes, err := ReadAsset(filename)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to open file: %s, base error: %s", filename, err.Error())
|
|
||||||
}
|
|
||||||
var geoipList router.GeoIPList
|
var geoipList router.GeoIPList
|
||||||
if err := proto.Unmarshal(geoipBytes, &geoipList); err != nil {
|
if err := proto.Unmarshal(geoipBytes, &geoipList); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -45,14 +41,10 @@ func loadIP(filename, country string) ([]*router.CIDR, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("country not found in %s%s%s", filename, ": ", country)
|
return nil, fmt.Errorf("country %s not found", country)
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadSite(filename, list string) ([]*router.Domain, error) {
|
func loadSite(geositeBytes []byte, list string) ([]*router.Domain, error) {
|
||||||
geositeBytes, err := ReadAsset(filename)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to open file: %s, base error: %s", filename, err.Error())
|
|
||||||
}
|
|
||||||
var geositeList router.GeoSiteList
|
var geositeList router.GeoSiteList
|
||||||
if err := proto.Unmarshal(geositeBytes, &geositeList); err != nil {
|
if err := proto.Unmarshal(geositeBytes, &geositeList); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -64,17 +56,33 @@ func loadSite(filename, list string) ([]*router.Domain, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("list not found in %s%s%s", filename, ": ", list)
|
return nil, fmt.Errorf("list %s not found", list)
|
||||||
}
|
}
|
||||||
|
|
||||||
type standardLoader struct{}
|
type standardLoader struct{}
|
||||||
|
|
||||||
func (d standardLoader) LoadSite(filename, list string) ([]*router.Domain, error) {
|
func (d standardLoader) LoadSiteByPath(filename, list string) ([]*router.Domain, error) {
|
||||||
return loadSite(filename, list)
|
geositeBytes, err := ReadAsset(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to open file: %s, base error: %s", filename, err.Error())
|
||||||
|
}
|
||||||
|
return loadSite(geositeBytes, list)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d standardLoader) LoadIP(filename, country string) ([]*router.CIDR, error) {
|
func (d standardLoader) LoadSiteByBytes(geositeBytes []byte, list string) ([]*router.Domain, error) {
|
||||||
return loadIP(filename, country)
|
return loadSite(geositeBytes, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d standardLoader) LoadIPByPath(filename, country string) ([]*router.CIDR, error) {
|
||||||
|
geoipBytes, err := ReadAsset(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to open file: %s, base error: %s", filename, err.Error())
|
||||||
|
}
|
||||||
|
return loadIP(geoipBytes, country)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d standardLoader) LoadIPByBytes(geoipBytes []byte, country string) ([]*router.CIDR, error) {
|
||||||
|
return loadIP(geoipBytes, country)
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -21,16 +21,16 @@ func SetLoader(newLoader string) {
|
|||||||
geoLoaderName = newLoader
|
geoLoaderName = newLoader
|
||||||
}
|
}
|
||||||
|
|
||||||
func Verify(name string) bool {
|
func Verify(name string) error {
|
||||||
switch name {
|
switch name {
|
||||||
case C.GeositeName:
|
case C.GeositeName:
|
||||||
_, _, err := LoadGeoSiteMatcher("CN")
|
_, _, err := LoadGeoSiteMatcher("CN")
|
||||||
return err == nil
|
return err
|
||||||
case C.GeoipName:
|
case C.GeoipName:
|
||||||
_, _, err := LoadGeoIPMatcher("CN")
|
_, _, err := LoadGeoIPMatcher("CN")
|
||||||
return err == nil
|
return err
|
||||||
default:
|
default:
|
||||||
return false
|
return fmt.Errorf("not support name")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
64
component/http/http.go
Normal file
64
component/http/http.go
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/Dreamacro/clash/listener/inner"
|
||||||
|
"github.com/Dreamacro/clash/log"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
URL "net/url"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
UA = "Clash"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HttpRequest(ctx context.Context, url, method string, header map[string][]string, body io.Reader) (*http.Response, error) {
|
||||||
|
method = strings.ToUpper(method)
|
||||||
|
urlRes, err := URL.Parse(url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest(method, urlRes.String(), body)
|
||||||
|
for k, v := range header {
|
||||||
|
for _, v := range v {
|
||||||
|
req.Header.Add(k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := header["User-Agent"]; !ok {
|
||||||
|
req.Header.Set("User-Agent", UA)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if user := urlRes.User; user != nil {
|
||||||
|
password, _ := user.Password()
|
||||||
|
req.SetBasicAuth(user.Username(), password)
|
||||||
|
}
|
||||||
|
|
||||||
|
req = req.WithContext(ctx)
|
||||||
|
|
||||||
|
transport := &http.Transport{
|
||||||
|
// from http.DefaultTransport
|
||||||
|
MaxIdleConns: 100,
|
||||||
|
IdleConnTimeout: 30 * time.Second,
|
||||||
|
TLSHandshakeTimeout: 10 * time.Second,
|
||||||
|
ExpectContinueTimeout: 1 * time.Second,
|
||||||
|
DialContext: func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||||
|
log.Infoln(urlRes.String())
|
||||||
|
conn := inner.HandleTcp(address, urlRes.Hostname())
|
||||||
|
return conn, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
client := http.Client{Transport: transport}
|
||||||
|
return client.Do(req)
|
||||||
|
|
||||||
|
}
|
69
component/js/function.go
Normal file
69
component/js/function.go
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
//go:build !no_script
|
||||||
|
|
||||||
|
package js
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Dreamacro/clash/component/resolver"
|
||||||
|
C "github.com/Dreamacro/clash/constant"
|
||||||
|
"github.com/Dreamacro/clash/log"
|
||||||
|
"github.com/dop251/goja"
|
||||||
|
"github.com/dop251/goja_nodejs/require"
|
||||||
|
"net/netip"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Context struct {
|
||||||
|
runtime *goja.Runtime
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) Resolve(host string, dnsType C.DnsType) []string {
|
||||||
|
var ips []string
|
||||||
|
var ipAddrs []netip.Addr
|
||||||
|
var err error
|
||||||
|
switch dnsType {
|
||||||
|
case C.IPv4:
|
||||||
|
ipAddrs, err = resolver.ResolveAllIPv4(host)
|
||||||
|
case C.IPv6:
|
||||||
|
ipAddrs, err = resolver.ResolveAllIPv6(host)
|
||||||
|
case C.All:
|
||||||
|
ipAddrs, err = resolver.ResolveAllIP(host)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorln("Script resolve %s failed, error: %v", host, err)
|
||||||
|
return ips
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, addr := range ipAddrs {
|
||||||
|
ips = append(ips, addr.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return ips
|
||||||
|
}
|
||||||
|
|
||||||
|
func newContext() require.ModuleLoader {
|
||||||
|
return func(runtime *goja.Runtime, object *goja.Object) {
|
||||||
|
ctx := Context{
|
||||||
|
runtime: runtime,
|
||||||
|
}
|
||||||
|
|
||||||
|
o := object.Get("exports").(*goja.Object)
|
||||||
|
o.Set("resolve", func(call goja.FunctionCall) goja.Value {
|
||||||
|
if len(call.Arguments) < 1 {
|
||||||
|
return runtime.ToValue([]string{})
|
||||||
|
}
|
||||||
|
|
||||||
|
host := call.Argument(0).String()
|
||||||
|
dnsType := C.IPv4
|
||||||
|
if len(call.Arguments) == 2 {
|
||||||
|
dnsType = int(call.Argument(1).ToInteger())
|
||||||
|
}
|
||||||
|
|
||||||
|
ips := ctx.Resolve(host, C.DnsType(dnsType))
|
||||||
|
return runtime.ToValue(ips)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func enable(rt *goja.Runtime) {
|
||||||
|
rt.Set("context", require.Require(rt, "context"))
|
||||||
|
}
|
60
component/js/js.go
Normal file
60
component/js/js.go
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
//go:build !no_script
|
||||||
|
|
||||||
|
package js
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Dreamacro/clash/log"
|
||||||
|
"github.com/dop251/goja"
|
||||||
|
"github.com/dop251/goja_nodejs/console"
|
||||||
|
"github.com/dop251/goja_nodejs/eventloop"
|
||||||
|
"github.com/dop251/goja_nodejs/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
logPrinter := console.RequireWithPrinter(&JsLog{})
|
||||||
|
require.RegisterNativeModule("console", logPrinter)
|
||||||
|
contextFuncLoader := newContext()
|
||||||
|
require.RegisterNativeModule("context", contextFuncLoader)
|
||||||
|
}
|
||||||
|
|
||||||
|
func preSetting(rt *goja.Runtime) {
|
||||||
|
registry := new(require.Registry)
|
||||||
|
registry.Enable(rt)
|
||||||
|
|
||||||
|
console.Enable(rt)
|
||||||
|
enable(rt)
|
||||||
|
eventloop.EnableConsole(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLoop() *eventloop.EventLoop {
|
||||||
|
loop := eventloop.NewEventLoop(func(loop *eventloop.EventLoop) {
|
||||||
|
loop.Run(func(runtime *goja.Runtime) {
|
||||||
|
preSetting(runtime)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
return loop
|
||||||
|
}
|
||||||
|
|
||||||
|
func compiler(name, code string) (*goja.Program, error) {
|
||||||
|
return goja.Compile(name, code, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func run(loop *eventloop.EventLoop, program *goja.Program, args map[string]any, callback func(any, error)) {
|
||||||
|
loop.Run(func(runtime *goja.Runtime) {
|
||||||
|
for k, v := range args {
|
||||||
|
runtime.SetFieldNameMapper(goja.TagFieldNameMapper("json", true))
|
||||||
|
err := runtime.Set(k, v)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorln("Args to script failed, %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
v, err := runtime.RunProgram(program)
|
||||||
|
if v == nil {
|
||||||
|
callback(nil, err)
|
||||||
|
} else {
|
||||||
|
callback(v.Export(), err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
20
component/js/log.go
Normal file
20
component/js/log.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
//go:build !no_script
|
||||||
|
|
||||||
|
package js
|
||||||
|
|
||||||
|
import "github.com/Dreamacro/clash/log"
|
||||||
|
|
||||||
|
type JsLog struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j JsLog) Log(s string) {
|
||||||
|
log.Infoln("[JS] %s", s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j JsLog) Warn(s string) {
|
||||||
|
log.Warnln("[JS] %s", s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j JsLog) Error(s string) {
|
||||||
|
log.Errorln("[JS] %s", s)
|
||||||
|
}
|
34
component/js/script.go
Normal file
34
component/js/script.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
//go:build !no_script
|
||||||
|
|
||||||
|
package js
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/dop251/goja"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var JS sync.Map
|
||||||
|
var mux sync.Mutex
|
||||||
|
|
||||||
|
func NewJS(name, code string) error {
|
||||||
|
program, err := compiler(name, code)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := JS.Load(name); !ok {
|
||||||
|
mux.Lock()
|
||||||
|
defer mux.Unlock()
|
||||||
|
if _, ok := JS.Load(name); !ok {
|
||||||
|
JS.Store(name, program)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Run(name string, args map[string]any, callback func(any, error)) {
|
||||||
|
if value, ok := JS.Load(name); ok {
|
||||||
|
run(getLoop(), value.(*goja.Program), args, callback)
|
||||||
|
}
|
||||||
|
}
|
12
component/js/script_no_script.go
Normal file
12
component/js/script_no_script.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
//go:build no_script
|
||||||
|
|
||||||
|
package js
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func NewJS(name, code string) error {
|
||||||
|
fmt.Errorf("unsupported script on the build")
|
||||||
|
}
|
||||||
|
|
||||||
|
func Run(name string, args map[string]any, callback func(any, error)) {
|
||||||
|
}
|
@ -6,13 +6,14 @@ import (
|
|||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"runtime"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrInvalidNetwork = errors.New("invalid network")
|
ErrInvalidNetwork = errors.New("invalid network")
|
||||||
ErrPlatformNotSupport = errors.New("not support on this platform")
|
ErrPlatformNotSupport = errors.New("not support on this platform")
|
||||||
ErrNotFound = errors.New("process not found")
|
ErrNotFound = errors.New("process not found")
|
||||||
|
|
||||||
|
enableFindProcess = true
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -20,7 +21,11 @@ const (
|
|||||||
UDP = "udp"
|
UDP = "udp"
|
||||||
)
|
)
|
||||||
|
|
||||||
func FindProcessName(network string, srcIP netip.Addr, srcPort int) (string, error) {
|
func EnableFindProcess(e bool) {
|
||||||
|
enableFindProcess = e
|
||||||
|
}
|
||||||
|
|
||||||
|
func FindProcessName(network string, srcIP netip.Addr, srcPort int) (int32, string, error) {
|
||||||
return findProcessName(network, srcIP, srcPort)
|
return findProcessName(network, srcIP, srcPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,10 +38,9 @@ func FindUid(network string, srcIP netip.Addr, srcPort int) (int32, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ShouldFindProcess(metadata *C.Metadata) bool {
|
func ShouldFindProcess(metadata *C.Metadata) bool {
|
||||||
if runtime.GOOS == "android" {
|
if !enableFindProcess ||
|
||||||
return false
|
metadata.Process != "" ||
|
||||||
}
|
metadata.ProcessPath != "" {
|
||||||
if metadata.Process != "" || metadata.ProcessPath != "" {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for _, ip := range localIPs {
|
for _, ip := range localIPs {
|
||||||
|
@ -21,7 +21,7 @@ func resolveSocketByNetlink(network string, ip netip.Addr, srcPort int) (int32,
|
|||||||
return 0, 0, ErrPlatformNotSupport
|
return 0, 0, ErrPlatformNotSupport
|
||||||
}
|
}
|
||||||
|
|
||||||
func findProcessName(network string, ip netip.Addr, port int) (string, error) {
|
func findProcessName(network string, ip netip.Addr, port int) (int32, string, error) {
|
||||||
var spath string
|
var spath string
|
||||||
switch network {
|
switch network {
|
||||||
case TCP:
|
case TCP:
|
||||||
@ -29,14 +29,14 @@ func findProcessName(network string, ip netip.Addr, port int) (string, error) {
|
|||||||
case UDP:
|
case UDP:
|
||||||
spath = "net.inet.udp.pcblist_n"
|
spath = "net.inet.udp.pcblist_n"
|
||||||
default:
|
default:
|
||||||
return "", ErrInvalidNetwork
|
return -1, "", ErrInvalidNetwork
|
||||||
}
|
}
|
||||||
|
|
||||||
isIPv4 := ip.Is4()
|
isIPv4 := ip.Is4()
|
||||||
|
|
||||||
value, err := syscall.Sysctl(spath)
|
value, err := syscall.Sysctl(spath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return -1, "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := []byte(value)
|
buf := []byte(value)
|
||||||
@ -81,10 +81,11 @@ func findProcessName(network string, ip netip.Addr, port int) (string, error) {
|
|||||||
|
|
||||||
// xsocket_n.so_last_pid
|
// xsocket_n.so_last_pid
|
||||||
pid := readNativeUint32(buf[so+68 : so+72])
|
pid := readNativeUint32(buf[so+68 : so+72])
|
||||||
return getExecPathFromPID(pid)
|
pp, err := getExecPathFromPID(pid)
|
||||||
|
return -1, pp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return "", ErrNotFound
|
return -1, "", ErrNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
func getExecPathFromPID(pid uint32) (string, error) {
|
func getExecPathFromPID(pid uint32) (string, error) {
|
||||||
|
@ -25,7 +25,7 @@ func resolveSocketByNetlink(network string, ip netip.Addr, srcPort int) (int32,
|
|||||||
return 0, 0, ErrPlatformNotSupport
|
return 0, 0, ErrPlatformNotSupport
|
||||||
}
|
}
|
||||||
|
|
||||||
func findProcessName(network string, ip netip.Addr, srcPort int) (string, error) {
|
func findProcessName(network string, ip netip.Addr, srcPort int) (int32, string, error) {
|
||||||
once.Do(func() {
|
once.Do(func() {
|
||||||
if err := initSearcher(); err != nil {
|
if err := initSearcher(); err != nil {
|
||||||
log.Errorln("Initialize PROCESS-NAME failed: %s", err.Error())
|
log.Errorln("Initialize PROCESS-NAME failed: %s", err.Error())
|
||||||
@ -35,7 +35,7 @@ func findProcessName(network string, ip netip.Addr, srcPort int) (string, error)
|
|||||||
})
|
})
|
||||||
|
|
||||||
if defaultSearcher == nil {
|
if defaultSearcher == nil {
|
||||||
return "", ErrPlatformNotSupport
|
return -1, "", ErrPlatformNotSupport
|
||||||
}
|
}
|
||||||
|
|
||||||
var spath string
|
var spath string
|
||||||
@ -46,21 +46,22 @@ func findProcessName(network string, ip netip.Addr, srcPort int) (string, error)
|
|||||||
case UDP:
|
case UDP:
|
||||||
spath = "net.inet.udp.pcblist"
|
spath = "net.inet.udp.pcblist"
|
||||||
default:
|
default:
|
||||||
return "", ErrInvalidNetwork
|
return -1, "", ErrInvalidNetwork
|
||||||
}
|
}
|
||||||
|
|
||||||
value, err := syscall.Sysctl(spath)
|
value, err := syscall.Sysctl(spath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return -1, "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := []byte(value)
|
buf := []byte(value)
|
||||||
pid, err := defaultSearcher.Search(buf, ip, uint16(srcPort), isTCP)
|
pid, err := defaultSearcher.Search(buf, ip, uint16(srcPort), isTCP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return -1, "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
return getExecPathFromPID(pid)
|
pp, err := getExecPathFromPID(pid)
|
||||||
|
return -1, pp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func getExecPathFromPID(pid uint32) (string, error) {
|
func getExecPathFromPID(pid uint32) (string, error) {
|
||||||
|
@ -8,6 +8,8 @@ import (
|
|||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"unicode"
|
"unicode"
|
||||||
@ -32,12 +34,13 @@ const (
|
|||||||
pathProc = "/proc"
|
pathProc = "/proc"
|
||||||
)
|
)
|
||||||
|
|
||||||
func findProcessName(network string, ip netip.Addr, srcPort int) (string, error) {
|
func findProcessName(network string, ip netip.Addr, srcPort int) (int32, string, error) {
|
||||||
inode, uid, err := resolveSocketByNetlink(network, ip, srcPort)
|
inode, uid, err := resolveSocketByNetlink(network, ip, srcPort)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return -1, "", err
|
||||||
}
|
}
|
||||||
return resolveProcessNameByProcSearch(inode, uid)
|
pp, err := resolveProcessNameByProcSearch(inode, uid)
|
||||||
|
return uid, pp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolveSocketByNetlink(network string, ip netip.Addr, srcPort int) (int32, int32, error) {
|
func resolveSocketByNetlink(network string, ip netip.Addr, srcPort int) (int32, int32, error) {
|
||||||
@ -195,8 +198,19 @@ func resolveProcessNameByProcSearch(inode, uid int32) (string, error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if bytes.Equal(buffer[:n], socket) {
|
if runtime.GOOS == "android" {
|
||||||
return os.Readlink(path.Join(processPath, "exe"))
|
if bytes.Equal(buffer[:n], socket) {
|
||||||
|
cmdline, err := os.ReadFile(path.Join(processPath, "cmdline"))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return splitCmdline(cmdline), nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if bytes.Equal(buffer[:n], socket) {
|
||||||
|
return os.Readlink(path.Join(processPath, "exe"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -204,6 +218,19 @@ func resolveProcessNameByProcSearch(inode, uid int32) (string, error) {
|
|||||||
return "", fmt.Errorf("process of uid(%d),inode(%d) not found", uid, inode)
|
return "", fmt.Errorf("process of uid(%d),inode(%d) not found", uid, inode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func splitCmdline(cmdline []byte) string {
|
||||||
|
cmdline = bytes.Trim(cmdline, " ")
|
||||||
|
|
||||||
|
idx := bytes.IndexFunc(cmdline, func(r rune) bool {
|
||||||
|
return unicode.IsControl(r) || unicode.IsSpace(r)
|
||||||
|
})
|
||||||
|
|
||||||
|
if idx == -1 {
|
||||||
|
return filepath.Base(string(cmdline))
|
||||||
|
}
|
||||||
|
return filepath.Base(string(cmdline[:idx]))
|
||||||
|
}
|
||||||
|
|
||||||
func isPid(s string) bool {
|
func isPid(s string) bool {
|
||||||
return strings.IndexFunc(s, func(r rune) bool {
|
return strings.IndexFunc(s, func(r rune) bool {
|
||||||
return !unicode.IsDigit(r)
|
return !unicode.IsDigit(r)
|
||||||
|
@ -4,8 +4,8 @@ package process
|
|||||||
|
|
||||||
import "net/netip"
|
import "net/netip"
|
||||||
|
|
||||||
func findProcessName(network string, ip netip.Addr, srcPort int) (string, error) {
|
func findProcessName(network string, ip netip.Addr, srcPort int) (int32, string, error) {
|
||||||
return "", ErrPlatformNotSupport
|
return -1, "", ErrPlatformNotSupport
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolveSocketByNetlink(network string, ip netip.Addr, srcPort int) (int32, int32, error) {
|
func resolveSocketByNetlink(network string, ip netip.Addr, srcPort int) (int32, int32, error) {
|
||||||
|
@ -62,7 +62,7 @@ func initWin32API() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func findProcessName(network string, ip netip.Addr, srcPort int) (string, error) {
|
func findProcessName(network string, ip netip.Addr, srcPort int) (int32, string, error) {
|
||||||
once.Do(func() {
|
once.Do(func() {
|
||||||
err := initWin32API()
|
err := initWin32API()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -86,21 +86,22 @@ func findProcessName(network string, ip netip.Addr, srcPort int) (string, error)
|
|||||||
fn = getExUDPTable
|
fn = getExUDPTable
|
||||||
class = udpTablePid
|
class = udpTablePid
|
||||||
default:
|
default:
|
||||||
return "", ErrInvalidNetwork
|
return -1, "", ErrInvalidNetwork
|
||||||
}
|
}
|
||||||
|
|
||||||
buf, err := getTransportTable(fn, family, class)
|
buf, err := getTransportTable(fn, family, class)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return -1, "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
s := newSearcher(family == windows.AF_INET, network == TCP)
|
s := newSearcher(family == windows.AF_INET, network == TCP)
|
||||||
|
|
||||||
pid, err := s.Search(buf, ip, uint16(srcPort))
|
pid, err := s.Search(buf, ip, uint16(srcPort))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return -1, "", err
|
||||||
}
|
}
|
||||||
return getExecPathFromPID(pid)
|
pp, err := getExecPathFromPID(pid)
|
||||||
|
return -1, pp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
type searcher struct {
|
type searcher struct {
|
||||||
|
@ -89,24 +89,39 @@ func ResolveIP(host string) (netip.Addr, error) {
|
|||||||
// ResolveIPv4ProxyServerHost proxies server host only
|
// ResolveIPv4ProxyServerHost proxies server host only
|
||||||
func ResolveIPv4ProxyServerHost(host string) (netip.Addr, error) {
|
func ResolveIPv4ProxyServerHost(host string) (netip.Addr, error) {
|
||||||
if ProxyServerHostResolver != nil {
|
if ProxyServerHostResolver != nil {
|
||||||
return ResolveIPv4WithResolver(host, ProxyServerHostResolver)
|
if ip, err := ResolveIPv4WithResolver(host, ProxyServerHostResolver); err != nil {
|
||||||
|
return ResolveIPv4(host)
|
||||||
|
} else {
|
||||||
|
return ip, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResolveIPv4(host)
|
return ResolveIPv4(host)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolveIPv6ProxyServerHost proxies server host only
|
// ResolveIPv6ProxyServerHost proxies server host only
|
||||||
func ResolveIPv6ProxyServerHost(host string) (netip.Addr, error) {
|
func ResolveIPv6ProxyServerHost(host string) (netip.Addr, error) {
|
||||||
if ProxyServerHostResolver != nil {
|
if ProxyServerHostResolver != nil {
|
||||||
return ResolveIPv6WithResolver(host, ProxyServerHostResolver)
|
if ip, err := ResolveIPv6WithResolver(host, ProxyServerHostResolver); err != nil {
|
||||||
|
return ResolveIPv6(host)
|
||||||
|
} else {
|
||||||
|
return ip, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResolveIPv6(host)
|
return ResolveIPv6(host)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolveProxyServerHost proxies server host only
|
// ResolveProxyServerHost proxies server host only
|
||||||
func ResolveProxyServerHost(host string) (netip.Addr, error) {
|
func ResolveProxyServerHost(host string) (netip.Addr, error) {
|
||||||
if ProxyServerHostResolver != nil {
|
if ProxyServerHostResolver != nil {
|
||||||
return ResolveIPWithResolver(host, ProxyServerHostResolver)
|
if ip, err := ResolveIPWithResolver(host, ProxyServerHostResolver); err != nil {
|
||||||
|
return ResolveIP(host)
|
||||||
|
} else {
|
||||||
|
return ip, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResolveIP(host)
|
return ResolveIP(host)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,12 +47,14 @@ type General struct {
|
|||||||
UnifiedDelay bool
|
UnifiedDelay bool
|
||||||
LogLevel log.LogLevel `json:"log-level"`
|
LogLevel log.LogLevel `json:"log-level"`
|
||||||
IPv6 bool `json:"ipv6"`
|
IPv6 bool `json:"ipv6"`
|
||||||
Interface string `json:"-"`
|
Interface string `json:"interface-name"`
|
||||||
RoutingMark int `json:"-"`
|
RoutingMark int `json:"-"`
|
||||||
GeodataMode bool `json:"geodata-mode"`
|
GeodataMode bool `json:"geodata-mode"`
|
||||||
GeodataLoader string `json:"geodata-loader"`
|
GeodataLoader string `json:"geodata-loader"`
|
||||||
TCPConcurrent bool `json:"tcp-concurrent"`
|
TCPConcurrent bool `json:"tcp-concurrent"`
|
||||||
|
EnableProcess bool `json:"enable-process"`
|
||||||
Tun Tun `json:"tun"`
|
Tun Tun `json:"tun"`
|
||||||
|
Sniffing bool `json:"sniffing"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inbound config
|
// Inbound config
|
||||||
@ -113,6 +115,7 @@ type Tun struct {
|
|||||||
DNSHijack []netip.AddrPort `yaml:"dns-hijack" json:"dns-hijack"`
|
DNSHijack []netip.AddrPort `yaml:"dns-hijack" json:"dns-hijack"`
|
||||||
AutoRoute bool `yaml:"auto-route" json:"auto-route"`
|
AutoRoute bool `yaml:"auto-route" json:"auto-route"`
|
||||||
AutoDetectInterface bool `yaml:"auto-detect-interface" json:"auto-detect-interface"`
|
AutoDetectInterface bool `yaml:"auto-detect-interface" json:"auto-detect-interface"`
|
||||||
|
TunAddressPrefix netip.Prefix `yaml:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// IPTables config
|
// IPTables config
|
||||||
@ -205,6 +208,7 @@ type RawConfig struct {
|
|||||||
GeodataMode bool `yaml:"geodata-mode"`
|
GeodataMode bool `yaml:"geodata-mode"`
|
||||||
GeodataLoader string `yaml:"geodata-loader"`
|
GeodataLoader string `yaml:"geodata-loader"`
|
||||||
TCPConcurrent bool `yaml:"tcp-concurrent" json:"tcp-concurrent"`
|
TCPConcurrent bool `yaml:"tcp-concurrent" json:"tcp-concurrent"`
|
||||||
|
EnableProcess bool `yaml:"enable-process" json:"enable-process"`
|
||||||
|
|
||||||
Sniffer RawSniffer `yaml:"sniffer"`
|
Sniffer RawSniffer `yaml:"sniffer"`
|
||||||
ProxyProvider map[string]map[string]any `yaml:"proxy-providers"`
|
ProxyProvider map[string]map[string]any `yaml:"proxy-providers"`
|
||||||
@ -215,11 +219,18 @@ type RawConfig struct {
|
|||||||
IPTables IPTables `yaml:"iptables"`
|
IPTables IPTables `yaml:"iptables"`
|
||||||
Experimental Experimental `yaml:"experimental"`
|
Experimental Experimental `yaml:"experimental"`
|
||||||
Profile Profile `yaml:"profile"`
|
Profile Profile `yaml:"profile"`
|
||||||
|
GeoXUrl RawGeoXUrl `yaml:"geox-url"`
|
||||||
Proxy []map[string]any `yaml:"proxies"`
|
Proxy []map[string]any `yaml:"proxies"`
|
||||||
ProxyGroup []map[string]any `yaml:"proxy-groups"`
|
ProxyGroup []map[string]any `yaml:"proxy-groups"`
|
||||||
Rule []string `yaml:"rules"`
|
Rule []string `yaml:"rules"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RawGeoXUrl struct {
|
||||||
|
GeoIp string `yaml:"geoip" json:"geoip"`
|
||||||
|
Mmdb string `yaml:"mmdb" json:"mmdb"`
|
||||||
|
GeoSite string `yaml:"geosite" json:"geosite"`
|
||||||
|
}
|
||||||
|
|
||||||
type RawSniffer struct {
|
type RawSniffer struct {
|
||||||
Enable bool `yaml:"enable" json:"enable"`
|
Enable bool `yaml:"enable" json:"enable"`
|
||||||
Sniffing []string `yaml:"sniffing" json:"sniffing"`
|
Sniffing []string `yaml:"sniffing" json:"sniffing"`
|
||||||
@ -254,6 +265,7 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) {
|
|||||||
Proxy: []map[string]any{},
|
Proxy: []map[string]any{},
|
||||||
ProxyGroup: []map[string]any{},
|
ProxyGroup: []map[string]any{},
|
||||||
TCPConcurrent: false,
|
TCPConcurrent: false,
|
||||||
|
EnableProcess: true,
|
||||||
Tun: RawTun{
|
Tun: RawTun{
|
||||||
Enable: false,
|
Enable: false,
|
||||||
Device: "",
|
Device: "",
|
||||||
@ -304,6 +316,11 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) {
|
|||||||
Profile: Profile{
|
Profile: Profile{
|
||||||
StoreSelected: true,
|
StoreSelected: true,
|
||||||
},
|
},
|
||||||
|
GeoXUrl: RawGeoXUrl{
|
||||||
|
GeoIp: "https://ghproxy.com/https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/geoip.dat",
|
||||||
|
Mmdb: "https://ghproxy.com/https://raw.githubusercontent.com/Loyalsoldier/geoip/release/Country.mmdb",
|
||||||
|
GeoSite: "https://ghproxy.com/https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/geosite.dat",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := yaml.Unmarshal(buf, rawCfg); err != nil {
|
if err := yaml.Unmarshal(buf, rawCfg); err != nil {
|
||||||
@ -327,12 +344,6 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) {
|
|||||||
}
|
}
|
||||||
config.General = general
|
config.General = general
|
||||||
|
|
||||||
tunCfg, err := parseTun(rawCfg.Tun, config.General)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
config.Tun = tunCfg
|
|
||||||
|
|
||||||
dialer.DefaultInterface.Store(config.General.Interface)
|
dialer.DefaultInterface.Store(config.General.Interface)
|
||||||
|
|
||||||
proxies, providers, err := parseProxies(rawCfg)
|
proxies, providers, err := parseProxies(rawCfg)
|
||||||
@ -361,6 +372,12 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) {
|
|||||||
}
|
}
|
||||||
config.DNS = dnsCfg
|
config.DNS = dnsCfg
|
||||||
|
|
||||||
|
tunCfg, err := parseTun(rawCfg.Tun, config.General, dnsCfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
config.Tun = tunCfg
|
||||||
|
|
||||||
config.Users = parseAuthentication(rawCfg.Authentication)
|
config.Users = parseAuthentication(rawCfg.Authentication)
|
||||||
|
|
||||||
config.Sniffer, err = parseSniffer(rawCfg.Sniffer)
|
config.Sniffer, err = parseSniffer(rawCfg.Sniffer)
|
||||||
@ -409,6 +426,7 @@ func parseGeneral(cfg *RawConfig) (*General, error) {
|
|||||||
GeodataMode: cfg.GeodataMode,
|
GeodataMode: cfg.GeodataMode,
|
||||||
GeodataLoader: cfg.GeodataLoader,
|
GeodataLoader: cfg.GeodataLoader,
|
||||||
TCPConcurrent: cfg.TCPConcurrent,
|
TCPConcurrent: cfg.TCPConcurrent,
|
||||||
|
EnableProcess: cfg.EnableProcess,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -865,7 +883,7 @@ func parseAuthentication(rawRecords []string) []auth.AuthUser {
|
|||||||
return users
|
return users
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseTun(rawTun RawTun, general *General) (*Tun, error) {
|
func parseTun(rawTun RawTun, general *General, dnsCfg *DNS) (*Tun, error) {
|
||||||
if rawTun.Enable && rawTun.AutoDetectInterface {
|
if rawTun.Enable && rawTun.AutoDetectInterface {
|
||||||
autoDetectInterfaceName, err := commons.GetAutoDetectInterface()
|
autoDetectInterfaceName, err := commons.GetAutoDetectInterface()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -892,6 +910,13 @@ func parseTun(rawTun RawTun, general *General) (*Tun, error) {
|
|||||||
dnsHijack = append(dnsHijack, addrPort)
|
dnsHijack = append(dnsHijack, addrPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var tunAddressPrefix netip.Prefix
|
||||||
|
if dnsCfg.FakeIPRange != nil {
|
||||||
|
tunAddressPrefix = *dnsCfg.FakeIPRange.IPNet()
|
||||||
|
} else {
|
||||||
|
tunAddressPrefix = netip.MustParsePrefix("198.18.0.1/16")
|
||||||
|
}
|
||||||
|
|
||||||
return &Tun{
|
return &Tun{
|
||||||
Enable: rawTun.Enable,
|
Enable: rawTun.Enable,
|
||||||
Device: rawTun.Device,
|
Device: rawTun.Device,
|
||||||
@ -899,6 +924,7 @@ func parseTun(rawTun RawTun, general *General) (*Tun, error) {
|
|||||||
DNSHijack: dnsHijack,
|
DNSHijack: dnsHijack,
|
||||||
AutoRoute: rawTun.AutoRoute,
|
AutoRoute: rawTun.AutoRoute,
|
||||||
AutoDetectInterface: rawTun.AutoDetectInterface,
|
AutoDetectInterface: rawTun.AutoDetectInterface,
|
||||||
|
TunAddressPrefix: tunAddressPrefix,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ import (
|
|||||||
var initMode = true
|
var initMode = true
|
||||||
|
|
||||||
func downloadMMDB(path string) (err error) {
|
func downloadMMDB(path string) (err error) {
|
||||||
resp, err := http.Get("https://raw.githubusercontents.com/Loyalsoldier/geoip/release/Country.mmdb")
|
resp, err := http.Get(C.MmdbUrl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -32,7 +32,7 @@ func downloadMMDB(path string) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func downloadGeoIP(path string) (err error) {
|
func downloadGeoIP(path string) (err error) {
|
||||||
resp, err := http.Get("https://raw.githubusercontents.com/Loyalsoldier/v2ray-rules-dat/release/geoip.dat")
|
resp, err := http.Get(C.GeoIpUrl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -49,7 +49,7 @@ func downloadGeoIP(path string) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func downloadGeoSite(path string) (err error) {
|
func downloadGeoSite(path string) (err error) {
|
||||||
resp, err := http.Get("https://raw.githubusercontents.com/Loyalsoldier/v2ray-rules-dat/release/geosite.dat")
|
resp, err := http.Get(C.GeoSiteUrl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -74,8 +74,8 @@ func initGeoSite() error {
|
|||||||
log.Infoln("Download GeoSite.dat finish")
|
log.Infoln("Download GeoSite.dat finish")
|
||||||
}
|
}
|
||||||
if initMode {
|
if initMode {
|
||||||
if !geodata.Verify(C.GeositeName) {
|
if err := geodata.Verify(C.GeositeName); err != nil {
|
||||||
log.Warnln("GeoSite.dat invalid, remove and download")
|
log.Warnln("GeoSite.dat invalid, remove and download: %s", err)
|
||||||
if err := os.Remove(C.Path.GeoSite()); err != nil {
|
if err := os.Remove(C.Path.GeoSite()); err != nil {
|
||||||
return fmt.Errorf("can't remove invalid GeoSite.dat: %s", err.Error())
|
return fmt.Errorf("can't remove invalid GeoSite.dat: %s", err.Error())
|
||||||
}
|
}
|
||||||
@ -97,8 +97,8 @@ func initGeoIP() error {
|
|||||||
log.Infoln("Download GeoIP.dat finish")
|
log.Infoln("Download GeoIP.dat finish")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !geodata.Verify(C.GeoipName) {
|
if err := geodata.Verify(C.GeoipName); err != nil {
|
||||||
log.Warnln("GeoIP.dat invalid, remove and download")
|
log.Warnln("GeoIP.dat invalid, remove and download: %s", err)
|
||||||
if err := os.Remove(C.Path.GeoIP()); err != nil {
|
if err := os.Remove(C.Path.GeoIP()); err != nil {
|
||||||
return fmt.Errorf("can't remove invalid GeoIP.dat: %s", err.Error())
|
return fmt.Errorf("can't remove invalid GeoIP.dat: %s", err.Error())
|
||||||
}
|
}
|
||||||
@ -159,6 +159,9 @@ func Init(dir string) error {
|
|||||||
if !C.GeodataMode {
|
if !C.GeodataMode {
|
||||||
C.GeodataMode = rawCfg.GeodataMode
|
C.GeodataMode = rawCfg.GeodataMode
|
||||||
}
|
}
|
||||||
|
C.GeoIpUrl = rawCfg.GeoXUrl.GeoIp
|
||||||
|
C.GeoSiteUrl = rawCfg.GeoXUrl.GeoSite
|
||||||
|
C.MmdbUrl = rawCfg.GeoXUrl.Mmdb
|
||||||
// initial GeoIP
|
// initial GeoIP
|
||||||
if err := initGeoIP(); err != nil {
|
if err := initGeoIP(); err != nil {
|
||||||
return fmt.Errorf("can't initial GeoIP: %w", err)
|
return fmt.Errorf("can't initial GeoIP: %w", err)
|
||||||
|
80
config/updateGeo.go
Normal file
80
config/updateGeo.go
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/Dreamacro/clash/component/geodata"
|
||||||
|
_ "github.com/Dreamacro/clash/component/geodata/standard"
|
||||||
|
C "github.com/Dreamacro/clash/constant"
|
||||||
|
"github.com/oschwald/geoip2-golang"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
func UpdateGeoDatabases() error {
|
||||||
|
defer runtime.GC()
|
||||||
|
geoLoader, err := geodata.GetGeoDataLoader("standard")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if C.GeodataMode {
|
||||||
|
data, err := downloadForBytes(C.GeoIpUrl)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("can't download GeoIP database file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = geoLoader.LoadIPByBytes(data, "cn"); err != nil {
|
||||||
|
return fmt.Errorf("invalid GeoIP database file: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if saveFile(data, C.Path.GeoIP()) != nil {
|
||||||
|
return fmt.Errorf("can't save GeoIP database file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
data, err := downloadForBytes(C.MmdbUrl)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("can't download MMDB database file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
instance, err := geoip2.FromBytes(data)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid MMDB database file: %s", err)
|
||||||
|
}
|
||||||
|
_ = instance.Close()
|
||||||
|
|
||||||
|
if saveFile(data, C.Path.MMDB()) != nil {
|
||||||
|
return fmt.Errorf("can't save MMDB database file: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := downloadForBytes(C.GeoSiteUrl)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("can't download GeoSite database file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = geoLoader.LoadSiteByBytes(data, "cn"); err != nil {
|
||||||
|
return fmt.Errorf("invalid GeoSite database file: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if saveFile(data, C.Path.GeoSite()) != nil {
|
||||||
|
return fmt.Errorf("can't save GeoSite database file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func downloadForBytes(url string) ([]byte, error) {
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
return ioutil.ReadAll(resp.Body)
|
||||||
|
}
|
||||||
|
|
||||||
|
func saveFile(bytes []byte, path string) error {
|
||||||
|
return ioutil.WriteFile(path, bytes, 0o644)
|
||||||
|
}
|
@ -41,6 +41,7 @@ const (
|
|||||||
type Connection interface {
|
type Connection interface {
|
||||||
Chains() Chain
|
Chains() Chain
|
||||||
AppendToChains(adapter ProxyAdapter)
|
AppendToChains(adapter ProxyAdapter)
|
||||||
|
RemoteDestination() string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Chain []string
|
type Chain []string
|
||||||
@ -107,6 +108,11 @@ type ProxyAdapter interface {
|
|||||||
Unwrap(metadata *Metadata) Proxy
|
Unwrap(metadata *Metadata) Proxy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Group interface {
|
||||||
|
URLTest(ctx context.Context, url string) (mp map[string]uint16, err error)
|
||||||
|
GetProxies(touch bool) []Proxy
|
||||||
|
}
|
||||||
|
|
||||||
type DelayHistory struct {
|
type DelayHistory struct {
|
||||||
Time time.Time `json:"time"`
|
Time time.Time `json:"time"`
|
||||||
Delay uint16 `json:"delay"`
|
Delay uint16 `json:"delay"`
|
||||||
|
7
constant/features/no_doq.go
Normal file
7
constant/features/no_doq.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
//go:build no_doq
|
||||||
|
|
||||||
|
package features
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
TAGS = append(TAGS, "no_doq")
|
||||||
|
}
|
7
constant/features/no_gvisor.go
Normal file
7
constant/features/no_gvisor.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
//go:build no_gvisor
|
||||||
|
|
||||||
|
package features
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
TAGS = append(TAGS, "no_gvisor")
|
||||||
|
}
|
3
constant/features/tags.go
Normal file
3
constant/features/tags.go
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
package features
|
||||||
|
|
||||||
|
var TAGS = make([]string, 0, 0)
|
@ -1,3 +1,8 @@
|
|||||||
package constant
|
package constant
|
||||||
|
|
||||||
var GeodataMode bool
|
var (
|
||||||
|
GeodataMode bool
|
||||||
|
GeoIpUrl string
|
||||||
|
MmdbUrl string
|
||||||
|
GeoSiteUrl string
|
||||||
|
)
|
||||||
|
@ -19,7 +19,7 @@ const (
|
|||||||
ALLNet
|
ALLNet
|
||||||
|
|
||||||
HTTP Type = iota
|
HTTP Type = iota
|
||||||
HTTPCONNECT
|
HTTPS
|
||||||
SOCKS4
|
SOCKS4
|
||||||
SOCKS5
|
SOCKS5
|
||||||
REDIR
|
REDIR
|
||||||
@ -49,8 +49,8 @@ func (t Type) String() string {
|
|||||||
switch t {
|
switch t {
|
||||||
case HTTP:
|
case HTTP:
|
||||||
return "HTTP"
|
return "HTTP"
|
||||||
case HTTPCONNECT:
|
case HTTPS:
|
||||||
return "HTTP Connect"
|
return "HTTPS"
|
||||||
case SOCKS4:
|
case SOCKS4:
|
||||||
return "Socks4"
|
return "Socks4"
|
||||||
case SOCKS5:
|
case SOCKS5:
|
||||||
@ -68,6 +68,31 @@ func (t Type) String() string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ParseType(t string) (*Type, error) {
|
||||||
|
var res Type
|
||||||
|
switch t {
|
||||||
|
case "HTTP":
|
||||||
|
res = HTTP
|
||||||
|
case "HTTPS":
|
||||||
|
res = HTTPS
|
||||||
|
case "SOCKS4":
|
||||||
|
res = SOCKS4
|
||||||
|
case "SOCKS5":
|
||||||
|
res = SOCKS5
|
||||||
|
case "REDIR":
|
||||||
|
res = REDIR
|
||||||
|
case "TPROXY":
|
||||||
|
res = TPROXY
|
||||||
|
case "TUN":
|
||||||
|
res = TUN
|
||||||
|
case "INNER":
|
||||||
|
res = INNER
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unknown type: %s", t)
|
||||||
|
}
|
||||||
|
return &res, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (t Type) MarshalJSON() ([]byte, error) {
|
func (t Type) MarshalJSON() ([]byte, error) {
|
||||||
return json.Marshal(t.String())
|
return json.Marshal(t.String())
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package provider
|
package provider
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Vehicle Type
|
// Vehicle Type
|
||||||
@ -65,10 +65,10 @@ type Provider interface {
|
|||||||
// ProxyProvider interface
|
// ProxyProvider interface
|
||||||
type ProxyProvider interface {
|
type ProxyProvider interface {
|
||||||
Provider
|
Provider
|
||||||
Proxies() []constant.Proxy
|
Proxies() []C.Proxy
|
||||||
// ProxiesWithTouch is used to inform the provider that the proxy is actually being used while getting the list of proxies.
|
// ProxiesWithTouch is used to inform the provider that the proxy is actually being used while getting the list of proxies.
|
||||||
// Commonly used in DialContext and DialPacketConn
|
// Commonly used in DialContext and DialPacketConn
|
||||||
ProxiesWithTouch() []constant.Proxy
|
ProxiesWithTouch() []C.Proxy
|
||||||
HealthCheck()
|
HealthCheck()
|
||||||
Version() uint
|
Version() uint
|
||||||
}
|
}
|
||||||
@ -100,7 +100,7 @@ func (rt RuleType) String() string {
|
|||||||
type RuleProvider interface {
|
type RuleProvider interface {
|
||||||
Provider
|
Provider
|
||||||
Behavior() RuleType
|
Behavior() RuleType
|
||||||
Match(*constant.Metadata) bool
|
Match(*C.Metadata) bool
|
||||||
ShouldResolveIP() bool
|
ShouldResolveIP() bool
|
||||||
AsRule(adaptor string) constant.Rule
|
AsRule(adaptor string) C.Rule
|
||||||
}
|
}
|
||||||
|
@ -9,13 +9,17 @@ const (
|
|||||||
GEOIP
|
GEOIP
|
||||||
IPCIDR
|
IPCIDR
|
||||||
SrcIPCIDR
|
SrcIPCIDR
|
||||||
|
IPSuffix
|
||||||
|
SrcIPSuffix
|
||||||
SrcPort
|
SrcPort
|
||||||
DstPort
|
DstPort
|
||||||
Process
|
Process
|
||||||
|
Script
|
||||||
ProcessPath
|
ProcessPath
|
||||||
RuleSet
|
RuleSet
|
||||||
Network
|
Network
|
||||||
Uid
|
Uid
|
||||||
|
INTYPE
|
||||||
MATCH
|
MATCH
|
||||||
AND
|
AND
|
||||||
OR
|
OR
|
||||||
@ -40,6 +44,10 @@ func (rt RuleType) String() string {
|
|||||||
return "IPCIDR"
|
return "IPCIDR"
|
||||||
case SrcIPCIDR:
|
case SrcIPCIDR:
|
||||||
return "SrcIPCIDR"
|
return "SrcIPCIDR"
|
||||||
|
case IPSuffix:
|
||||||
|
return "IPSuffix"
|
||||||
|
case SrcIPSuffix:
|
||||||
|
return "SrcIPSuffix"
|
||||||
case SrcPort:
|
case SrcPort:
|
||||||
return "SrcPort"
|
return "SrcPort"
|
||||||
case DstPort:
|
case DstPort:
|
||||||
@ -54,8 +62,12 @@ func (rt RuleType) String() string {
|
|||||||
return "RuleSet"
|
return "RuleSet"
|
||||||
case Network:
|
case Network:
|
||||||
return "Network"
|
return "Network"
|
||||||
|
case Script:
|
||||||
|
return "Script"
|
||||||
case Uid:
|
case Uid:
|
||||||
return "Uid"
|
return "Uid"
|
||||||
|
case INTYPE:
|
||||||
|
return "InType"
|
||||||
case AND:
|
case AND:
|
||||||
return "AND"
|
return "AND"
|
||||||
case OR:
|
case OR:
|
||||||
|
27
constant/script.go
Normal file
27
constant/script.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package constant
|
||||||
|
|
||||||
|
type JSRuleMetadata struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Network string `json:"network"`
|
||||||
|
Host string `json:"host"`
|
||||||
|
SrcIP string `json:"srcIP"`
|
||||||
|
DstIP string `json:"dstIP"`
|
||||||
|
SrcPort string `json:"srcPort"`
|
||||||
|
DstPort string `json:"dstPort"`
|
||||||
|
Uid *int32 `json:"uid"`
|
||||||
|
Process string `json:"process"`
|
||||||
|
ProcessPath string `json:"processPath"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DnsType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
IPv4 = 1 << iota
|
||||||
|
IPv6
|
||||||
|
All
|
||||||
|
)
|
||||||
|
|
||||||
|
type JSFunction interface {
|
||||||
|
//Resolve host to ip by Clash DNS
|
||||||
|
Resolve(host string, resolveType DnsType) []string
|
||||||
|
}
|
@ -82,12 +82,16 @@ func (r *Resolver) ResolveAllIP(host string) (ips []netip.Addr, err error) {
|
|||||||
|
|
||||||
ips, err = r.resolveIP(host, D.TypeA)
|
ips, err = r.resolveIP(host, D.TypeA)
|
||||||
|
|
||||||
ipv6s, open := <-ch
|
select {
|
||||||
if !open && err != nil {
|
case ipv6s, open := <-ch:
|
||||||
return nil, resolver.ErrIPNotFound
|
if !open && err != nil {
|
||||||
|
return nil, resolver.ErrIPNotFound
|
||||||
|
}
|
||||||
|
ips = append(ips, ipv6s...)
|
||||||
|
case <-time.After(1 * time.Millisecond):
|
||||||
|
// wait ipv6 result
|
||||||
}
|
}
|
||||||
|
|
||||||
ips = append(ips, ipv6s...)
|
|
||||||
return ips, nil
|
return ips, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
10
go.mod
10
go.mod
@ -4,7 +4,9 @@ go 1.18
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Dreamacro/go-shadowsocks2 v0.1.8
|
github.com/Dreamacro/go-shadowsocks2 v0.1.8
|
||||||
github.com/dlclark/regexp2 v1.4.0
|
github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91
|
||||||
|
github.com/dop251/goja v0.0.0-20220516123900-4418d4575a41
|
||||||
|
github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d
|
||||||
github.com/go-chi/chi/v5 v5.0.7
|
github.com/go-chi/chi/v5 v5.0.7
|
||||||
github.com/go-chi/cors v1.2.1
|
github.com/go-chi/cors v1.2.1
|
||||||
github.com/go-chi/render v1.0.1
|
github.com/go-chi/render v1.0.1
|
||||||
@ -16,6 +18,7 @@ require (
|
|||||||
github.com/oschwald/geoip2-golang v1.7.0
|
github.com/oschwald/geoip2-golang v1.7.0
|
||||||
github.com/sirupsen/logrus v1.8.1
|
github.com/sirupsen/logrus v1.8.1
|
||||||
github.com/stretchr/testify v1.7.1
|
github.com/stretchr/testify v1.7.1
|
||||||
|
github.com/vishvananda/netlink v1.2.0-beta.0.20220404152918-5e915e014938
|
||||||
github.com/xtls/go v0.0.0-20210920065950-d4af136d3672
|
github.com/xtls/go v0.0.0-20210920065950-d4af136d3672
|
||||||
go.etcd.io/bbolt v1.3.6
|
go.etcd.io/bbolt v1.3.6
|
||||||
go.uber.org/atomic v1.9.0
|
go.uber.org/atomic v1.9.0
|
||||||
@ -37,9 +40,9 @@ require (
|
|||||||
github.com/cheekybits/genny v1.0.0 // indirect
|
github.com/cheekybits/genny v1.0.0 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.5.4 // indirect
|
github.com/fsnotify/fsnotify v1.5.4 // indirect
|
||||||
|
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||||
github.com/google/btree v1.0.1 // indirect
|
github.com/google/btree v1.0.1 // indirect
|
||||||
github.com/kr/pretty v0.2.1 // indirect
|
|
||||||
github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect
|
github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect
|
||||||
github.com/marten-seemann/qtls-go1-17 v0.1.1 // indirect
|
github.com/marten-seemann/qtls-go1-17 v0.1.1 // indirect
|
||||||
github.com/marten-seemann/qtls-go1-18 v0.1.1 // indirect
|
github.com/marten-seemann/qtls-go1-18 v0.1.1 // indirect
|
||||||
@ -48,6 +51,7 @@ require (
|
|||||||
github.com/oschwald/maxminddb-golang v1.9.0 // indirect
|
github.com/oschwald/maxminddb-golang v1.9.0 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/u-root/uio v0.0.0-20220204230159-dac05f7d2cb4 // indirect
|
github.com/u-root/uio v0.0.0-20220204230159-dac05f7d2cb4 // indirect
|
||||||
|
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
|
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
|
||||||
golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab // indirect
|
golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab // indirect
|
||||||
golang.org/x/tools v0.1.10 // indirect
|
golang.org/x/tools v0.1.10 // indirect
|
||||||
@ -57,4 +61,4 @@ require (
|
|||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
replace golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 => github.com/MetaCubeX/wintun-go v0.0.0-20220319102620-bbc5e6b2015e
|
replace github.com/vishvananda/netlink v1.2.0-beta.0.20220404152918-5e915e014938 => github.com/MetaCubeX/netlink v1.2.0-beta.0.20220529072258-d6853f887820
|
||||||
|
37
go.sum
37
go.sum
@ -10,8 +10,8 @@ git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGy
|
|||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/Dreamacro/go-shadowsocks2 v0.1.8 h1:Ixejp5JscEc866gAvm/l6TFd7BOBvDviKgwb1quWw3g=
|
github.com/Dreamacro/go-shadowsocks2 v0.1.8 h1:Ixejp5JscEc866gAvm/l6TFd7BOBvDviKgwb1quWw3g=
|
||||||
github.com/Dreamacro/go-shadowsocks2 v0.1.8/go.mod h1:51y4Q6tJoCE7e8TmYXcQRqfoxPfE9Cvn79V6pB6Df7Y=
|
github.com/Dreamacro/go-shadowsocks2 v0.1.8/go.mod h1:51y4Q6tJoCE7e8TmYXcQRqfoxPfE9Cvn79V6pB6Df7Y=
|
||||||
github.com/MetaCubeX/wintun-go v0.0.0-20220319102620-bbc5e6b2015e h1:GRfT5Lf8HP7RNczKIwTYLoCh1PPuIs/sY9hj+W+3deg=
|
github.com/MetaCubeX/netlink v1.2.0-beta.0.20220529072258-d6853f887820 h1:fGKWZ25VApYnuPZoNeqdH/nZtHa2XMajwH6Yj/OgoVc=
|
||||||
github.com/MetaCubeX/wintun-go v0.0.0-20220319102620-bbc5e6b2015e/go.mod h1:ARUuShAtcziEJ/vnZ2hgoP+zc0J7Ukcca2S/NPDoQCc=
|
github.com/MetaCubeX/netlink v1.2.0-beta.0.20220529072258-d6853f887820/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
|
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
|
||||||
@ -20,11 +20,18 @@ github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitf
|
|||||||
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
|
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
|
github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91 h1:Izz0+t1Z5nI16/II7vuEo/nHjodOg0p7+OiDpjX5t1E=
|
||||||
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||||
|
github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
|
||||||
|
github.com/dop251/goja v0.0.0-20220516123900-4418d4575a41 h1:yRPjAkkuR/E/tsVG7QmhzEeEtD3P2yllxsT1/ftURb0=
|
||||||
|
github.com/dop251/goja v0.0.0-20220516123900-4418d4575a41/go.mod h1:TQJQ+ZNyFVvUtUEtCZxBhfWiH7RJqR3EivNmvD6Waik=
|
||||||
|
github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y=
|
||||||
|
github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d h1:W1n4DvpzZGOISgp7wWNtraLcHtnmnTwBlJidqtMIuwQ=
|
||||||
|
github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM=
|
||||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc=
|
github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc=
|
||||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||||
@ -42,6 +49,8 @@ github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vz
|
|||||||
github.com/go-chi/render v1.0.1 h1:4/5tis2cKaNdnv9zFLfXzcquC9HbeZgCnxGnKrltBS8=
|
github.com/go-chi/render v1.0.1 h1:4/5tis2cKaNdnv9zFLfXzcquC9HbeZgCnxGnKrltBS8=
|
||||||
github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns=
|
github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns=
|
||||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||||
|
github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU=
|
||||||
|
github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||||
github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0=
|
github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0=
|
||||||
@ -100,12 +109,14 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1
|
|||||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
|
||||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
|
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||||
|
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/lucas-clemente/quic-go v0.27.0 h1:v6WY87q9zD4dKASbG8hy/LpzAVNzEQzw8sEIeloJsc4=
|
github.com/lucas-clemente/quic-go v0.27.0 h1:v6WY87q9zD4dKASbG8hy/LpzAVNzEQzw8sEIeloJsc4=
|
||||||
github.com/lucas-clemente/quic-go v0.27.0/go.mod h1:AzgQoPda7N+3IqMMMkywBKggIFo2KT6pfnlrQ2QieeI=
|
github.com/lucas-clemente/quic-go v0.27.0/go.mod h1:AzgQoPda7N+3IqMMMkywBKggIFo2KT6pfnlrQ2QieeI=
|
||||||
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
||||||
@ -159,6 +170,8 @@ github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP
|
|||||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||||
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||||
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
|
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
|
||||||
|
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||||
github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
|
github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
|
||||||
@ -202,6 +215,9 @@ github.com/u-root/uio v0.0.0-20220204230159-dac05f7d2cb4 h1:hl6sK6aFgTLISijk6xIz
|
|||||||
github.com/u-root/uio v0.0.0-20220204230159-dac05f7d2cb4/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA=
|
github.com/u-root/uio v0.0.0-20220204230159-dac05f7d2cb4/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA=
|
||||||
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
|
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
|
||||||
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
|
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
|
||||||
|
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/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/xtls/go v0.0.0-20210920065950-d4af136d3672 h1:4mkzGhKqt3JO1BWYjtD3iRFyAx4ow67hmSqOcGjuxqQ=
|
github.com/xtls/go v0.0.0-20210920065950-d4af136d3672 h1:4mkzGhKqt3JO1BWYjtD3iRFyAx4ow67hmSqOcGjuxqQ=
|
||||||
github.com/xtls/go v0.0.0-20210920065950-d4af136d3672/go.mod h1:YGGVbz9cOxyKFUmhW7LGaLZaMA0cPlHJinvAmVxEMSU=
|
github.com/xtls/go v0.0.0-20210920065950-d4af136d3672/go.mod h1:YGGVbz9cOxyKFUmhW7LGaLZaMA0cPlHJinvAmVxEMSU=
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
@ -289,9 +305,11 @@ golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@ -313,6 +331,7 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3
|
|||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab h1:eHo2TTVBaAPw9lDGK2Gb9GyPMXT6g7O63W6sx3ylbzU=
|
golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab h1:eHo2TTVBaAPw9lDGK2Gb9GyPMXT6g7O63W6sx3ylbzU=
|
||||||
golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0=
|
golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0=
|
||||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
@ -338,6 +357,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
|
|||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f h1:GGU+dLjvlC3qDwqYgL6UgRmHXhOOgns0bZu2Ty5mm6U=
|
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f h1:GGU+dLjvlC3qDwqYgL6UgRmHXhOOgns0bZu2Ty5mm6U=
|
||||||
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 h1:Ug9qvr1myri/zFN6xL17LSCBGFDnphBBhzmILHsM5TY=
|
||||||
|
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20220407013110-ef5c587f782d h1:q4JksJ2n0fmbXC0Aj0eOs6E0AcPqnKglxWXWFqGD6x0=
|
golang.zx2c4.com/wireguard v0.0.0-20220407013110-ef5c587f782d h1:q4JksJ2n0fmbXC0Aj0eOs6E0AcPqnKglxWXWFqGD6x0=
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20220407013110-ef5c587f782d/go.mod h1:bVQfyl2sCM/QIIGHpWbFGfHPuDvqnCNkT6MQLTCjO/U=
|
golang.zx2c4.com/wireguard v0.0.0-20220407013110-ef5c587f782d/go.mod h1:bVQfyl2sCM/QIIGHpWbFGfHPuDvqnCNkT6MQLTCjO/U=
|
||||||
golang.zx2c4.com/wireguard/windows v0.5.4-0.20220317000008-6432784c2469 h1:SEYkJAIuYAsSAPkCffOiYLtq5brBDSI+L0mRjSsvSTY=
|
golang.zx2c4.com/wireguard/windows v0.5.4-0.20220317000008-6432784c2469 h1:SEYkJAIuYAsSAPkCffOiYLtq5brBDSI+L0mRjSsvSTY=
|
||||||
@ -369,8 +390,10 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
|
|||||||
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
|
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
|
||||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||||
|
@ -2,6 +2,7 @@ package executor
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/Dreamacro/clash/component/process"
|
||||||
"github.com/Dreamacro/clash/listener/inner"
|
"github.com/Dreamacro/clash/listener/inner"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
@ -85,7 +86,7 @@ func ApplyConfig(cfg *config.Config, force bool) {
|
|||||||
loadRuleProvider(cfg.RuleProviders)
|
loadRuleProvider(cfg.RuleProviders)
|
||||||
updateGeneral(cfg.General, force)
|
updateGeneral(cfg.General, force)
|
||||||
updateIPTables(cfg)
|
updateIPTables(cfg)
|
||||||
updateTun(cfg.Tun, cfg.DNS)
|
updateTun(cfg.Tun)
|
||||||
updateExperimental(cfg)
|
updateExperimental(cfg)
|
||||||
|
|
||||||
log.SetLevel(cfg.General.LogLevel)
|
log.SetLevel(cfg.General.LogLevel)
|
||||||
@ -118,6 +119,9 @@ func GetGeneral() *config.General {
|
|||||||
IPv6: !resolver.DisableIPv6,
|
IPv6: !resolver.DisableIPv6,
|
||||||
GeodataLoader: G.LoaderName(),
|
GeodataLoader: G.LoaderName(),
|
||||||
Tun: P.GetTunConf(),
|
Tun: P.GetTunConf(),
|
||||||
|
Interface: dialer.DefaultInterface.Load(),
|
||||||
|
Sniffing: tunnel.IsSniffing(),
|
||||||
|
TCPConcurrent: dialer.GetDial(),
|
||||||
}
|
}
|
||||||
|
|
||||||
return general
|
return general
|
||||||
@ -244,12 +248,8 @@ func loadProxyProvider(proxyProviders map[string]provider.ProxyProvider) {
|
|||||||
wg.Wait()
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateTun(tun *config.Tun, dns *config.DNS) {
|
func updateTun(tun *config.Tun) {
|
||||||
var tunAddressPrefix *netip.Prefix
|
P.ReCreateTun(tun, tunnel.TCPIn(), tunnel.UDPIn())
|
||||||
if dns.FakeIPRange != nil {
|
|
||||||
tunAddressPrefix = dns.FakeIPRange.IPNet()
|
|
||||||
}
|
|
||||||
P.ReCreateTun(tun, tunAddressPrefix, tunnel.TCPIn(), tunnel.UDPIn())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateSniffer(sniffer *config.Sniffer) {
|
func updateSniffer(sniffer *config.Sniffer) {
|
||||||
@ -274,6 +274,7 @@ func updateSniffer(sniffer *config.Sniffer) {
|
|||||||
|
|
||||||
func updateGeneral(general *config.General, force bool) {
|
func updateGeneral(general *config.General, force bool) {
|
||||||
log.SetLevel(general.LogLevel)
|
log.SetLevel(general.LogLevel)
|
||||||
|
process.EnableFindProcess(general.EnableProcess)
|
||||||
tunnel.SetMode(general.Mode)
|
tunnel.SetMode(general.Mode)
|
||||||
dialer.DisableIPv6 = !general.IPv6
|
dialer.DisableIPv6 = !general.IPv6
|
||||||
if !dialer.DisableIPv6 {
|
if !dialer.DisableIPv6 {
|
||||||
@ -353,7 +354,7 @@ func patchSelectGroup(proxies map[string]C.Proxy) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
selector, ok := outbound.ProxyAdapter.(*outboundgroup.Selector)
|
selector, ok := outbound.ProxyAdapter.(outboundgroup.SelectAble)
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -428,7 +429,7 @@ func updateIPTables(cfg *config.Config) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Shutdown() {
|
func Shutdown() {
|
||||||
P.Cleanup()
|
P.Cleanup(false)
|
||||||
tproxy.CleanupTProxyIPTables()
|
tproxy.CleanupTProxyIPTables()
|
||||||
resolver.StoreFakePoolState()
|
resolver.StoreFakePoolState()
|
||||||
|
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
package route
|
package route
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/Dreamacro/clash/component/dialer"
|
||||||
"net/http"
|
"net/http"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/component/resolver"
|
"github.com/Dreamacro/clash/component/resolver"
|
||||||
"github.com/Dreamacro/clash/config"
|
"github.com/Dreamacro/clash/config"
|
||||||
@ -16,26 +18,34 @@ import (
|
|||||||
"github.com/go-chi/render"
|
"github.com/go-chi/render"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
updateGeoMux sync.Mutex
|
||||||
|
updatingGeo = false
|
||||||
|
)
|
||||||
|
|
||||||
func configRouter() http.Handler {
|
func configRouter() http.Handler {
|
||||||
r := chi.NewRouter()
|
r := chi.NewRouter()
|
||||||
r.Get("/", getConfigs)
|
r.Get("/", getConfigs)
|
||||||
r.Put("/", updateConfigs)
|
r.Put("/", updateConfigs)
|
||||||
|
r.Post("/geo", updateGeoDatabases)
|
||||||
r.Patch("/", patchConfigs)
|
r.Patch("/", patchConfigs)
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
type configSchema struct {
|
type configSchema struct {
|
||||||
Port *int `json:"port"`
|
Port *int `json:"port"`
|
||||||
SocksPort *int `json:"socks-port"`
|
SocksPort *int `json:"socks-port"`
|
||||||
RedirPort *int `json:"redir-port"`
|
RedirPort *int `json:"redir-port"`
|
||||||
TProxyPort *int `json:"tproxy-port"`
|
TProxyPort *int `json:"tproxy-port"`
|
||||||
MixedPort *int `json:"mixed-port"`
|
MixedPort *int `json:"mixed-port"`
|
||||||
Tun *config.Tun `json:"tun"`
|
Tun *config.Tun `json:"tun"`
|
||||||
AllowLan *bool `json:"allow-lan"`
|
AllowLan *bool `json:"allow-lan"`
|
||||||
BindAddress *string `json:"bind-address"`
|
BindAddress *string `json:"bind-address"`
|
||||||
Mode *tunnel.TunnelMode `json:"mode"`
|
Mode *tunnel.TunnelMode `json:"mode"`
|
||||||
LogLevel *log.LogLevel `json:"log-level"`
|
LogLevel *log.LogLevel `json:"log-level"`
|
||||||
IPv6 *bool `json:"ipv6"`
|
IPv6 *bool `json:"ipv6"`
|
||||||
|
Sniffing *bool `json:"sniffing"`
|
||||||
|
TcpConcurrent *bool `json:"tcp-concurrent"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func getConfigs(w http.ResponseWriter, r *http.Request) {
|
func getConfigs(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -67,6 +77,14 @@ func patchConfigs(w http.ResponseWriter, r *http.Request) {
|
|||||||
P.SetBindAddress(*general.BindAddress)
|
P.SetBindAddress(*general.BindAddress)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if general.Sniffing != nil {
|
||||||
|
tunnel.SetSniffing(*general.Sniffing)
|
||||||
|
}
|
||||||
|
|
||||||
|
if general.TcpConcurrent != nil {
|
||||||
|
dialer.SetDial(*general.TcpConcurrent)
|
||||||
|
}
|
||||||
|
|
||||||
ports := P.GetPorts()
|
ports := P.GetPorts()
|
||||||
|
|
||||||
tcpIn := tunnel.TCPIn()
|
tcpIn := tunnel.TCPIn()
|
||||||
@ -138,3 +156,42 @@ func updateConfigs(w http.ResponseWriter, r *http.Request) {
|
|||||||
executor.ApplyConfig(cfg, force)
|
executor.ApplyConfig(cfg, force)
|
||||||
render.NoContent(w, r)
|
render.NoContent(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateGeoDatabases(w http.ResponseWriter, r *http.Request) {
|
||||||
|
updateGeoMux.Lock()
|
||||||
|
|
||||||
|
if updatingGeo {
|
||||||
|
updateGeoMux.Unlock()
|
||||||
|
render.Status(r, http.StatusBadRequest)
|
||||||
|
render.JSON(w, r, newError("updating..."))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
updatingGeo = true
|
||||||
|
updateGeoMux.Unlock()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer func() {
|
||||||
|
updatingGeo = false
|
||||||
|
}()
|
||||||
|
|
||||||
|
log.Warnln("[REST-API] updating GEO databases...")
|
||||||
|
|
||||||
|
if err := config.UpdateGeoDatabases(); err != nil {
|
||||||
|
log.Errorln("[REST-API] update GEO databases failed: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg, err := executor.ParseWithPath(constant.Path.Config())
|
||||||
|
if err != nil {
|
||||||
|
log.Errorln("[REST-API] update GEO databases failed: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Warnln("[REST-API] update GEO databases successful, apply config...")
|
||||||
|
|
||||||
|
executor.ApplyConfig(cfg, false)
|
||||||
|
}()
|
||||||
|
|
||||||
|
render.NoContent(w, r)
|
||||||
|
}
|
||||||
|
79
hub/route/groups.go
Normal file
79
hub/route/groups.go
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
package route
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/Dreamacro/clash/adapter"
|
||||||
|
C "github.com/Dreamacro/clash/constant"
|
||||||
|
"github.com/Dreamacro/clash/tunnel"
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
|
"github.com/go-chi/render"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GroupRouter() http.Handler {
|
||||||
|
r := chi.NewRouter()
|
||||||
|
r.Get("/", getGroups)
|
||||||
|
|
||||||
|
r.Route("/{name}", func(r chi.Router) {
|
||||||
|
r.Use(parseProxyName, findProxyByName)
|
||||||
|
r.Get("/", getGroup)
|
||||||
|
r.Get("/delay", getGroupDelay)
|
||||||
|
})
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func getGroups(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var gs []C.Proxy
|
||||||
|
for _, p := range tunnel.Proxies() {
|
||||||
|
if _, ok := p.(*adapter.Proxy).ProxyAdapter.(C.Group); ok {
|
||||||
|
gs = append(gs, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
render.JSON(w, r, render.M{
|
||||||
|
"proxies": gs,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func getGroup(w http.ResponseWriter, r *http.Request) {
|
||||||
|
proxy := r.Context().Value(CtxKeyProxy).(C.Proxy)
|
||||||
|
if _, ok := proxy.(*adapter.Proxy).ProxyAdapter.(C.Group); ok {
|
||||||
|
render.JSON(w, r, proxy)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
render.Status(r, http.StatusNotFound)
|
||||||
|
render.JSON(w, r, ErrNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getGroupDelay(w http.ResponseWriter, r *http.Request) {
|
||||||
|
proxy := r.Context().Value(CtxKeyProxy).(C.Proxy)
|
||||||
|
group, ok := proxy.(*adapter.Proxy).ProxyAdapter.(C.Group)
|
||||||
|
if !ok {
|
||||||
|
render.Status(r, http.StatusNotFound)
|
||||||
|
render.JSON(w, r, ErrNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
query := r.URL.Query()
|
||||||
|
url := query.Get("url")
|
||||||
|
timeout, err := strconv.ParseInt(query.Get("timeout"), 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
render.Status(r, http.StatusBadRequest)
|
||||||
|
render.JSON(w, r, ErrBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*time.Duration(timeout))
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
dm, err := group.URLTest(ctx, url)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
render.Status(r, http.StatusGatewayTimeout)
|
||||||
|
render.JSON(w, r, newError(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.JSON(w, r, dm)
|
||||||
|
}
|
@ -83,7 +83,7 @@ func updateProxy(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
proxy := r.Context().Value(CtxKeyProxy).(*adapter.Proxy)
|
proxy := r.Context().Value(CtxKeyProxy).(*adapter.Proxy)
|
||||||
selector, ok := proxy.ProxyAdapter.(*outboundgroup.Selector)
|
selector, ok := proxy.ProxyAdapter.(outboundgroup.SelectAble)
|
||||||
if !ok {
|
if !ok {
|
||||||
render.Status(r, http.StatusBadRequest)
|
render.Status(r, http.StatusBadRequest)
|
||||||
render.JSON(w, r, newError("Must be a Selector"))
|
render.JSON(w, r, newError("Must be a Selector"))
|
||||||
|
@ -20,7 +20,7 @@ type Rule struct {
|
|||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Payload string `json:"payload"`
|
Payload string `json:"payload"`
|
||||||
Proxy string `json:"proxy"`
|
Proxy string `json:"proxy"`
|
||||||
Size int `json:"Size"`
|
Size int `json:"size"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func getRules(w http.ResponseWriter, r *http.Request) {
|
func getRules(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -68,6 +68,7 @@ func Start(addr string, secret string) {
|
|||||||
r.Get("/version", version)
|
r.Get("/version", version)
|
||||||
r.Mount("/configs", configRouter())
|
r.Mount("/configs", configRouter())
|
||||||
r.Mount("/proxies", proxyRouter())
|
r.Mount("/proxies", proxyRouter())
|
||||||
|
r.Mount("/group", GroupRouter())
|
||||||
r.Mount("/rules", ruleRouter())
|
r.Mount("/rules", ruleRouter())
|
||||||
r.Mount("/connections", connectionRouter())
|
r.Mount("/connections", connectionRouter())
|
||||||
r.Mount("/providers/proxies", proxyProviderRouter())
|
r.Mount("/providers/proxies", proxyProviderRouter())
|
||||||
|
@ -2,11 +2,9 @@ package proxy
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/Dreamacro/clash/common/cmd"
|
|
||||||
"github.com/Dreamacro/clash/listener/inner"
|
"github.com/Dreamacro/clash/listener/inner"
|
||||||
|
"github.com/Dreamacro/clash/listener/tun/ipstack/commons"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
|
||||||
"runtime"
|
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
@ -25,10 +23,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
allowLan = false
|
allowLan = false
|
||||||
bindAddress = "*"
|
bindAddress = "*"
|
||||||
lastTunConf *config.Tun
|
lastTunConf *config.Tun
|
||||||
lastTunAddressPrefix *netip.Prefix
|
|
||||||
|
|
||||||
socksListener *socks.Listener
|
socksListener *socks.Listener
|
||||||
socksUDPListener *socks.UDPListener
|
socksUDPListener *socks.UDPListener
|
||||||
@ -327,7 +324,7 @@ func ReCreateMixed(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.P
|
|||||||
log.Infoln("Mixed(http+socks) proxy listening at: %s", mixedListener.Address())
|
log.Infoln("Mixed(http+socks) proxy listening at: %s", mixedListener.Address())
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReCreateTun(tunConf *config.Tun, tunAddressPrefix *netip.Prefix, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) {
|
func ReCreateTun(tunConf *config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) {
|
||||||
tunMux.Lock()
|
tunMux.Lock()
|
||||||
defer tunMux.Unlock()
|
defer tunMux.Unlock()
|
||||||
|
|
||||||
@ -335,35 +332,23 @@ func ReCreateTun(tunConf *config.Tun, tunAddressPrefix *netip.Prefix, tcpIn chan
|
|||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorln("Start TUN listening error: %s", err.Error())
|
log.Errorln("Start TUN listening error: %s", err.Error())
|
||||||
|
Cleanup(false)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if tunAddressPrefix == nil {
|
if !hasTunConfigChange(tunConf) {
|
||||||
tunAddressPrefix = lastTunAddressPrefix
|
|
||||||
}
|
|
||||||
|
|
||||||
if !hasTunConfigChange(tunConf, tunAddressPrefix) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if tunStackListener != nil {
|
Cleanup(true)
|
||||||
_ = tunStackListener.Close()
|
|
||||||
tunStackListener = nil
|
|
||||||
lastTunConf = nil
|
|
||||||
lastTunAddressPrefix = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if !tunConf.Enable {
|
if !tunConf.Enable {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tunStackListener, err = tun.New(tunConf, tunAddressPrefix, tcpIn, udpIn)
|
tunStackListener, err = tun.New(tunConf, tcpIn, udpIn)
|
||||||
if err != nil {
|
|
||||||
log.Warnln("Failed to start TUN interface: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
lastTunConf = tunConf
|
lastTunConf = tunConf
|
||||||
lastTunAddressPrefix = tunAddressPrefix
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPorts return the ports of proxy servers
|
// GetPorts return the ports of proxy servers
|
||||||
@ -422,7 +407,7 @@ func genAddr(host string, port int, allowLan bool) string {
|
|||||||
return fmt.Sprintf("127.0.0.1:%d", port)
|
return fmt.Sprintf("127.0.0.1:%d", port)
|
||||||
}
|
}
|
||||||
|
|
||||||
func hasTunConfigChange(tunConf *config.Tun, tunAddressPrefix *netip.Prefix) bool {
|
func hasTunConfigChange(tunConf *config.Tun) bool {
|
||||||
if lastTunConf == nil {
|
if lastTunConf == nil {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -448,29 +433,29 @@ func hasTunConfigChange(tunConf *config.Tun, tunAddressPrefix *netip.Prefix) boo
|
|||||||
if lastTunConf.Enable != tunConf.Enable ||
|
if lastTunConf.Enable != tunConf.Enable ||
|
||||||
lastTunConf.Device != tunConf.Device ||
|
lastTunConf.Device != tunConf.Device ||
|
||||||
lastTunConf.Stack != tunConf.Stack ||
|
lastTunConf.Stack != tunConf.Stack ||
|
||||||
lastTunConf.AutoRoute != tunConf.AutoRoute {
|
lastTunConf.AutoRoute != tunConf.AutoRoute ||
|
||||||
|
lastTunConf.AutoDetectInterface != tunConf.AutoDetectInterface {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tunAddressPrefix != nil && lastTunAddressPrefix == nil) || (tunAddressPrefix == nil && lastTunAddressPrefix != nil) {
|
if tunConf.TunAddressPrefix.String() != lastTunConf.TunAddressPrefix.String() {
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if tunAddressPrefix != nil && lastTunAddressPrefix != nil && *tunAddressPrefix != *lastTunAddressPrefix {
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func Cleanup() {
|
func Cleanup(wait bool) {
|
||||||
if tunStackListener != nil {
|
if tunStackListener != nil {
|
||||||
_ = tunStackListener.Close()
|
_ = tunStackListener.Close()
|
||||||
if runtime.GOOS == "android" {
|
commons.StopDefaultInterfaceChangeMonitor()
|
||||||
prefs := []int{9000, 9001, 9002, 9003, 9004}
|
|
||||||
for _, pref := range prefs {
|
if wait {
|
||||||
_, _ = cmd.ExecCmd(fmt.Sprintf("ip rule del pref %d", pref))
|
commons.WaitForTunClose(lastTunConf.Device)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
commons.CleanupRule()
|
||||||
}
|
}
|
||||||
|
tunStackListener = nil
|
||||||
|
lastTunConf = nil
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
//go:build !no_gvisor
|
||||||
|
|
||||||
package device
|
package device
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
29
listener/tun/device/device_no_gvisor.go
Normal file
29
listener/tun/device/device_no_gvisor.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
//go:build no_gvisor
|
||||||
|
|
||||||
|
package device
|
||||||
|
|
||||||
|
// Device is the interface that implemented by network layer devices (e.g. tun),
|
||||||
|
// and easy to use as stack.LinkEndpoint.
|
||||||
|
type Device interface {
|
||||||
|
|
||||||
|
// Name returns the current name of the device.
|
||||||
|
Name() string
|
||||||
|
|
||||||
|
// Type returns the driver type of the device.
|
||||||
|
Type() string
|
||||||
|
|
||||||
|
// Read packets from tun device
|
||||||
|
Read(packet []byte) (int, error)
|
||||||
|
|
||||||
|
// Write packets to tun device
|
||||||
|
Write(packet []byte) (int, error)
|
||||||
|
|
||||||
|
// Close stops and closes the device.
|
||||||
|
Close() error
|
||||||
|
|
||||||
|
// UseEndpoint work for gVisor stack
|
||||||
|
UseEndpoint() error
|
||||||
|
|
||||||
|
// UseIOBased work for other ip stack
|
||||||
|
UseIOBased() error
|
||||||
|
}
|
@ -4,24 +4,13 @@ package fdbased
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/listener/tun/device"
|
"github.com/Dreamacro/clash/listener/tun/device"
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type FD struct {
|
|
||||||
stack.LinkEndpoint
|
|
||||||
|
|
||||||
fd int
|
|
||||||
mtu uint32
|
|
||||||
|
|
||||||
file *os.File
|
|
||||||
}
|
|
||||||
|
|
||||||
func Open(name string, mtu uint32) (device.Device, error) {
|
func Open(name string, mtu uint32) (device.Device, error) {
|
||||||
fd, err := strconv.Atoi(name)
|
fd, err := strconv.Atoi(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
17
listener/tun/device/fdbased/fd_unix_gvisor.go
Normal file
17
listener/tun/device/fdbased/fd_unix_gvisor.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
//go:build !no_gvisor
|
||||||
|
|
||||||
|
package fdbased
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FD struct {
|
||||||
|
stack.LinkEndpoint
|
||||||
|
|
||||||
|
fd int
|
||||||
|
mtu uint32
|
||||||
|
|
||||||
|
file *os.File
|
||||||
|
}
|
14
listener/tun/device/fdbased/fd_unix_no_gvisor.go
Normal file
14
listener/tun/device/fdbased/fd_unix_no_gvisor.go
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
//go:build no_gvisor
|
||||||
|
|
||||||
|
package fdbased
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FD struct {
|
||||||
|
fd int
|
||||||
|
mtu uint32
|
||||||
|
|
||||||
|
file *os.File
|
||||||
|
}
|
@ -7,7 +7,6 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/listener/tun/device"
|
"github.com/Dreamacro/clash/listener/tun/device"
|
||||||
"github.com/Dreamacro/clash/listener/tun/device/iobased"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func open(fd int, mtu uint32) (device.Device, error) {
|
func open(fd int, mtu uint32) (device.Device, error) {
|
||||||
@ -17,12 +16,7 @@ func open(fd int, mtu uint32) (device.Device, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (f *FD) useEndpoint() error {
|
func (f *FD) useEndpoint() error {
|
||||||
ep, err := iobased.New(os.NewFile(uintptr(f.fd), f.Name()), f.mtu, 0)
|
return newEp(f)
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("create endpoint: %w", err)
|
|
||||||
}
|
|
||||||
f.LinkEndpoint = ep
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FD) useIOBased() error {
|
func (f *FD) useIOBased() error {
|
||||||
|
19
listener/tun/device/fdbased/open_others_gvisor.go
Normal file
19
listener/tun/device/fdbased/open_others_gvisor.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
//go:build !no_gvisor && !linux && !windows
|
||||||
|
|
||||||
|
package fdbased
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/listener/tun/device/iobased"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newEp(f *FD) error {
|
||||||
|
ep, err := iobased.New(os.NewFile(uintptr(f.fd), f.Name()), f.mtu, 0)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create endpoint: %w", err)
|
||||||
|
}
|
||||||
|
f.LinkEndpoint = ep
|
||||||
|
return nil
|
||||||
|
}
|
11
listener/tun/device/fdbased/open_others_no_gvisor.go
Normal file
11
listener/tun/device/fdbased/open_others_no_gvisor.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
//go:build no_gvisor && !linux && !windows
|
||||||
|
|
||||||
|
package fdbased
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newEp(f *FD) error {
|
||||||
|
return fmt.Errorf("unsupported gvisor on the build")
|
||||||
|
}
|
@ -1,3 +1,5 @@
|
|||||||
|
//go:build !no_gvisor
|
||||||
|
|
||||||
// Package iobased provides the implementation of io.ReadWriter
|
// Package iobased provides the implementation of io.ReadWriter
|
||||||
// based data-link layer endpoints.
|
// based data-link layer endpoints.
|
||||||
package iobased
|
package iobased
|
||||||
|
1
listener/tun/device/iobased/iobased.go
Normal file
1
listener/tun/device/iobased/iobased.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package iobased
|
233
listener/tun/device/tun/driver/dll_windows.go
Normal file
233
listener/tun/device/tun/driver/dll_windows.go
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/log"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
"golang.zx2c4.com/wireguard/windows/driver/memmod"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:linkname modwintun golang.zx2c4.com/wintun.modwintun
|
||||||
|
|
||||||
|
//go:linkname procWintunCreateAdapter golang.zx2c4.com/wintun.procWintunCreateAdapter
|
||||||
|
|
||||||
|
//go:linkname procWintunOpenAdapter golang.zx2c4.com/wintun.procWintunOpenAdapter
|
||||||
|
|
||||||
|
//go:linkname procWintunCloseAdapter golang.zx2c4.com/wintun.procWintunCloseAdapter
|
||||||
|
|
||||||
|
//go:linkname procWintunDeleteDriver golang.zx2c4.com/wintun.procWintunDeleteDriver
|
||||||
|
|
||||||
|
//go:linkname procWintunGetAdapterLUID golang.zx2c4.com/wintun.procWintunGetAdapterLUID
|
||||||
|
|
||||||
|
//go:linkname procWintunGetRunningDriverVersion golang.zx2c4.com/wintun.procWintunGetRunningDriverVersion
|
||||||
|
|
||||||
|
//go:linkname procWintunAllocateSendPacket golang.zx2c4.com/wintun.procWintunAllocateSendPacket
|
||||||
|
|
||||||
|
//go:linkname procWintunEndSession golang.zx2c4.com/wintun.procWintunEndSession
|
||||||
|
|
||||||
|
//go:linkname procWintunGetReadWaitEvent golang.zx2c4.com/wintun.procWintunGetReadWaitEvent
|
||||||
|
|
||||||
|
//go:linkname procWintunReceivePacket golang.zx2c4.com/wintun.procWintunReceivePacket
|
||||||
|
|
||||||
|
//go:linkname procWintunReleaseReceivePacket golang.zx2c4.com/wintun.procWintunReleaseReceivePacket
|
||||||
|
|
||||||
|
//go:linkname procWintunSendPacket golang.zx2c4.com/wintun.procWintunSendPacket
|
||||||
|
|
||||||
|
//go:linkname procWintunStartSession golang.zx2c4.com/wintun.procWintunStartSession
|
||||||
|
|
||||||
|
var (
|
||||||
|
modwintun *lazyDLL
|
||||||
|
procWintunCreateAdapter *lazyProc
|
||||||
|
procWintunOpenAdapter *lazyProc
|
||||||
|
procWintunCloseAdapter *lazyProc
|
||||||
|
procWintunDeleteDriver *lazyProc
|
||||||
|
procWintunGetAdapterLUID *lazyProc
|
||||||
|
procWintunGetRunningDriverVersion *lazyProc
|
||||||
|
procWintunAllocateSendPacket *lazyProc
|
||||||
|
procWintunEndSession *lazyProc
|
||||||
|
procWintunGetReadWaitEvent *lazyProc
|
||||||
|
procWintunReceivePacket *lazyProc
|
||||||
|
procWintunReleaseReceivePacket *lazyProc
|
||||||
|
procWintunSendPacket *lazyProc
|
||||||
|
procWintunStartSession *lazyProc
|
||||||
|
)
|
||||||
|
|
||||||
|
type loggerLevel int
|
||||||
|
|
||||||
|
const (
|
||||||
|
logInfo loggerLevel = iota
|
||||||
|
logWarn
|
||||||
|
logErr
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
modwintun = newLazyDLL("wintun.dll", setupLogger)
|
||||||
|
procWintunCreateAdapter = modwintun.NewProc("WintunCreateAdapter")
|
||||||
|
procWintunOpenAdapter = modwintun.NewProc("WintunOpenAdapter")
|
||||||
|
procWintunCloseAdapter = modwintun.NewProc("WintunCloseAdapter")
|
||||||
|
procWintunDeleteDriver = modwintun.NewProc("WintunDeleteDriver")
|
||||||
|
procWintunGetAdapterLUID = modwintun.NewProc("WintunGetAdapterLUID")
|
||||||
|
procWintunGetRunningDriverVersion = modwintun.NewProc("WintunGetRunningDriverVersion")
|
||||||
|
procWintunAllocateSendPacket = modwintun.NewProc("WintunAllocateSendPacket")
|
||||||
|
procWintunEndSession = modwintun.NewProc("WintunEndSession")
|
||||||
|
procWintunGetReadWaitEvent = modwintun.NewProc("WintunGetReadWaitEvent")
|
||||||
|
procWintunReceivePacket = modwintun.NewProc("WintunReceivePacket")
|
||||||
|
procWintunReleaseReceivePacket = modwintun.NewProc("WintunReleaseReceivePacket")
|
||||||
|
procWintunSendPacket = modwintun.NewProc("WintunSendPacket")
|
||||||
|
procWintunStartSession = modwintun.NewProc("WintunStartSession")
|
||||||
|
}
|
||||||
|
|
||||||
|
func InitWintun() (err error) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
err = fmt.Errorf("init wintun.dll error: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err = modwintun.Load(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
procWintunCreateAdapter.Addr()
|
||||||
|
procWintunOpenAdapter.Addr()
|
||||||
|
procWintunCloseAdapter.Addr()
|
||||||
|
procWintunDeleteDriver.Addr()
|
||||||
|
procWintunGetAdapterLUID.Addr()
|
||||||
|
procWintunGetRunningDriverVersion.Addr()
|
||||||
|
procWintunAllocateSendPacket.Addr()
|
||||||
|
procWintunEndSession.Addr()
|
||||||
|
procWintunGetReadWaitEvent.Addr()
|
||||||
|
procWintunReceivePacket.Addr()
|
||||||
|
procWintunReleaseReceivePacket.Addr()
|
||||||
|
procWintunSendPacket.Addr()
|
||||||
|
procWintunStartSession.Addr()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func newLazyDLL(name string, onLoad func(d *lazyDLL)) *lazyDLL {
|
||||||
|
return &lazyDLL{Name: name, onLoad: onLoad}
|
||||||
|
}
|
||||||
|
|
||||||
|
func logMessage(level loggerLevel, _ uint64, msg *uint16) int {
|
||||||
|
switch level {
|
||||||
|
case logInfo:
|
||||||
|
log.Infoln("[TUN] %s", windows.UTF16PtrToString(msg))
|
||||||
|
case logWarn:
|
||||||
|
log.Warnln("[TUN] %s", windows.UTF16PtrToString(msg))
|
||||||
|
case logErr:
|
||||||
|
log.Errorln("[TUN] %s", windows.UTF16PtrToString(msg))
|
||||||
|
default:
|
||||||
|
log.Debugln("[TUN] %s", windows.UTF16PtrToString(msg))
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupLogger(dll *lazyDLL) {
|
||||||
|
var callback uintptr
|
||||||
|
if runtime.GOARCH == "386" {
|
||||||
|
callback = windows.NewCallback(func(level loggerLevel, _, _ uint32, msg *uint16) int {
|
||||||
|
return logMessage(level, 0, msg)
|
||||||
|
})
|
||||||
|
} else if runtime.GOARCH == "arm" {
|
||||||
|
callback = windows.NewCallback(func(level loggerLevel, _, _, _ uint32, msg *uint16) int {
|
||||||
|
return logMessage(level, 0, msg)
|
||||||
|
})
|
||||||
|
} else if runtime.GOARCH == "amd64" || runtime.GOARCH == "arm64" {
|
||||||
|
callback = windows.NewCallback(logMessage)
|
||||||
|
}
|
||||||
|
_, _, _ = syscall.SyscallN(dll.NewProc("WintunSetLogger").Addr(), callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *lazyDLL) NewProc(name string) *lazyProc {
|
||||||
|
return &lazyProc{dll: d, Name: name}
|
||||||
|
}
|
||||||
|
|
||||||
|
type lazyProc struct {
|
||||||
|
Name string
|
||||||
|
mu sync.Mutex
|
||||||
|
dll *lazyDLL
|
||||||
|
addr uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *lazyProc) Find() error {
|
||||||
|
if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&p.addr))) != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
p.mu.Lock()
|
||||||
|
defer p.mu.Unlock()
|
||||||
|
if p.addr != 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err := p.dll.Load()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error loading DLL: %s, MODULE: %s, error: %w", p.dll.Name, p.Name, err)
|
||||||
|
}
|
||||||
|
addr, err := p.nameToAddr()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error getting %s address: %w", p.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&p.addr)), unsafe.Pointer(addr))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *lazyProc) Addr() uintptr {
|
||||||
|
err := p.Find()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return p.addr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *lazyProc) Load() error {
|
||||||
|
return p.dll.Load()
|
||||||
|
}
|
||||||
|
|
||||||
|
type lazyDLL struct {
|
||||||
|
Name string
|
||||||
|
Base windows.Handle
|
||||||
|
mu sync.Mutex
|
||||||
|
module *memmod.Module
|
||||||
|
onLoad func(d *lazyDLL)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *lazyDLL) Load() error {
|
||||||
|
if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.module))) != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
d.mu.Lock()
|
||||||
|
defer d.mu.Unlock()
|
||||||
|
if d.module != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
module, err := memmod.LoadLibrary(dllContent)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to load library: %w", err)
|
||||||
|
}
|
||||||
|
d.Base = windows.Handle(module.BaseAddr())
|
||||||
|
|
||||||
|
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.module)), unsafe.Pointer(module))
|
||||||
|
if d.onLoad != nil {
|
||||||
|
d.onLoad(d)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *lazyProc) nameToAddr() (uintptr, error) {
|
||||||
|
return p.dll.module.ProcAddressByName(p.Name)
|
||||||
|
}
|
13
listener/tun/device/tun/driver/dll_windows_386.go
Normal file
13
listener/tun/device/tun/driver/dll_windows_386.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "embed"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed x86/wintun.dll
|
||||||
|
var dllContent []byte
|
13
listener/tun/device/tun/driver/dll_windows_amd64.go
Normal file
13
listener/tun/device/tun/driver/dll_windows_amd64.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "embed"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed amd64/wintun.dll
|
||||||
|
var dllContent []byte
|
13
listener/tun/device/tun/driver/dll_windows_arm.go
Normal file
13
listener/tun/device/tun/driver/dll_windows_arm.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "embed"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed arm/wintun.dll
|
||||||
|
var dllContent []byte
|
13
listener/tun/device/tun/driver/dll_windows_arm64.go
Normal file
13
listener/tun/device/tun/driver/dll_windows_arm64.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "embed"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed arm64/wintun.dll
|
||||||
|
var dllContent []byte
|
5
listener/tun/device/tun/wintun/package_info.go → listener/tun/device/tun/driver/package_info.go
Executable file → Normal file
5
listener/tun/device/tun/wintun/package_info.go → listener/tun/device/tun/driver/package_info.go
Executable file → Normal file
@ -1,11 +1,10 @@
|
|||||||
//go:build windows
|
//go:build windows
|
||||||
// +build windows
|
|
||||||
|
|
||||||
// Modified from: https://git.zx2c4.com/wireguard-go/tree/tun/wintun
|
// https://git.zx2c4.com/wireguard-go/tree/tun/wintun
|
||||||
|
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2019-2021 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2019-2021 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package wintun
|
package driver
|
@ -3,28 +3,15 @@
|
|||||||
package tun
|
package tun
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/listener/tun/device"
|
"github.com/Dreamacro/clash/listener/tun/device"
|
||||||
"github.com/Dreamacro/clash/listener/tun/device/iobased"
|
|
||||||
|
|
||||||
"golang.zx2c4.com/wireguard/tun"
|
"golang.zx2c4.com/wireguard/tun"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TUN struct {
|
|
||||||
*iobased.Endpoint
|
|
||||||
|
|
||||||
nt *tun.NativeTun
|
|
||||||
mtu uint32
|
|
||||||
name string
|
|
||||||
offset int
|
|
||||||
|
|
||||||
cache []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func Open(name string, mtu uint32) (_ device.Device, err error) {
|
func Open(name string, mtu uint32) (_ device.Device, err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
@ -43,11 +30,11 @@ func Open(name string, mtu uint32) (_ device.Device, err error) {
|
|||||||
forcedMTU = int(t.mtu)
|
forcedMTU = int(t.mtu)
|
||||||
}
|
}
|
||||||
|
|
||||||
nt, err := tun.CreateTUN(t.name, forcedMTU) // forcedMTU do not work on wintun, need to be setting by other way
|
nt, err := newDevice(t.name, forcedMTU) // forcedMTU do not work on wintun, need to be setting by other way
|
||||||
|
|
||||||
// retry if abnormal exit on Windows at last time
|
// retry if abnormal exit at last time on Windows
|
||||||
if err != nil && runtime.GOOS == "windows" && errors.Is(err, os.ErrExist) {
|
if err != nil && runtime.GOOS == "windows" && os.IsExist(err) {
|
||||||
nt, err = tun.CreateTUN(t.name, forcedMTU)
|
nt, err = newDevice(t.name, forcedMTU)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -92,11 +79,7 @@ func (t *TUN) Write(packet []byte) (int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *TUN) Close() error {
|
func (t *TUN) Close() error {
|
||||||
defer func(ep *iobased.Endpoint) {
|
defer closeIO(t)
|
||||||
if ep != nil {
|
|
||||||
ep.Close()
|
|
||||||
}
|
|
||||||
}(t.Endpoint)
|
|
||||||
return t.nt.Close()
|
return t.nt.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,12 +89,7 @@ func (t *TUN) Name() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *TUN) UseEndpoint() error {
|
func (t *TUN) UseEndpoint() error {
|
||||||
ep, err := iobased.New(t, t.mtu, t.offset)
|
return newEq(t)
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("create endpoint: %w", err)
|
|
||||||
}
|
|
||||||
t.Endpoint = ep
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TUN) UseIOBased() error {
|
func (t *TUN) UseIOBased() error {
|
||||||
|
34
listener/tun/device/tun/tun_wireguard_gvisor.go
Normal file
34
listener/tun/device/tun/tun_wireguard_gvisor.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
//go:build !linux && !no_gvisor
|
||||||
|
|
||||||
|
package tun
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/Dreamacro/clash/listener/tun/device/iobased"
|
||||||
|
"golang.zx2c4.com/wireguard/tun"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TUN struct {
|
||||||
|
*iobased.Endpoint
|
||||||
|
nt *tun.NativeTun
|
||||||
|
mtu uint32
|
||||||
|
name string
|
||||||
|
offset int
|
||||||
|
|
||||||
|
cache []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func closeIO(t *TUN) {
|
||||||
|
if t.Endpoint != nil {
|
||||||
|
t.Endpoint.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newEq(t *TUN) error {
|
||||||
|
ep, err := iobased.New(t, t.mtu, t.offset)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create endpoint: %w", err)
|
||||||
|
}
|
||||||
|
t.Endpoint = ep
|
||||||
|
return nil
|
||||||
|
}
|
24
listener/tun/device/tun/tun_wireguard_no_gvisor.go
Normal file
24
listener/tun/device/tun/tun_wireguard_no_gvisor.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
//go:build !linux && no_gvisor
|
||||||
|
|
||||||
|
package tun
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.zx2c4.com/wireguard/tun"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TUN struct {
|
||||||
|
nt *tun.NativeTun
|
||||||
|
mtu uint32
|
||||||
|
name string
|
||||||
|
offset int
|
||||||
|
|
||||||
|
cache []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func closeIO(t *TUN) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func newEq(t *TUN) error {
|
||||||
|
return nil
|
||||||
|
}
|
@ -2,7 +2,13 @@
|
|||||||
|
|
||||||
package tun
|
package tun
|
||||||
|
|
||||||
|
import "golang.zx2c4.com/wireguard/tun"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
offset = 4 /* 4 bytes TUN_PI */
|
offset = 4 /* 4 bytes TUN_PI */
|
||||||
defaultMTU = 1500
|
defaultMTU = 1500
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func newDevice(name string, mtu int) (tun.Device, error) {
|
||||||
|
return tun.CreateTUN(name, mtu)
|
||||||
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package tun
|
package tun
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/Dreamacro/clash/listener/tun/device/tun/driver"
|
||||||
|
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
"golang.zx2c4.com/wireguard/tun"
|
"golang.zx2c4.com/wireguard/tun"
|
||||||
)
|
)
|
||||||
@ -20,3 +22,11 @@ func init() {
|
|||||||
func (t *TUN) LUID() uint64 {
|
func (t *TUN) LUID() uint64 {
|
||||||
return t.nt.LUID()
|
return t.nt.LUID()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newDevice(name string, mtu int) (nt tun.Device, err error) {
|
||||||
|
if err = driver.InitWintun(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return tun.CreateTUN(name, mtu)
|
||||||
|
}
|
||||||
|
@ -1,94 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package wintun
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"golang.zx2c4.com/wintun/embed_dll"
|
|
||||||
"golang.zx2c4.com/wireguard/windows/driver/memmod"
|
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
func newLazyDLL(name string, onLoad func(d *lazyDLL)) *lazyDLL {
|
|
||||||
return &lazyDLL{Name: name, onLoad: onLoad}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *lazyDLL) NewProc(name string) *lazyProc {
|
|
||||||
return &lazyProc{dll: d, Name: name}
|
|
||||||
}
|
|
||||||
|
|
||||||
type lazyProc struct {
|
|
||||||
Name string
|
|
||||||
mu sync.Mutex
|
|
||||||
dll *lazyDLL
|
|
||||||
addr uintptr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *lazyProc) Find() error {
|
|
||||||
if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&p.addr))) != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
p.mu.Lock()
|
|
||||||
defer p.mu.Unlock()
|
|
||||||
if p.addr != 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
err := p.dll.Load()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error loading %v DLL: %w", p.dll.Name, err)
|
|
||||||
}
|
|
||||||
addr, err := p.nameToAddr()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error getting %v address: %w", p.Name, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&p.addr)), unsafe.Pointer(addr))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *lazyProc) Addr() uintptr {
|
|
||||||
err := p.Find()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return p.addr
|
|
||||||
}
|
|
||||||
|
|
||||||
type lazyDLL struct {
|
|
||||||
Name string
|
|
||||||
mu sync.Mutex
|
|
||||||
module *memmod.Module
|
|
||||||
onLoad func(d *lazyDLL)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *lazyDLL) Load() error {
|
|
||||||
if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.module))) != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
d.mu.Lock()
|
|
||||||
defer d.mu.Unlock()
|
|
||||||
if d.module != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
module, err := memmod.LoadLibrary(embed_dll.DDlContent)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to load library: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.module)), unsafe.Pointer(module))
|
|
||||||
if d.onLoad != nil {
|
|
||||||
d.onLoad(d)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *lazyProc) nameToAddr() (uintptr, error) {
|
|
||||||
return p.dll.module.ProcAddressByName(p.Name)
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
package embed_dll
|
|
||||||
|
|
||||||
// Copyright 2020 MeshStep Authors.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
import (
|
|
||||||
_ "embed"
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:embed x86/wintun.dll
|
|
||||||
var DDlContent []byte
|
|
@ -1,21 +0,0 @@
|
|||||||
package embed_dll
|
|
||||||
|
|
||||||
// Copyright 2020 MeshStep Authors.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
import (
|
|
||||||
_ "embed"
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:embed amd64/wintun.dll
|
|
||||||
var DDlContent []byte
|
|
@ -1,21 +0,0 @@
|
|||||||
package embed_dll
|
|
||||||
|
|
||||||
// Copyright 2020 MeshStep Authors.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
import (
|
|
||||||
_ "embed"
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:embed arm/wintun.dll
|
|
||||||
var DDlContent []byte
|
|
@ -1,21 +0,0 @@
|
|||||||
package embed_dll
|
|
||||||
|
|
||||||
// Copyright 2020 MeshStep Authors.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
import (
|
|
||||||
_ "embed"
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:embed arm64/wintun.dll
|
|
||||||
var DDlContent []byte
|
|
@ -1,8 +0,0 @@
|
|||||||
module golang.zx2c4.com/wintun
|
|
||||||
|
|
||||||
go 1.18
|
|
||||||
|
|
||||||
require (
|
|
||||||
golang.org/x/sys v0.0.0-20220318055525-2edf467146b5
|
|
||||||
golang.zx2c4.com/wireguard/windows v0.5.3
|
|
||||||
)
|
|
@ -1,17 +0,0 @@
|
|||||||
github.com/MetaCubeX/Clash.Meta v1.9.1 h1:jHZhVRBxFuaCRBN9vxB/FL5R16wY4kIgNqjszdXPeLs=
|
|
||||||
github.com/MetaCubeX/Clash.Meta v1.9.1/go.mod h1:/I4cSh+PcgmtS5SEnFp8RANL6aVRd3i9YOult+mKLhU=
|
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
|
||||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
|
||||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
|
||||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
|
||||||
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20220317061510-51cd9980dadf h1:Fm4IcnUL803i92qDlmB0obyHmosDrxZWxJL3gIeNqOw=
|
|
||||||
golang.org/x/sys v0.0.0-20220317061510-51cd9980dadf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE=
|
|
||||||
golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
|
@ -1,90 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package wintun
|
|
||||||
|
|
||||||
import (
|
|
||||||
"syscall"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"golang.org/x/sys/windows"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Session struct {
|
|
||||||
handle uintptr
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
PacketSizeMax = 0xffff // Maximum packet size
|
|
||||||
RingCapacityMin = 0x20000 // Minimum ring capacity (128 kiB)
|
|
||||||
RingCapacityMax = 0x4000000 // Maximum ring capacity (64 MiB)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Packet with data
|
|
||||||
type Packet struct {
|
|
||||||
Next *Packet // Pointer to next packet in queue
|
|
||||||
Size uint32 // Size of packet (max WINTUN_MAX_IP_PACKET_SIZE)
|
|
||||||
Data *[PacketSizeMax]byte // Pointer to layer 3 IPv4 or IPv6 packet
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
procWintunAllocateSendPacket = modwintun.NewProc("WintunAllocateSendPacket")
|
|
||||||
procWintunEndSession = modwintun.NewProc("WintunEndSession")
|
|
||||||
procWintunGetReadWaitEvent = modwintun.NewProc("WintunGetReadWaitEvent")
|
|
||||||
procWintunReceivePacket = modwintun.NewProc("WintunReceivePacket")
|
|
||||||
procWintunReleaseReceivePacket = modwintun.NewProc("WintunReleaseReceivePacket")
|
|
||||||
procWintunSendPacket = modwintun.NewProc("WintunSendPacket")
|
|
||||||
procWintunStartSession = modwintun.NewProc("WintunStartSession")
|
|
||||||
)
|
|
||||||
|
|
||||||
func (wintun *Adapter) StartSession(capacity uint32) (session Session, err error) {
|
|
||||||
r0, _, e1 := syscall.Syscall(procWintunStartSession.Addr(), 2, uintptr(wintun.handle), uintptr(capacity), 0)
|
|
||||||
if r0 == 0 {
|
|
||||||
err = e1
|
|
||||||
} else {
|
|
||||||
session = Session{r0}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (session Session) End() {
|
|
||||||
syscall.Syscall(procWintunEndSession.Addr(), 1, session.handle, 0, 0)
|
|
||||||
session.handle = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (session Session) ReadWaitEvent() (handle windows.Handle) {
|
|
||||||
r0, _, _ := syscall.Syscall(procWintunGetReadWaitEvent.Addr(), 1, session.handle, 0, 0)
|
|
||||||
handle = windows.Handle(r0)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (session Session) ReceivePacket() (packet []byte, err error) {
|
|
||||||
var packetSize uint32
|
|
||||||
r0, _, e1 := syscall.Syscall(procWintunReceivePacket.Addr(), 2, session.handle, uintptr(unsafe.Pointer(&packetSize)), 0)
|
|
||||||
if r0 == 0 {
|
|
||||||
err = e1
|
|
||||||
return
|
|
||||||
}
|
|
||||||
packet = unsafe.Slice((*byte)(unsafe.Pointer(r0)), packetSize)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (session Session) ReleaseReceivePacket(packet []byte) {
|
|
||||||
syscall.Syscall(procWintunReleaseReceivePacket.Addr(), 2, session.handle, uintptr(unsafe.Pointer(&packet[0])), 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (session Session) AllocateSendPacket(packetSize int) (packet []byte, err error) {
|
|
||||||
r0, _, e1 := syscall.Syscall(procWintunAllocateSendPacket.Addr(), 2, session.handle, uintptr(packetSize), 0)
|
|
||||||
if r0 == 0 {
|
|
||||||
err = e1
|
|
||||||
return
|
|
||||||
}
|
|
||||||
packet = unsafe.Slice((*byte)(unsafe.Pointer(r0)), packetSize)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (session Session) SendPacket(packet []byte) {
|
|
||||||
syscall.Syscall(procWintunSendPacket.Addr(), 2, session.handle, uintptr(unsafe.Pointer(&packet[0])), 0)
|
|
||||||
}
|
|
@ -1,152 +0,0 @@
|
|||||||
//go:build windows
|
|
||||||
|
|
||||||
/* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package wintun
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"runtime"
|
|
||||||
"syscall"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"golang.org/x/sys/windows"
|
|
||||||
)
|
|
||||||
|
|
||||||
type loggerLevel int
|
|
||||||
|
|
||||||
const (
|
|
||||||
logInfo loggerLevel = iota
|
|
||||||
logWarn
|
|
||||||
logErr
|
|
||||||
)
|
|
||||||
|
|
||||||
const AdapterNameMax = 128
|
|
||||||
|
|
||||||
type Adapter struct {
|
|
||||||
handle uintptr
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
modwintun = newLazyDLL("wintun.dll", setupLogger)
|
|
||||||
procWintunCreateAdapter = modwintun.NewProc("WintunCreateAdapter")
|
|
||||||
procWintunOpenAdapter = modwintun.NewProc("WintunOpenAdapter")
|
|
||||||
procWintunCloseAdapter = modwintun.NewProc("WintunCloseAdapter")
|
|
||||||
procWintunDeleteDriver = modwintun.NewProc("WintunDeleteDriver")
|
|
||||||
procWintunGetAdapterLUID = modwintun.NewProc("WintunGetAdapterLUID")
|
|
||||||
procWintunGetRunningDriverVersion = modwintun.NewProc("WintunGetRunningDriverVersion")
|
|
||||||
)
|
|
||||||
|
|
||||||
type TimestampedWriter interface {
|
|
||||||
WriteWithTimestamp(p []byte, ts int64) (n int, err error)
|
|
||||||
}
|
|
||||||
|
|
||||||
func logMessage(level loggerLevel, timestamp uint64, msg *uint16) int {
|
|
||||||
if tw, ok := log.Default().Writer().(TimestampedWriter); ok {
|
|
||||||
tw.WriteWithTimestamp([]byte(log.Default().Prefix()+windows.UTF16PtrToString(msg)), (int64(timestamp)-116444736000000000)*100)
|
|
||||||
} else {
|
|
||||||
log.Println(windows.UTF16PtrToString(msg))
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func setupLogger(dll *lazyDLL) {
|
|
||||||
var callback uintptr
|
|
||||||
if runtime.GOARCH == "386" {
|
|
||||||
callback = windows.NewCallback(func(level loggerLevel, timestampLow, timestampHigh uint32, msg *uint16) int {
|
|
||||||
return logMessage(level, uint64(timestampHigh)<<32|uint64(timestampLow), msg)
|
|
||||||
})
|
|
||||||
} else if runtime.GOARCH == "arm" {
|
|
||||||
callback = windows.NewCallback(func(level loggerLevel, _, timestampLow, timestampHigh uint32, msg *uint16) int {
|
|
||||||
return logMessage(level, uint64(timestampHigh)<<32|uint64(timestampLow), msg)
|
|
||||||
})
|
|
||||||
} else if runtime.GOARCH == "amd64" || runtime.GOARCH == "arm64" {
|
|
||||||
callback = windows.NewCallback(logMessage)
|
|
||||||
}
|
|
||||||
syscall.Syscall(dll.NewProc("WintunSetLogger").Addr(), 1, callback, 0, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func closeAdapter(wintun *Adapter) {
|
|
||||||
syscall.Syscall(procWintunCloseAdapter.Addr(), 1, wintun.handle, 0, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateAdapter creates a Wintun adapter. name is the cosmetic name of the adapter.
|
|
||||||
// tunnelType represents the type of adapter and should be "Wintun". requestedGUID is
|
|
||||||
// the GUID of the created network adapter, which then influences NLA generation
|
|
||||||
// deterministically. If it is set to nil, the GUID is chosen by the system at random,
|
|
||||||
// and hence a new NLA entry is created for each new adapter.
|
|
||||||
func CreateAdapter(name string, tunnelType string, requestedGUID *windows.GUID) (wintun *Adapter, err error) {
|
|
||||||
var name16 *uint16
|
|
||||||
name16, err = windows.UTF16PtrFromString(name)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var tunnelType16 *uint16
|
|
||||||
tunnelType16, err = windows.UTF16PtrFromString(tunnelType)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
r0, _, e1 := syscall.Syscall(procWintunCreateAdapter.Addr(), 3, uintptr(unsafe.Pointer(name16)), uintptr(unsafe.Pointer(tunnelType16)), uintptr(unsafe.Pointer(requestedGUID)))
|
|
||||||
if r0 == 0 {
|
|
||||||
err = e1
|
|
||||||
return
|
|
||||||
}
|
|
||||||
wintun = &Adapter{handle: r0}
|
|
||||||
runtime.SetFinalizer(wintun, closeAdapter)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// OpenAdapter opens an existing Wintun adapter by name.
|
|
||||||
func OpenAdapter(name string) (wintun *Adapter, err error) {
|
|
||||||
var name16 *uint16
|
|
||||||
name16, err = windows.UTF16PtrFromString(name)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
r0, _, e1 := syscall.Syscall(procWintunOpenAdapter.Addr(), 1, uintptr(unsafe.Pointer(name16)), 0, 0)
|
|
||||||
if r0 == 0 {
|
|
||||||
err = e1
|
|
||||||
return
|
|
||||||
}
|
|
||||||
wintun = &Adapter{handle: r0}
|
|
||||||
runtime.SetFinalizer(wintun, closeAdapter)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close closes a Wintun adapter.
|
|
||||||
func (wintun *Adapter) Close() (err error) {
|
|
||||||
runtime.SetFinalizer(wintun, nil)
|
|
||||||
r1, _, e1 := syscall.Syscall(procWintunCloseAdapter.Addr(), 1, wintun.handle, 0, 0)
|
|
||||||
if r1 == 0 {
|
|
||||||
err = e1
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uninstall removes the driver from the system if no drivers are currently in use.
|
|
||||||
func Uninstall() (err error) {
|
|
||||||
r1, _, e1 := syscall.Syscall(procWintunDeleteDriver.Addr(), 0, 0, 0, 0)
|
|
||||||
if r1 == 0 {
|
|
||||||
err = e1
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// RunningVersion returns the version of the loaded driver.
|
|
||||||
func RunningVersion() (version uint32, err error) {
|
|
||||||
r0, _, e1 := syscall.Syscall(procWintunGetRunningDriverVersion.Addr(), 0, 0, 0, 0)
|
|
||||||
version = uint32(r0)
|
|
||||||
if version == 0 {
|
|
||||||
err = e1
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// LUID returns the LUID of the adapter.
|
|
||||||
func (wintun *Adapter) LUID() (luid uint64) {
|
|
||||||
syscall.Syscall(procWintunGetAdapterLUID.Addr(), 2, uintptr(wintun.handle), uintptr(unsafe.Pointer(&luid)), 0)
|
|
||||||
return
|
|
||||||
}
|
|
90
listener/tun/ipstack/commons/auto_linux.go
Normal file
90
listener/tun/ipstack/commons/auto_linux.go
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
package commons
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Dreamacro/clash/component/dialer"
|
||||||
|
"github.com/Dreamacro/clash/component/iface"
|
||||||
|
"github.com/Dreamacro/clash/log"
|
||||||
|
"github.com/vishvananda/netlink"
|
||||||
|
"go.uber.org/atomic"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
monitorStarted = atomic.NewBool(false)
|
||||||
|
monitorStop = make(chan struct{}, 2)
|
||||||
|
)
|
||||||
|
|
||||||
|
func StartDefaultInterfaceChangeMonitor() {
|
||||||
|
go func() {
|
||||||
|
if monitorStarted.Load() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
monitorStarted.Store(true)
|
||||||
|
|
||||||
|
done := make(chan struct{})
|
||||||
|
ch := make(chan netlink.RouteUpdate, 2)
|
||||||
|
err := netlink.RouteSubscribe(ch, done)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnln("[TUN] auto detect interface fail: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Debugln("[TUN] start auto detect interface monitor")
|
||||||
|
defer func() {
|
||||||
|
close(done)
|
||||||
|
monitorStarted.Store(false)
|
||||||
|
log.Debugln("[TUN] stop auto detect interface monitor")
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-monitorStop:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-monitorStop:
|
||||||
|
return
|
||||||
|
case <-ch:
|
||||||
|
}
|
||||||
|
|
||||||
|
interfaceName, err := GetAutoDetectInterface()
|
||||||
|
if err != nil {
|
||||||
|
t := time.NewTicker(2 * time.Second)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case ch <- <-ch:
|
||||||
|
break
|
||||||
|
case <-t.C:
|
||||||
|
interfaceName, err = GetAutoDetectInterface()
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
t.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Debugln("[TUN] detect interface: %s", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
old := dialer.DefaultInterface.Load()
|
||||||
|
if interfaceName == old {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
dialer.DefaultInterface.Store(interfaceName)
|
||||||
|
iface.FlushCache()
|
||||||
|
|
||||||
|
log.Warnln("[TUN] default interface changed by monitor, %s => %s", old, interfaceName)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func StopDefaultInterfaceChangeMonitor() {
|
||||||
|
if monitorStarted.Load() {
|
||||||
|
monitorStop <- struct{}{}
|
||||||
|
}
|
||||||
|
}
|
67
listener/tun/ipstack/commons/auto_others.go
Normal file
67
listener/tun/ipstack/commons/auto_others.go
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
//go:build !linux
|
||||||
|
|
||||||
|
package commons
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Dreamacro/clash/component/dialer"
|
||||||
|
"github.com/Dreamacro/clash/component/iface"
|
||||||
|
"github.com/Dreamacro/clash/log"
|
||||||
|
"go.uber.org/atomic"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
monitorDuration = 3 * time.Second
|
||||||
|
monitorStarted = atomic.NewBool(false)
|
||||||
|
monitorStop = make(chan struct{}, 2)
|
||||||
|
)
|
||||||
|
|
||||||
|
func StartDefaultInterfaceChangeMonitor() {
|
||||||
|
go func() {
|
||||||
|
if monitorStarted.Load() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
monitorStarted.Store(true)
|
||||||
|
t := time.NewTicker(monitorDuration)
|
||||||
|
log.Debugln("[TUN] start auto detect interface monitor")
|
||||||
|
defer func() {
|
||||||
|
monitorStarted.Store(false)
|
||||||
|
t.Stop()
|
||||||
|
log.Debugln("[TUN] stop auto detect interface monitor")
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-monitorStop:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-t.C:
|
||||||
|
interfaceName, err := GetAutoDetectInterface()
|
||||||
|
if err != nil {
|
||||||
|
log.Warnln("[TUN] default interface monitor err: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
old := dialer.DefaultInterface.Load()
|
||||||
|
if interfaceName == old {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
dialer.DefaultInterface.Store(interfaceName)
|
||||||
|
iface.FlushCache()
|
||||||
|
|
||||||
|
log.Warnln("[TUN] default interface changed by monitor, %s => %s", old, interfaceName)
|
||||||
|
case <-monitorStop:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func StopDefaultInterfaceChangeMonitor() {
|
||||||
|
if monitorStarted.Load() {
|
||||||
|
monitorStop <- struct{}{}
|
||||||
|
}
|
||||||
|
}
|
@ -2,16 +2,13 @@ package commons
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/Dreamacro/clash/log"
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/component/dialer"
|
|
||||||
"github.com/Dreamacro/clash/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
defaultRoutes = []string{"1.0.0.0/8", "2.0.0.0/7", "4.0.0.0/6", "8.0.0.0/5", "16.0.0.0/4", "32.0.0.0/3", "64.0.0.0/2", "128.0.0.0/1"}
|
defaultRoutes = []string{"1.0.0.0/8", "2.0.0.0/7", "4.0.0.0/6", "8.0.0.0/5", "16.0.0.0/4", "32.0.0.0/3", "64.0.0.0/2", "128.0.0.0/1"}
|
||||||
defaultInterfaceMonitorDuration = 3 * time.Second
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func ipv4MaskString(bits int) string {
|
func ipv4MaskString(bits int) string {
|
||||||
@ -23,26 +20,27 @@ func ipv4MaskString(bits int) string {
|
|||||||
return fmt.Sprintf("%d.%d.%d.%d", m[0], m[1], m[2], m[3])
|
return fmt.Sprintf("%d.%d.%d.%d", m[0], m[1], m[2], m[3])
|
||||||
}
|
}
|
||||||
|
|
||||||
func DefaultInterfaceChangeMonitor() {
|
func WaitForTunClose(deviceName string) {
|
||||||
t := time.NewTicker(defaultInterfaceMonitorDuration)
|
t := time.NewTicker(600 * time.Millisecond)
|
||||||
defer t.Stop()
|
defer t.Stop()
|
||||||
|
log.Debugln("[TUN] waiting for device close")
|
||||||
for {
|
for {
|
||||||
<-t.C
|
<-t.C
|
||||||
|
interfaces, err := net.Interfaces()
|
||||||
interfaceName, err := GetAutoDetectInterface()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnln("[TUN] default interface monitor exited, cause: %v", err)
|
break
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
old := dialer.DefaultInterface.Load()
|
found := false
|
||||||
if interfaceName == old {
|
for i := len(interfaces) - 1; i > -1; i-- {
|
||||||
continue
|
if interfaces[i].Name == deviceName {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dialer.DefaultInterface.Store(interfaceName)
|
if !found {
|
||||||
|
break
|
||||||
log.Warnln("[TUN] default interface changed by monitor, %s => %s", old, interfaceName)
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,86 +2,139 @@ package commons
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/Dreamacro/clash/common/cmd"
|
|
||||||
"github.com/Dreamacro/clash/listener/tun/device"
|
"github.com/Dreamacro/clash/listener/tun/device"
|
||||||
"github.com/Dreamacro/clash/log"
|
"github.com/Dreamacro/clash/log"
|
||||||
|
"github.com/vishvananda/netlink"
|
||||||
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetAutoDetectInterface() (ifn string, err error) {
|
func GetAutoDetectInterface() (ifn string, err error) {
|
||||||
cmdRes, err := cmd.ExecCmd("ip route get 1.1.1.1 uid 4294967295")
|
routes, err := netlink.RouteGetWithOptions(
|
||||||
|
net.ParseIP("1.1.1.1"),
|
||||||
sps := strings.Split(cmdRes, " ")
|
&netlink.RouteGetOptions{
|
||||||
if len(sps) > 4 {
|
Uid: &netlink.UID{Uid: 4294967295},
|
||||||
ifn = sps[4]
|
})
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if ifn == "" {
|
for _, route := range routes {
|
||||||
err = fmt.Errorf("interface not found")
|
if lk, err := netlink.LinkByIndex(route.LinkIndex); err == nil {
|
||||||
|
return lk.Attrs().Name, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return
|
|
||||||
|
return "", fmt.Errorf("interface not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
func ConfigInterfaceAddress(dev device.Device, addr netip.Prefix, forceMTU int, autoRoute, autoDetectInterface bool) error {
|
func ConfigInterfaceAddress(dev device.Device, addr netip.Prefix, forceMTU int, autoRoute bool) error {
|
||||||
var (
|
var (
|
||||||
interfaceName = dev.Name()
|
interfaceName = dev.Name()
|
||||||
ip = addr.Masked().Addr().Next()
|
ip = addr.Masked().Addr().Next()
|
||||||
)
|
)
|
||||||
|
|
||||||
_, err := cmd.ExecCmd(fmt.Sprintf("ip addr add %s dev %s", ip.String(), interfaceName))
|
metaLink, err := netlink.LinkByName(interfaceName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = cmd.ExecCmd(fmt.Sprintf("ip link set %s up", interfaceName))
|
naddr, err := netlink.ParseAddr(addr.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = execRouterCmd("add", addr.Masked().String(), interfaceName, ip.String(), "main"); err != nil {
|
if err = netlink.AddrAdd(metaLink, naddr); err != nil && err.Error() != "file exists" {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = netlink.LinkSetUp(metaLink); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if autoRoute {
|
if autoRoute {
|
||||||
err = configInterfaceRouting(interfaceName, addr, autoDetectInterface)
|
err = configInterfaceRouting(metaLink.Attrs().Index, interfaceName, ip)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func configInterfaceRouting(interfaceName string, addr netip.Prefix, autoDetectInterface bool) error {
|
func configInterfaceRouting(index int, interfaceName string, ip netip.Addr) error {
|
||||||
linkIP := addr.Masked().Addr().Next()
|
|
||||||
const tableId = 1981801
|
const tableId = 1981801
|
||||||
|
var pref = 9000
|
||||||
|
|
||||||
for _, route := range defaultRoutes {
|
for _, route := range defaultRoutes {
|
||||||
if err := execRouterCmd("add", route, interfaceName, linkIP.String(), strconv.Itoa(tableId)); err != nil {
|
_, ipn, err := net.ParseCIDR(route)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := netlink.RouteAdd(&netlink.Route{
|
||||||
|
LinkIndex: index,
|
||||||
|
Scope: netlink.SCOPE_LINK,
|
||||||
|
Protocol: 2,
|
||||||
|
Src: ip.AsSlice(),
|
||||||
|
Dst: ipn,
|
||||||
|
Table: tableId,
|
||||||
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
execAddRuleCmd(fmt.Sprintf("lookup main pref 9000"))
|
|
||||||
execAddRuleCmd(fmt.Sprintf("from 0.0.0.0 iif lo uidrange 0-4294967294 lookup %d pref 9001", tableId))
|
|
||||||
execAddRuleCmd(fmt.Sprintf("from %s iif lo uidrange 0-4294967294 lookup %d pref 9002", linkIP, tableId))
|
|
||||||
execAddRuleCmd(fmt.Sprintf("from all iif %s lookup main suppress_prefixlength 0 pref 9003", interfaceName))
|
|
||||||
execAddRuleCmd(fmt.Sprintf("not from all iif lo lookup %d pref 9004", tableId))
|
|
||||||
|
|
||||||
if autoDetectInterface {
|
logIfErr := func(e error) {
|
||||||
go DefaultInterfaceChangeMonitor()
|
if e != nil {
|
||||||
|
log.Warnln("[TOUTE] config route rule: %s", e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var r *netlink.Rule
|
||||||
|
r = netlink.NewRule()
|
||||||
|
r.Table = 254
|
||||||
|
r.Priority = pref
|
||||||
|
logIfErr(netlink.RuleAdd(r))
|
||||||
|
pref += 10
|
||||||
|
|
||||||
|
r = netlink.NewRule()
|
||||||
|
_, nl, _ := net.ParseCIDR("0.0.0.0/32")
|
||||||
|
r.Table = tableId
|
||||||
|
r.Priority = pref
|
||||||
|
r.Src = nl
|
||||||
|
r.IifName = "lo"
|
||||||
|
r.UID = netlink.NewRuleUIDRange(0, 4294967294)
|
||||||
|
logIfErr(netlink.RuleAdd(r))
|
||||||
|
pref += 10
|
||||||
|
|
||||||
|
_, nl, _ = net.ParseCIDR(ip.String())
|
||||||
|
r.Priority = pref
|
||||||
|
r.Src = nl
|
||||||
|
logIfErr(netlink.RuleAdd(r))
|
||||||
|
pref += 10
|
||||||
|
|
||||||
|
r = netlink.NewRule()
|
||||||
|
r.Table = 254
|
||||||
|
r.Priority = pref
|
||||||
|
r.IifName = interfaceName
|
||||||
|
r.SuppressPrefixlen = 0
|
||||||
|
logIfErr(netlink.RuleAdd(r))
|
||||||
|
pref += 10
|
||||||
|
|
||||||
|
r = netlink.NewRule()
|
||||||
|
r.Table = tableId
|
||||||
|
r.Priority = pref
|
||||||
|
r.IifName = "lo"
|
||||||
|
r.SuppressPrefixlen = 0
|
||||||
|
r.Invert = true
|
||||||
|
logIfErr(netlink.RuleAdd(r))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func execAddRuleCmd(rule string) {
|
func CleanupRule() {
|
||||||
_, err := cmd.ExecCmd("ip rule add " + rule)
|
r := netlink.NewRule()
|
||||||
if err != nil {
|
for i := 0; i < 5; i++ {
|
||||||
log.Warnln("%s", err)
|
r.Priority = 9000 + i*10
|
||||||
|
err := netlink.RuleDel(r)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnln("[TOUTE] cleanup route rule: %s", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func execRouterCmd(action, route, interfaceName, linkIP, table string) error {
|
|
||||||
cmdStr := fmt.Sprintf("ip route %s %s dev %s proto kernel scope link src %s table %s", action, route, interfaceName, linkIP, table)
|
|
||||||
|
|
||||||
_, err := cmd.ExecCmd(cmdStr)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
@ -12,7 +12,7 @@ func GetAutoDetectInterface() (string, error) {
|
|||||||
return cmd.ExecCmd("bash -c route -n get default | grep 'interface:' | awk -F ' ' 'NR==1{print $2}' | xargs echo -n")
|
return cmd.ExecCmd("bash -c route -n get default | grep 'interface:' | awk -F ' ' 'NR==1{print $2}' | xargs echo -n")
|
||||||
}
|
}
|
||||||
|
|
||||||
func ConfigInterfaceAddress(dev device.Device, addr netip.Prefix, forceMTU int, autoRoute, autoDetectInterface bool) error {
|
func ConfigInterfaceAddress(dev device.Device, addr netip.Prefix, forceMTU int, autoRoute bool) error {
|
||||||
if !addr.Addr().Is4() {
|
if !addr.Addr().Is4() {
|
||||||
return fmt.Errorf("supported ipv4 only")
|
return fmt.Errorf("supported ipv4 only")
|
||||||
}
|
}
|
||||||
@ -37,12 +37,12 @@ func ConfigInterfaceAddress(dev device.Device, addr netip.Prefix, forceMTU int,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if autoRoute {
|
if autoRoute {
|
||||||
err = configInterfaceRouting(interfaceName, addr, autoDetectInterface)
|
err = configInterfaceRouting(interfaceName, addr)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func configInterfaceRouting(interfaceName string, addr netip.Prefix, autoDetectInterface bool) error {
|
func configInterfaceRouting(interfaceName string, addr netip.Prefix) error {
|
||||||
var (
|
var (
|
||||||
routes = append(defaultRoutes, addr.String())
|
routes = append(defaultRoutes, addr.String())
|
||||||
gateway = addr.Masked().Addr().Next()
|
gateway = addr.Masked().Addr().Next()
|
||||||
@ -54,10 +54,6 @@ func configInterfaceRouting(interfaceName string, addr netip.Prefix, autoDetectI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if autoDetectInterface {
|
|
||||||
go DefaultInterfaceChangeMonitor()
|
|
||||||
}
|
|
||||||
|
|
||||||
return execRouterCmd("add", "-inet6", "2000::/3", interfaceName)
|
return execRouterCmd("add", "-inet6", "2000::/3", interfaceName)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,3 +61,5 @@ func execRouterCmd(action, inet, route string, interfaceName string) error {
|
|||||||
_, err := cmd.ExecCmd(fmt.Sprintf("route %s %s %s -interface %s", action, inet, route, interfaceName))
|
_, err := cmd.ExecCmd(fmt.Sprintf("route %s %s %s -interface %s", action, inet, route, interfaceName))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CleanupRule() {}
|
||||||
|
@ -4,58 +4,86 @@ package commons
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/Dreamacro/clash/common/cmd"
|
|
||||||
"github.com/Dreamacro/clash/listener/tun/device"
|
"github.com/Dreamacro/clash/listener/tun/device"
|
||||||
|
"github.com/vishvananda/netlink"
|
||||||
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetAutoDetectInterface() (string, error) {
|
func GetAutoDetectInterface() (string, error) {
|
||||||
return cmd.ExecCmd("bash -c ip route show | grep 'default via' | awk -F ' ' 'NR==1{print $5}' | xargs echo -n")
|
routes, err := netlink.RouteList(nil, netlink.FAMILY_V4)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, route := range routes {
|
||||||
|
if route.Dst == nil {
|
||||||
|
lk, err := netlink.LinkByIndex(route.LinkIndex)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if lk.Type() == "tuntap" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
return lk.Attrs().Name, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", fmt.Errorf("interface not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
func ConfigInterfaceAddress(dev device.Device, addr netip.Prefix, forceMTU int, autoRoute, autoDetectInterface bool) error {
|
func ConfigInterfaceAddress(dev device.Device, addr netip.Prefix, forceMTU int, autoRoute bool) error {
|
||||||
var (
|
var (
|
||||||
interfaceName = dev.Name()
|
interfaceName = dev.Name()
|
||||||
ip = addr.Masked().Addr().Next()
|
ip = addr.Masked().Addr().Next()
|
||||||
)
|
)
|
||||||
|
|
||||||
if _, err := cmd.ExecCmd(fmt.Sprintf("ip addr add %s dev %s", ip.String(), interfaceName)); err != nil {
|
metaLink, err := netlink.LinkByName(interfaceName)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := cmd.ExecCmd(fmt.Sprintf("ip link set %s up", interfaceName)); err != nil {
|
nlAddr, err := netlink.ParseAddr(addr.String())
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := execRouterCmd("add", addr.Masked().String(), interfaceName, ip.String(), "main"); err != nil {
|
if err = netlink.AddrAdd(metaLink, nlAddr); err != nil && err.Error() != "file exists" {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = netlink.LinkSetUp(metaLink); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if autoRoute {
|
if autoRoute {
|
||||||
_ = configInterfaceRouting(interfaceName, addr, autoDetectInterface)
|
_ = configInterfaceRouting(metaLink.Attrs().Index, ip)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func configInterfaceRouting(interfaceName string, addr netip.Prefix, autoDetectInterface bool) error {
|
func configInterfaceRouting(index int, ip netip.Addr) error {
|
||||||
linkIP := addr.Masked().Addr().Next()
|
|
||||||
|
|
||||||
for _, route := range defaultRoutes {
|
for _, route := range defaultRoutes {
|
||||||
if err := execRouterCmd("add", route, interfaceName, linkIP.String(), "main"); err != nil {
|
_, ipn, err := net.ParseCIDR(route)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := netlink.RouteAdd(&netlink.Route{
|
||||||
|
LinkIndex: index,
|
||||||
|
Scope: netlink.SCOPE_LINK,
|
||||||
|
Protocol: 2,
|
||||||
|
Src: ip.AsSlice(),
|
||||||
|
Dst: ipn,
|
||||||
|
Table: 254,
|
||||||
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if autoDetectInterface {
|
|
||||||
go DefaultInterfaceChangeMonitor()
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func execRouterCmd(action, route, interfaceName, linkIP, table string) error {
|
func CleanupRule() {}
|
||||||
cmdStr := fmt.Sprintf("ip route %s %s dev %s proto kernel scope link src %s table %s", action, route, interfaceName, linkIP, table)
|
|
||||||
|
|
||||||
_, err := cmd.ExecCmd(cmdStr)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
@ -14,6 +14,8 @@ func GetAutoDetectInterface() (string, error) {
|
|||||||
return "", fmt.Errorf("can not auto detect interface-name on this OS: %s, you must be detecting interface-name by manual", runtime.GOOS)
|
return "", fmt.Errorf("can not auto detect interface-name on this OS: %s, you must be detecting interface-name by manual", runtime.GOOS)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ConfigInterfaceAddress(device.Device, netip.Prefix, int, bool, bool) error {
|
func ConfigInterfaceAddress(device.Device, netip.Prefix, int, bool) error {
|
||||||
return fmt.Errorf("unsupported on this OS: %s", runtime.GOOS)
|
return fmt.Errorf("unsupported on this OS: %s", runtime.GOOS)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CleanupRule() {}
|
||||||
|
@ -27,7 +27,7 @@ func GetAutoDetectInterface() (string, error) {
|
|||||||
return getAutoDetectInterfaceByFamily(winipcfg.AddressFamily(windows.AF_INET6))
|
return getAutoDetectInterfaceByFamily(winipcfg.AddressFamily(windows.AF_INET6))
|
||||||
}
|
}
|
||||||
|
|
||||||
func ConfigInterfaceAddress(dev device.Device, addr netip.Prefix, forceMTU int, autoRoute, autoDetectInterface bool) error {
|
func ConfigInterfaceAddress(dev device.Device, addr netip.Prefix, forceMTU int, autoRoute bool) error {
|
||||||
retryOnFailure := services.StartedAtBoot()
|
retryOnFailure := services.StartedAtBoot()
|
||||||
tryTimes := 0
|
tryTimes := 0
|
||||||
var err error
|
var err error
|
||||||
@ -205,10 +205,6 @@ startOver:
|
|||||||
|
|
||||||
wintunInterfaceName = dev.Name()
|
wintunInterfaceName = dev.Name()
|
||||||
|
|
||||||
if autoDetectInterface {
|
|
||||||
go DefaultInterfaceChangeMonitor()
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,3 +269,5 @@ func getAutoDetectInterfaceByFamily(family winipcfg.AddressFamily) (string, erro
|
|||||||
|
|
||||||
return "", errors.New("ethernet interface not found")
|
return "", errors.New("ethernet interface not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CleanupRule() {}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
//go:build !no_gvisor
|
||||||
|
|
||||||
package adapter
|
package adapter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user