Compare commits

..

63 Commits

Author SHA1 Message Date
190c930294 Merge branch 'js-core' into release 2022-06-05 21:10:57 +08:00
d578ca788c feat: 添加resolve方法解析dns,关闭script规则需要解析IP 2022-06-05 21:06:26 +08:00
9602d42d7d feat: 修改元数据 2022-06-05 17:54:51 +08:00
55c7c4edb3 feat: 尝试实现脚本rule 2022-06-05 17:28:19 +08:00
20cb4d0643 feat: add tag(no_script) 2022-06-04 23:54:06 +08:00
91b4176557 feat: init js-core 2022-06-04 23:51:13 +08:00
f03f33840e Merge branch 'dev' into release 2022-06-04 19:28:32 +08:00
8ff7e180a4 fix: 当初始化失败时,定时更新失效 2022-06-04 19:15:30 +08:00
3827e00b54 refactor: 抽离http请求方法 2022-06-04 19:14:39 +08:00
c52e689d0d fix: classical rule-set 更新未清理 2022-06-04 19:12:50 +08:00
8e959bd245 chore: 当无tag时不输出无效日志 2022-06-03 21:00:45 +08:00
298ca42369 chore: 启动参数v,查看版本同时打印使用的tags 2022-06-03 20:23:53 +08:00
ed9b9ce3c5 refactor: 添加no_gvisor 编译tag, 剔除gvisor stack支持, 方便在arm设备上debug 2022-06-03 20:07:30 +08:00
1298d2f8b6 chore: 添加tag no_doq 编译不含doq版本, 仅减少1.5MB(macOS-arm64) 2022-06-03 18:12:06 +08:00
6e84f685ce chore: 更新geox时通过内存存储 2022-06-03 16:50:49 +08:00
1ad87cfec9 chore: 选择fallback时,当节点不可用时触发urltest 2022-06-03 13:32:11 +08:00
582c0763ba Merge branch 'dev' into release 2022-06-02 21:01:14 +08:00
9e9f459c0e refactor: 优化proxy server nameserver, 当节点专用dns全部查询失败会回落到正常逻辑 2022-06-02 20:58:25 +08:00
fa3e0c726e chore: 调整解析逻辑 2022-06-02 17:03:08 +08:00
3b038310ab fix: 类型转换错误导致规则解析错误 2022-06-02 15:43:27 +08:00
6709936a8f refactor: 归类规则解析代码 2022-06-02 13:42:18 +08:00
04e5d02ab9 feat: IP-SUFFIX
eg. IP-SUFFIX,0.0.0.124/6,匹配ip二进制后四位(IP-CIDR的倒序),支持ipv6
2022-06-02 12:53:19 +08:00
1af39cb228 fix: OpenClash 回环 2022-06-01 12:32:45 +08:00
c95735f083 chore: 调整内置winTun.dll部分 2022-06-01 12:01:08 +08:00
fa2e6be05d fix: TUN file exists 2022-05-31 10:34:13 +08:00
4092a7c84b feat: proxies group URLTest api 2022-05-30 22:07:09 +08:00
58d299c737 chore: 调整geosite初始化位置 2022-05-30 21:26:41 +08:00
11ddac2b5f refactor: 逻辑规则显示效果 2022-05-30 13:58:37 +08:00
1f95c74f1e chore: 识别线程数 2022-05-29 21:04:47 +08:00
c9616f70b7 chore: 添加build 2022-05-29 20:59:21 +08:00
a197fbd4b5 Merge branch 'Alpha' into dev 2022-05-29 20:43:39 +08:00
708b8beadf fix: compile 2022-05-29 20:02:30 +08:00
0e1601e5b6 fix: 调整not规则判断子规则数量,逻辑规则返回payload采用解析后结果 2022-05-29 19:54:11 +08:00
c7355510a2 chore: 调整uid系统判断位置 2022-05-29 18:55:08 +08:00
1faa172944 chore: 调整uid系统判断位置 2022-05-29 18:12:43 +08:00
7a8c98cd90 refactor: 使用 netlink 获取默认网卡 2022-05-29 15:35:08 +08:00
13e907bbd0 refactor: 使用 netlink 配置 ip rule 2022-05-29 15:03:27 +08:00
39e7832676 fix: route on android 2022-05-28 23:29:22 +08:00
9b999e72ce fix: npe 2022-05-28 23:29:03 +08:00
e1a61503e4 Merge branch 'netlink' into Alpha 2022-05-28 21:59:04 +08:00
9272d02149 refactor: 合并部分android代码入linux && ip 使用netlink配置路由 2022-05-28 21:58:29 +08:00
067c02aba1 fix: 调整获取远程目的的位置 2022-05-28 20:01:27 +08:00
d0268bb9a2 chore: 降低并发查询时IPv6等待 2022-05-28 09:58:45 +08:00
fb4872ff7f fix: 关闭并发时双栈使用错误 2022-05-27 20:43:39 +08:00
2044458df9 fix: npe 2022-05-27 20:33:27 +08:00
d6df026550 chore: 更换GeoData下载地址
Signed-off-by: Meta <maze.y2b@gmail.com>
2022-05-27 18:32:23 +08:00
7858ca6cc5 fix: geox url setting 2022-05-27 12:32:59 +08:00
ac36473d13 refactor: 获取远程目的从tunnel中剔除,移至tracker 2022-05-27 09:00:48 +08:00
72fb153fe0 refactor: 优化UDP远程目标获取 2022-05-26 23:41:09 +08:00
527a602eba fix: 更新错误时未停止后续流程,日志修改 2022-05-26 23:13:36 +08:00
a71fd3b4df fix: 启动时检测provider文件是否过期,强制更新 2022-05-26 21:05:00 +08:00
2ebc0383b5 feat: RESTful API support set tcp-concurrent 2022-05-26 19:49:12 +08:00
7431001ed6 feat: RESTful API support update Geo file
and can set update url by user, eg.
geox-url:
  geoip: "http://xxxx/gepip.dat"
  mmdb: "http://xxxx/country.mmdb"
  geosite: "http://xxxx/geosite.dat"
2022-05-24 15:04:13 +08:00
149b4b5b43 feat: RESTful API support disable sniffer 2022-05-24 13:44:52 +08:00
c0eb9aac1c feat: fallback can be select by user 2022-05-24 10:17:44 +08:00
79469fc8d6 feat: uid rule support for logic and rule-set 2022-05-22 13:07:20 +08:00
948700eed6 fix: 并发dns查询,由于ipv6阻塞导致某些情况下的网络不通 2022-05-21 00:34:15 +08:00
3ab82849d4 feat: IN-TYPE rule support
eg. IN-TYPE,SOCKS/REDIR/INNER,Proxy
support list: HTTP HTTPS SOCKS SOCKS4 SOCKS5 REDIR TPROXY TUN INNER
2022-05-20 23:17:16 +08:00
0f43a19fdb refactor: new way to get interface change even for linux 2022-05-20 21:44:19 +08:00
cc1c1340a3 feat: 安卓恢复进程规则,可通过enable-process开关,默认true 2022-05-19 20:44:09 +08:00
fe25ae83df refactor: 修改sticky-session尝试逻辑 2022-05-19 20:27:26 +08:00
c787bbe0e5 fix: 热重载Tun配置 2022-05-19 19:19:19 +08:00
7aff9aac82 fix: sticky-sessions异常 2022-05-18 22:29:27 +08:00
128 changed files with 2363 additions and 1046 deletions

View File

@ -43,7 +43,7 @@ jobs:
env:
NAME: Clash.Meta
BINDIR: bin
run: make -j releases
run: make -j$(($(nproc) + 1)) releases
- name: Delete current release assets
uses: andreaswilli/delete-release-assets-action@v2.0.0

64
.github/workflows/product.yaml vendored Normal file
View 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

View File

@ -33,7 +33,7 @@ jobs:
env:
NAME: Clash.Meta
BINDIR: bin
run: make -j releases
run: make -j$(($(nproc) + 1)) releases
- name: Upload Release
uses: softprops/action-gh-release@v1

View File

@ -47,9 +47,9 @@ WINDOWS_ARCH_LIST = \
windows-arm64 \
windows-arm32v7
all:linux-amd64 linux-arm64\
darwin-amd64 darwin-arm64\
windows-amd64 windows-arm64\
all:linux-amd64v3 linux-arm64\
darwin-amd64v3 darwin-arm64\
windows-amd64v3 windows-arm64\
docker:
GOAMD64=v3 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@

View File

@ -4,6 +4,9 @@ import (
"context"
"encoding/json"
"fmt"
"github.com/Dreamacro/clash/common/queue"
"github.com/Dreamacro/clash/component/dialer"
C "github.com/Dreamacro/clash/constant"
"net"
"net/http"
"net/netip"
@ -11,10 +14,6 @@ import (
"strings"
"time"
"github.com/Dreamacro/clash/common/queue"
"github.com/Dreamacro/clash/component/dialer"
C "github.com/Dreamacro/clash/constant"
"go.uber.org/atomic"
)

View File

@ -11,7 +11,7 @@ import (
// NewHTTPS receive CONNECT request and return ConnContext
func NewHTTPS(request *http.Request, conn net.Conn) *context.ConnContext {
metadata := parseHTTPAddr(request)
metadata.Type = C.HTTPCONNECT
metadata.Type = C.HTTPS
if ip, port, err := parseAddr(conn.RemoteAddr().String()); err == nil {
metadata.SrcIP = ip
metadata.SrcPort = port

View File

@ -6,6 +6,7 @@ import (
"errors"
"github.com/gofrs/uuid"
"net"
"strings"
"github.com/Dreamacro/clash/component/dialer"
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")
}
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
func (b *Base) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) {
return nil, errors.New("no support")
@ -128,7 +133,12 @@ func NewBase(opt BaseOption) *Base {
type conn struct {
net.Conn
chain C.Chain
chain C.Chain
actualRemoteDestination string
}
func (c *conn) RemoteDestination() string {
return c.actualRemoteDestination
}
// 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 {
return &conn{c, []string{a.Name()}}
return &conn{c, []string{a.Name()}, parseRemoteDestination(a.Addr())}
}
type packetConn struct {
net.PacketConn
chain C.Chain
chain C.Chain
actualRemoteDestination string
}
func (c *packetConn) RemoteDestination() string {
return c.actualRemoteDestination
}
// 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 {
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 ""
}
}
}

View File

@ -3,15 +3,19 @@ package outboundgroup
import (
"context"
"encoding/json"
"errors"
"github.com/Dreamacro/clash/adapter/outbound"
"github.com/Dreamacro/clash/component/dialer"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/constant/provider"
"time"
)
type Fallback struct {
*GroupBase
disableUDP bool
testUrl string
selected 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 {
proxies := f.GetProxies(touch)
for _, proxy := range proxies {
if proxy.Alive() {
al := proxies[0]
for i := len(proxies) - 1; i > -1; i-- {
proxy := proxies[i]
if proxy.Name() == f.selected && proxy.Alive() {
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 {
@ -97,5 +128,6 @@ func NewFallback(option *GroupCommonOption, providers []provider.ProxyProvider)
providers,
}),
disableUDP: option.DisableUDP,
testUrl: option.URL,
}
}

View File

@ -1,6 +1,8 @@
package outboundgroup
import (
"context"
"fmt"
"github.com/Dreamacro/clash/adapter/outbound"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/constant/provider"
@ -105,6 +107,34 @@ func (gb *GroupBase) GetProxies(touch bool) []C.Proxy {
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() {
if gb.failedTesting.Load() {
return
@ -116,14 +146,14 @@ func (gb *GroupBase) onDialFailed() {
gb.failedTimes++
if gb.failedTimes == 1 {
log.Warnln("ProxyGroup: %s first failed", gb.Name())
log.Debugln("ProxyGroup: %s first failed", gb.Name())
gb.failedTime = time.Now()
} else {
if time.Since(gb.failedTime) > gb.failedTimeoutInterval() {
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() {
gb.failedTesting.Store(true)
log.Warnln("because %s failed multiple times, active health check", gb.Name())

View File

@ -151,29 +151,35 @@ func strategyConsistentHashing() strategyFn {
func strategyStickySessions() strategyFn {
ttl := time.Minute * 10
c := cache.New[uint64, int](1 * time.Second)
maxRetry := 5
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 {
key := uint64(murmur3.Sum32([]byte(getKeyWithSrcAndDst(metadata))))
length := len(proxies)
idx, expireTime := c.GetWithExpire(key)
if expireTime.IsZero() {
idx = int(jumpHash(key+uint64(time.Now().UnixMilli()), int32(length)))
idx, has := lruCache.Get(key)
if !has {
idx = int(jumpHash(key+uint64(time.Now().UnixNano()), int32(length)))
}
for i := 0; i < length; i++ {
nowIdx := (idx + 1) % length
nowIdx := idx
for i := 1; i < maxRetry; i++ {
proxy := proxies[nowIdx]
if proxy.Alive() {
if nowIdx != idx {
c.Put(key, idx, -1)
c.Put(key, nowIdx, ttl)
lruCache.Delete(key)
lruCache.Set(key, nowIdx)
}
return proxy
} else {
nowIdx = int(jumpHash(key+uint64(time.Now().UnixNano()), int32(length)))
}
}
lruCache.Delete(key)
lruCache.Set(key, 0)
return proxies[0]
}
}

View File

@ -76,7 +76,9 @@ func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, provide
providersMap[groupName] = pd
} else {
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)

View File

@ -51,3 +51,7 @@ func tcpKeepAlive(c net.Conn) {
_ = tcp.SetKeepAlivePeriod(30 * time.Second)
}
}
type SelectAble interface {
Set(string) error
}

View File

@ -26,6 +26,7 @@ type fetcher struct {
done chan struct{}
hash [16]byte
parser parser
interval time.Duration
onUpdate func(any)
}
@ -56,6 +57,12 @@ func (f *fetcher) Initial() (any, error) {
modTime := stat.ModTime()
f.updatedAt = &modTime
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 {
buf, err = f.vehicle.Read()
}
@ -137,21 +144,11 @@ func (f *fetcher) pullLoop() {
for {
select {
case <-f.ticker.C:
elm, same, err := f.Update()
if err != nil {
log.Warnln("[Provider] %s pull error: %s", f.Name(), err.Error())
same, err := f.update()
if same || err != nil {
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:
f.ticker.Stop()
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 {
dir := filepath.Dir(path)
@ -184,5 +201,6 @@ func newFetcher(name string, interval time.Duration, vehicle types.Vehicle, pars
parser: parser,
done: make(chan struct{}, 1),
onUpdate: onUpdate,
interval: interval,
}
}

View File

@ -75,7 +75,7 @@ func (pp *proxySetProvider) Initial() error {
pp.onUpdate(elm)
if pp.healthCheck.auto() {
go pp.healthCheck.process()
defer func() { go pp.healthCheck.process() }()
}
return nil

View File

@ -2,16 +2,12 @@ package provider
import (
"context"
"github.com/Dreamacro/clash/listener/inner"
netHttp "github.com/Dreamacro/clash/component/http"
types "github.com/Dreamacro/clash/constant/provider"
"io"
"net"
"net/http"
"net/url"
"os"
"time"
netHttp "github.com/Dreamacro/clash/common/net"
types "github.com/Dreamacro/clash/constant/provider"
)
type FileVehicle struct {
@ -50,52 +46,16 @@ func (h *HTTPVehicle) Path() string {
func (h *HTTPVehicle) Read() ([]byte, error) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
defer cancel()
uri, err := url.Parse(h.url)
resp, err := netHttp.HttpRequest(ctx, h.url, http.MethodGet, nil, nil)
if err != nil {
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()
buf, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return buf, nil
}

View File

@ -1,5 +0,0 @@
package net
const (
UA = "Clash"
)

View File

@ -15,6 +15,7 @@ var (
dialMux sync.Mutex
actualSingleDialContext = singleDialContext
actualDualStackDialContext = dualStackDialContext
tcpConcurrent = false
DisableIPv6 = false
)
@ -76,17 +77,22 @@ func ListenPacket(ctx context.Context, network, address string, options ...Optio
func SetDial(concurrent bool) {
dialMux.Lock()
tcpConcurrent = concurrent
if concurrent {
actualSingleDialContext = concurrentSingleDialContext
actualDualStackDialContext = concurrentDualStackDialContext
} else {
actualSingleDialContext = singleDialContext
actualDualStackDialContext = concurrentDualStackDialContext
actualDualStackDialContext = dualStackDialContext
}
dialMux.Unlock()
}
func GetDial() bool {
return tcpConcurrent
}
func dialContext(ctx context.Context, network string, destination netip.Addr, port string, opt *option) (net.Conn, error) {
dialer := &net.Dialer{}
if opt.interfaceName != "" {

View File

@ -30,7 +30,7 @@ func (l *loader) LoadGeoSiteWithAttr(file string, siteWithAttr string) ([]*route
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 {
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) {
return l.LoadIP(C.GeoipName, country)
return l.LoadIPByPath(C.GeoipName, country)
}
var loaders map[string]func() LoaderImplementation

View File

@ -5,8 +5,10 @@ import (
)
type LoaderImplementation interface {
LoadSite(filename, list string) ([]*router.Domain, error)
LoadIP(filename, country string) ([]*router.CIDR, error)
LoadSiteByPath(filename, list string) ([]*router.Domain, 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 {

View File

@ -1,6 +1,7 @@
package memconservative
import (
"errors"
"fmt"
"runtime"
@ -13,7 +14,7 @@ type memConservativeLoader struct {
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()
geoip, err := m.geoipcache.Unmarshal(filename, country)
if err != nil {
@ -22,7 +23,11 @@ func (m *memConservativeLoader) LoadIP(filename, country string) ([]*router.CIDR
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()
geosite, err := m.geositecache.Unmarshal(filename, list)
if err != nil {
@ -31,6 +36,10 @@ func (m *memConservativeLoader) LoadSite(filename, list string) ([]*router.Domai
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 {
return &memConservativeLoader{make(map[string]*router.GeoIP), make(map[string]*router.GeoSite)}
}

View File

@ -29,11 +29,7 @@ func ReadAsset(file string) ([]byte, error) {
return ReadFile(C.Path.GetAssetLocation(file))
}
func loadIP(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())
}
func loadIP(geoipBytes []byte, country string) ([]*router.CIDR, error) {
var geoipList router.GeoIPList
if err := proto.Unmarshal(geoipBytes, &geoipList); err != nil {
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) {
geositeBytes, err := ReadAsset(filename)
if err != nil {
return nil, fmt.Errorf("failed to open file: %s, base error: %s", filename, err.Error())
}
func loadSite(geositeBytes []byte, list string) ([]*router.Domain, error) {
var geositeList router.GeoSiteList
if err := proto.Unmarshal(geositeBytes, &geositeList); err != nil {
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{}
func (d standardLoader) LoadSite(filename, list string) ([]*router.Domain, error) {
return loadSite(filename, list)
func (d standardLoader) LoadSiteByPath(filename, 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())
}
return loadSite(geositeBytes, list)
}
func (d standardLoader) LoadIP(filename, country string) ([]*router.CIDR, error) {
return loadIP(filename, country)
func (d standardLoader) LoadSiteByBytes(geositeBytes []byte, list string) ([]*router.Domain, error) {
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() {

View File

@ -21,16 +21,16 @@ func SetLoader(newLoader string) {
geoLoaderName = newLoader
}
func Verify(name string) bool {
func Verify(name string) error {
switch name {
case C.GeositeName:
_, _, err := LoadGeoSiteMatcher("CN")
return err == nil
return err
case C.GeoipName:
_, _, err := LoadGeoIPMatcher("CN")
return err == nil
return err
default:
return false
return fmt.Errorf("not support name")
}
}

64
component/http/http.go Normal file
View 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
View 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
View 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
View 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
View 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)
}
}

View 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)) {
}

View File

@ -6,13 +6,14 @@ import (
C "github.com/Dreamacro/clash/constant"
"net"
"net/netip"
"runtime"
)
var (
ErrInvalidNetwork = errors.New("invalid network")
ErrPlatformNotSupport = errors.New("not support on this platform")
ErrNotFound = errors.New("process not found")
enableFindProcess = true
)
const (
@ -20,7 +21,11 @@ const (
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)
}
@ -33,10 +38,9 @@ func FindUid(network string, srcIP netip.Addr, srcPort int) (int32, error) {
}
func ShouldFindProcess(metadata *C.Metadata) bool {
if runtime.GOOS == "android" {
return false
}
if metadata.Process != "" || metadata.ProcessPath != "" {
if !enableFindProcess ||
metadata.Process != "" ||
metadata.ProcessPath != "" {
return false
}
for _, ip := range localIPs {

View File

@ -21,7 +21,7 @@ func resolveSocketByNetlink(network string, ip netip.Addr, srcPort int) (int32,
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
switch network {
case TCP:
@ -29,14 +29,14 @@ func findProcessName(network string, ip netip.Addr, port int) (string, error) {
case UDP:
spath = "net.inet.udp.pcblist_n"
default:
return "", ErrInvalidNetwork
return -1, "", ErrInvalidNetwork
}
isIPv4 := ip.Is4()
value, err := syscall.Sysctl(spath)
if err != nil {
return "", err
return -1, "", err
}
buf := []byte(value)
@ -81,10 +81,11 @@ func findProcessName(network string, ip netip.Addr, port int) (string, error) {
// xsocket_n.so_last_pid
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) {

View File

@ -25,7 +25,7 @@ func resolveSocketByNetlink(network string, ip netip.Addr, srcPort int) (int32,
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() {
if err := initSearcher(); err != nil {
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 {
return "", ErrPlatformNotSupport
return -1, "", ErrPlatformNotSupport
}
var spath string
@ -46,21 +46,22 @@ func findProcessName(network string, ip netip.Addr, srcPort int) (string, error)
case UDP:
spath = "net.inet.udp.pcblist"
default:
return "", ErrInvalidNetwork
return -1, "", ErrInvalidNetwork
}
value, err := syscall.Sysctl(spath)
if err != nil {
return "", err
return -1, "", err
}
buf := []byte(value)
pid, err := defaultSearcher.Search(buf, ip, uint16(srcPort), isTCP)
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) {

View File

@ -8,6 +8,8 @@ import (
"net/netip"
"os"
"path"
"path/filepath"
"runtime"
"strings"
"syscall"
"unicode"
@ -32,12 +34,13 @@ const (
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)
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) {
@ -195,8 +198,19 @@ func resolveProcessNameByProcSearch(inode, uid int32) (string, error) {
continue
}
if bytes.Equal(buffer[:n], socket) {
return os.Readlink(path.Join(processPath, "exe"))
if runtime.GOOS == "android" {
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)
}
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 {
return strings.IndexFunc(s, func(r rune) bool {
return !unicode.IsDigit(r)

View File

@ -4,8 +4,8 @@ package process
import "net/netip"
func findProcessName(network string, ip netip.Addr, srcPort int) (string, error) {
return "", ErrPlatformNotSupport
func findProcessName(network string, ip netip.Addr, srcPort int) (int32, string, error) {
return -1, "", ErrPlatformNotSupport
}
func resolveSocketByNetlink(network string, ip netip.Addr, srcPort int) (int32, int32, error) {

View File

@ -62,7 +62,7 @@ func initWin32API() error {
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() {
err := initWin32API()
if err != nil {
@ -86,21 +86,22 @@ func findProcessName(network string, ip netip.Addr, srcPort int) (string, error)
fn = getExUDPTable
class = udpTablePid
default:
return "", ErrInvalidNetwork
return -1, "", ErrInvalidNetwork
}
buf, err := getTransportTable(fn, family, class)
if err != nil {
return "", err
return -1, "", err
}
s := newSearcher(family == windows.AF_INET, network == TCP)
pid, err := s.Search(buf, ip, uint16(srcPort))
if err != nil {
return "", err
return -1, "", err
}
return getExecPathFromPID(pid)
pp, err := getExecPathFromPID(pid)
return -1, pp, err
}
type searcher struct {

View File

@ -89,24 +89,39 @@ func ResolveIP(host string) (netip.Addr, error) {
// ResolveIPv4ProxyServerHost proxies server host only
func ResolveIPv4ProxyServerHost(host string) (netip.Addr, error) {
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)
}
// ResolveIPv6ProxyServerHost proxies server host only
func ResolveIPv6ProxyServerHost(host string) (netip.Addr, error) {
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)
}
// ResolveProxyServerHost proxies server host only
func ResolveProxyServerHost(host string) (netip.Addr, error) {
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)
}

View File

@ -47,12 +47,14 @@ type General struct {
UnifiedDelay bool
LogLevel log.LogLevel `json:"log-level"`
IPv6 bool `json:"ipv6"`
Interface string `json:"-"`
Interface string `json:"interface-name"`
RoutingMark int `json:"-"`
GeodataMode bool `json:"geodata-mode"`
GeodataLoader string `json:"geodata-loader"`
TCPConcurrent bool `json:"tcp-concurrent"`
EnableProcess bool `json:"enable-process"`
Tun Tun `json:"tun"`
Sniffing bool `json:"sniffing"`
}
// Inbound config
@ -113,6 +115,7 @@ type Tun struct {
DNSHijack []netip.AddrPort `yaml:"dns-hijack" json:"dns-hijack"`
AutoRoute bool `yaml:"auto-route" json:"auto-route"`
AutoDetectInterface bool `yaml:"auto-detect-interface" json:"auto-detect-interface"`
TunAddressPrefix netip.Prefix `yaml:"-" json:"-"`
}
// IPTables config
@ -205,6 +208,7 @@ type RawConfig struct {
GeodataMode bool `yaml:"geodata-mode"`
GeodataLoader string `yaml:"geodata-loader"`
TCPConcurrent bool `yaml:"tcp-concurrent" json:"tcp-concurrent"`
EnableProcess bool `yaml:"enable-process" json:"enable-process"`
Sniffer RawSniffer `yaml:"sniffer"`
ProxyProvider map[string]map[string]any `yaml:"proxy-providers"`
@ -215,11 +219,18 @@ type RawConfig struct {
IPTables IPTables `yaml:"iptables"`
Experimental Experimental `yaml:"experimental"`
Profile Profile `yaml:"profile"`
GeoXUrl RawGeoXUrl `yaml:"geox-url"`
Proxy []map[string]any `yaml:"proxies"`
ProxyGroup []map[string]any `yaml:"proxy-groups"`
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 {
Enable bool `yaml:"enable" json:"enable"`
Sniffing []string `yaml:"sniffing" json:"sniffing"`
@ -254,6 +265,7 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) {
Proxy: []map[string]any{},
ProxyGroup: []map[string]any{},
TCPConcurrent: false,
EnableProcess: true,
Tun: RawTun{
Enable: false,
Device: "",
@ -304,6 +316,11 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) {
Profile: Profile{
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 {
@ -327,12 +344,6 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) {
}
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)
proxies, providers, err := parseProxies(rawCfg)
@ -361,6 +372,12 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) {
}
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.Sniffer, err = parseSniffer(rawCfg.Sniffer)
@ -409,6 +426,7 @@ func parseGeneral(cfg *RawConfig) (*General, error) {
GeodataMode: cfg.GeodataMode,
GeodataLoader: cfg.GeodataLoader,
TCPConcurrent: cfg.TCPConcurrent,
EnableProcess: cfg.EnableProcess,
}, nil
}
@ -865,7 +883,7 @@ func parseAuthentication(rawRecords []string) []auth.AuthUser {
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 {
autoDetectInterfaceName, err := commons.GetAutoDetectInterface()
if err != nil {
@ -892,6 +910,13 @@ func parseTun(rawTun RawTun, general *General) (*Tun, error) {
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{
Enable: rawTun.Enable,
Device: rawTun.Device,
@ -899,6 +924,7 @@ func parseTun(rawTun RawTun, general *General) (*Tun, error) {
DNSHijack: dnsHijack,
AutoRoute: rawTun.AutoRoute,
AutoDetectInterface: rawTun.AutoDetectInterface,
TunAddressPrefix: tunAddressPrefix,
}, nil
}

View File

@ -15,7 +15,7 @@ import (
var initMode = true
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 {
return
}
@ -32,7 +32,7 @@ func downloadMMDB(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 {
return
}
@ -49,7 +49,7 @@ func downloadGeoIP(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 {
return
}
@ -74,8 +74,8 @@ func initGeoSite() error {
log.Infoln("Download GeoSite.dat finish")
}
if initMode {
if !geodata.Verify(C.GeositeName) {
log.Warnln("GeoSite.dat invalid, remove and download")
if err := geodata.Verify(C.GeositeName); err != nil {
log.Warnln("GeoSite.dat invalid, remove and download: %s", err)
if err := os.Remove(C.Path.GeoSite()); err != nil {
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")
}
if !geodata.Verify(C.GeoipName) {
log.Warnln("GeoIP.dat invalid, remove and download")
if err := geodata.Verify(C.GeoipName); err != nil {
log.Warnln("GeoIP.dat invalid, remove and download: %s", err)
if err := os.Remove(C.Path.GeoIP()); err != nil {
return fmt.Errorf("can't remove invalid GeoIP.dat: %s", err.Error())
}
@ -159,6 +159,9 @@ func Init(dir string) error {
if !C.GeodataMode {
C.GeodataMode = rawCfg.GeodataMode
}
C.GeoIpUrl = rawCfg.GeoXUrl.GeoIp
C.GeoSiteUrl = rawCfg.GeoXUrl.GeoSite
C.MmdbUrl = rawCfg.GeoXUrl.Mmdb
// initial GeoIP
if err := initGeoIP(); err != nil {
return fmt.Errorf("can't initial GeoIP: %w", err)

80
config/updateGeo.go Normal file
View 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)
}

View File

@ -41,6 +41,7 @@ const (
type Connection interface {
Chains() Chain
AppendToChains(adapter ProxyAdapter)
RemoteDestination() string
}
type Chain []string
@ -107,6 +108,11 @@ type ProxyAdapter interface {
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 {
Time time.Time `json:"time"`
Delay uint16 `json:"delay"`

View File

@ -0,0 +1,7 @@
//go:build no_doq
package features
func init() {
TAGS = append(TAGS, "no_doq")
}

View File

@ -0,0 +1,7 @@
//go:build no_gvisor
package features
func init() {
TAGS = append(TAGS, "no_gvisor")
}

View File

@ -0,0 +1,3 @@
package features
var TAGS = make([]string, 0, 0)

View File

@ -1,3 +1,8 @@
package constant
var GeodataMode bool
var (
GeodataMode bool
GeoIpUrl string
MmdbUrl string
GeoSiteUrl string
)

View File

@ -19,7 +19,7 @@ const (
ALLNet
HTTP Type = iota
HTTPCONNECT
HTTPS
SOCKS4
SOCKS5
REDIR
@ -49,8 +49,8 @@ func (t Type) String() string {
switch t {
case HTTP:
return "HTTP"
case HTTPCONNECT:
return "HTTP Connect"
case HTTPS:
return "HTTPS"
case SOCKS4:
return "Socks4"
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) {
return json.Marshal(t.String())
}

View File

@ -1,7 +1,7 @@
package provider
import (
"github.com/Dreamacro/clash/constant"
C "github.com/Dreamacro/clash/constant"
)
// Vehicle Type
@ -65,10 +65,10 @@ type Provider interface {
// ProxyProvider interface
type ProxyProvider interface {
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.
// Commonly used in DialContext and DialPacketConn
ProxiesWithTouch() []constant.Proxy
ProxiesWithTouch() []C.Proxy
HealthCheck()
Version() uint
}
@ -100,7 +100,7 @@ func (rt RuleType) String() string {
type RuleProvider interface {
Provider
Behavior() RuleType
Match(*constant.Metadata) bool
Match(*C.Metadata) bool
ShouldResolveIP() bool
AsRule(adaptor string) constant.Rule
AsRule(adaptor string) C.Rule
}

View File

@ -9,13 +9,17 @@ const (
GEOIP
IPCIDR
SrcIPCIDR
IPSuffix
SrcIPSuffix
SrcPort
DstPort
Process
Script
ProcessPath
RuleSet
Network
Uid
INTYPE
MATCH
AND
OR
@ -40,6 +44,10 @@ func (rt RuleType) String() string {
return "IPCIDR"
case SrcIPCIDR:
return "SrcIPCIDR"
case IPSuffix:
return "IPSuffix"
case SrcIPSuffix:
return "SrcIPSuffix"
case SrcPort:
return "SrcPort"
case DstPort:
@ -54,8 +62,12 @@ func (rt RuleType) String() string {
return "RuleSet"
case Network:
return "Network"
case Script:
return "Script"
case Uid:
return "Uid"
case INTYPE:
return "InType"
case AND:
return "AND"
case OR:

27
constant/script.go Normal file
View 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
}

View File

@ -82,12 +82,16 @@ func (r *Resolver) ResolveAllIP(host string) (ips []netip.Addr, err error) {
ips, err = r.resolveIP(host, D.TypeA)
ipv6s, open := <-ch
if !open && err != nil {
return nil, resolver.ErrIPNotFound
select {
case ipv6s, open := <-ch:
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
}

10
go.mod
View File

@ -4,7 +4,9 @@ go 1.18
require (
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/cors v1.2.1
github.com/go-chi/render v1.0.1
@ -16,6 +18,7 @@ require (
github.com/oschwald/geoip2-golang v1.7.0
github.com/sirupsen/logrus v1.8.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
go.etcd.io/bbolt v1.3.6
go.uber.org/atomic v1.9.0
@ -37,9 +40,9 @@ require (
github.com/cheekybits/genny v1.0.0 // indirect
github.com/davecgh/go-spew v1.1.1 // 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/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-17 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/pmezard/go-difflib v1.0.0 // 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/text v0.3.8-0.20220124021120-d1c84af989ab // indirect
golang.org/x/tools v0.1.10 // indirect
@ -57,4 +61,4 @@ require (
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
View File

@ -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/Dreamacro/go-shadowsocks2 v0.1.8 h1:Ixejp5JscEc866gAvm/l6TFd7BOBvDviKgwb1quWw3g=
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/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 h1:fGKWZ25VApYnuPZoNeqdH/nZtHa2XMajwH6Yj/OgoVc=
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/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=
@ -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/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/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.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91 h1:Izz0+t1Z5nI16/II7vuEo/nHjodOg0p7+OiDpjX5t1E=
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/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=
@ -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/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns=
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/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
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/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.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
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.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.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/go.mod h1:AzgQoPda7N+3IqMMMkywBKggIFo2KT6pfnlrQ2QieeI=
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/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/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/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=
@ -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/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
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/go.mod h1:YGGVbz9cOxyKFUmhW7LGaLZaMA0cPlHJinvAmVxEMSU=
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-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-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-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-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-20200930185726-fdedc70b468f/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.3/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/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0=
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-20220411194840-2f41105eb62f h1:GGU+dLjvlC3qDwqYgL6UgRmHXhOOgns0bZu2Ty5mm6U=
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/go.mod h1:bVQfyl2sCM/QIIGHpWbFGfHPuDvqnCNkT6MQLTCjO/U=
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/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/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/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=

View File

@ -2,6 +2,7 @@ package executor
import (
"fmt"
"github.com/Dreamacro/clash/component/process"
"github.com/Dreamacro/clash/listener/inner"
"net/netip"
"os"
@ -85,7 +86,7 @@ func ApplyConfig(cfg *config.Config, force bool) {
loadRuleProvider(cfg.RuleProviders)
updateGeneral(cfg.General, force)
updateIPTables(cfg)
updateTun(cfg.Tun, cfg.DNS)
updateTun(cfg.Tun)
updateExperimental(cfg)
log.SetLevel(cfg.General.LogLevel)
@ -118,6 +119,9 @@ func GetGeneral() *config.General {
IPv6: !resolver.DisableIPv6,
GeodataLoader: G.LoaderName(),
Tun: P.GetTunConf(),
Interface: dialer.DefaultInterface.Load(),
Sniffing: tunnel.IsSniffing(),
TCPConcurrent: dialer.GetDial(),
}
return general
@ -244,12 +248,8 @@ func loadProxyProvider(proxyProviders map[string]provider.ProxyProvider) {
wg.Wait()
}
func updateTun(tun *config.Tun, dns *config.DNS) {
var tunAddressPrefix *netip.Prefix
if dns.FakeIPRange != nil {
tunAddressPrefix = dns.FakeIPRange.IPNet()
}
P.ReCreateTun(tun, tunAddressPrefix, tunnel.TCPIn(), tunnel.UDPIn())
func updateTun(tun *config.Tun) {
P.ReCreateTun(tun, tunnel.TCPIn(), tunnel.UDPIn())
}
func updateSniffer(sniffer *config.Sniffer) {
@ -274,6 +274,7 @@ func updateSniffer(sniffer *config.Sniffer) {
func updateGeneral(general *config.General, force bool) {
log.SetLevel(general.LogLevel)
process.EnableFindProcess(general.EnableProcess)
tunnel.SetMode(general.Mode)
dialer.DisableIPv6 = !general.IPv6
if !dialer.DisableIPv6 {
@ -353,7 +354,7 @@ func patchSelectGroup(proxies map[string]C.Proxy) {
continue
}
selector, ok := outbound.ProxyAdapter.(*outboundgroup.Selector)
selector, ok := outbound.ProxyAdapter.(outboundgroup.SelectAble)
if !ok {
continue
}
@ -428,7 +429,7 @@ func updateIPTables(cfg *config.Config) {
}
func Shutdown() {
P.Cleanup()
P.Cleanup(false)
tproxy.CleanupTProxyIPTables()
resolver.StoreFakePoolState()

View File

@ -1,8 +1,10 @@
package route
import (
"github.com/Dreamacro/clash/component/dialer"
"net/http"
"path/filepath"
"sync"
"github.com/Dreamacro/clash/component/resolver"
"github.com/Dreamacro/clash/config"
@ -16,26 +18,34 @@ import (
"github.com/go-chi/render"
)
var (
updateGeoMux sync.Mutex
updatingGeo = false
)
func configRouter() http.Handler {
r := chi.NewRouter()
r.Get("/", getConfigs)
r.Put("/", updateConfigs)
r.Post("/geo", updateGeoDatabases)
r.Patch("/", patchConfigs)
return r
}
type configSchema struct {
Port *int `json:"port"`
SocksPort *int `json:"socks-port"`
RedirPort *int `json:"redir-port"`
TProxyPort *int `json:"tproxy-port"`
MixedPort *int `json:"mixed-port"`
Tun *config.Tun `json:"tun"`
AllowLan *bool `json:"allow-lan"`
BindAddress *string `json:"bind-address"`
Mode *tunnel.TunnelMode `json:"mode"`
LogLevel *log.LogLevel `json:"log-level"`
IPv6 *bool `json:"ipv6"`
Port *int `json:"port"`
SocksPort *int `json:"socks-port"`
RedirPort *int `json:"redir-port"`
TProxyPort *int `json:"tproxy-port"`
MixedPort *int `json:"mixed-port"`
Tun *config.Tun `json:"tun"`
AllowLan *bool `json:"allow-lan"`
BindAddress *string `json:"bind-address"`
Mode *tunnel.TunnelMode `json:"mode"`
LogLevel *log.LogLevel `json:"log-level"`
IPv6 *bool `json:"ipv6"`
Sniffing *bool `json:"sniffing"`
TcpConcurrent *bool `json:"tcp-concurrent"`
}
func getConfigs(w http.ResponseWriter, r *http.Request) {
@ -67,6 +77,14 @@ func patchConfigs(w http.ResponseWriter, r *http.Request) {
P.SetBindAddress(*general.BindAddress)
}
if general.Sniffing != nil {
tunnel.SetSniffing(*general.Sniffing)
}
if general.TcpConcurrent != nil {
dialer.SetDial(*general.TcpConcurrent)
}
ports := P.GetPorts()
tcpIn := tunnel.TCPIn()
@ -138,3 +156,42 @@ func updateConfigs(w http.ResponseWriter, r *http.Request) {
executor.ApplyConfig(cfg, force)
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
View 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)
}

View File

@ -83,7 +83,7 @@ func updateProxy(w http.ResponseWriter, r *http.Request) {
}
proxy := r.Context().Value(CtxKeyProxy).(*adapter.Proxy)
selector, ok := proxy.ProxyAdapter.(*outboundgroup.Selector)
selector, ok := proxy.ProxyAdapter.(outboundgroup.SelectAble)
if !ok {
render.Status(r, http.StatusBadRequest)
render.JSON(w, r, newError("Must be a Selector"))

View File

@ -20,7 +20,7 @@ type Rule struct {
Type string `json:"type"`
Payload string `json:"payload"`
Proxy string `json:"proxy"`
Size int `json:"Size"`
Size int `json:"size"`
}
func getRules(w http.ResponseWriter, r *http.Request) {

View File

@ -68,6 +68,7 @@ func Start(addr string, secret string) {
r.Get("/version", version)
r.Mount("/configs", configRouter())
r.Mount("/proxies", proxyRouter())
r.Mount("/group", GroupRouter())
r.Mount("/rules", ruleRouter())
r.Mount("/connections", connectionRouter())
r.Mount("/providers/proxies", proxyProviderRouter())

View File

@ -2,11 +2,9 @@ package proxy
import (
"fmt"
"github.com/Dreamacro/clash/common/cmd"
"github.com/Dreamacro/clash/listener/inner"
"github.com/Dreamacro/clash/listener/tun/ipstack/commons"
"net"
"net/netip"
"runtime"
"sort"
"strconv"
"sync"
@ -25,10 +23,9 @@ import (
)
var (
allowLan = false
bindAddress = "*"
lastTunConf *config.Tun
lastTunAddressPrefix *netip.Prefix
allowLan = false
bindAddress = "*"
lastTunConf *config.Tun
socksListener *socks.Listener
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())
}
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()
defer tunMux.Unlock()
@ -335,35 +332,23 @@ func ReCreateTun(tunConf *config.Tun, tunAddressPrefix *netip.Prefix, tcpIn chan
defer func() {
if err != nil {
log.Errorln("Start TUN listening error: %s", err.Error())
Cleanup(false)
}
}()
if tunAddressPrefix == nil {
tunAddressPrefix = lastTunAddressPrefix
}
if !hasTunConfigChange(tunConf, tunAddressPrefix) {
if !hasTunConfigChange(tunConf) {
return
}
if tunStackListener != nil {
_ = tunStackListener.Close()
tunStackListener = nil
lastTunConf = nil
lastTunAddressPrefix = nil
}
Cleanup(true)
if !tunConf.Enable {
return
}
tunStackListener, err = tun.New(tunConf, tunAddressPrefix, tcpIn, udpIn)
if err != nil {
log.Warnln("Failed to start TUN interface: %s", err)
}
tunStackListener, err = tun.New(tunConf, tcpIn, udpIn)
lastTunConf = tunConf
lastTunAddressPrefix = tunAddressPrefix
}
// 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)
}
func hasTunConfigChange(tunConf *config.Tun, tunAddressPrefix *netip.Prefix) bool {
func hasTunConfigChange(tunConf *config.Tun) bool {
if lastTunConf == nil {
return true
}
@ -448,29 +433,29 @@ func hasTunConfigChange(tunConf *config.Tun, tunAddressPrefix *netip.Prefix) boo
if lastTunConf.Enable != tunConf.Enable ||
lastTunConf.Device != tunConf.Device ||
lastTunConf.Stack != tunConf.Stack ||
lastTunConf.AutoRoute != tunConf.AutoRoute {
lastTunConf.AutoRoute != tunConf.AutoRoute ||
lastTunConf.AutoDetectInterface != tunConf.AutoDetectInterface {
return true
}
if (tunAddressPrefix != nil && lastTunAddressPrefix == nil) || (tunAddressPrefix == nil && lastTunAddressPrefix != nil) {
return true
}
if tunAddressPrefix != nil && lastTunAddressPrefix != nil && *tunAddressPrefix != *lastTunAddressPrefix {
if tunConf.TunAddressPrefix.String() != lastTunConf.TunAddressPrefix.String() {
return true
}
return false
}
func Cleanup() {
func Cleanup(wait bool) {
if tunStackListener != nil {
_ = tunStackListener.Close()
if runtime.GOOS == "android" {
prefs := []int{9000, 9001, 9002, 9003, 9004}
for _, pref := range prefs {
_, _ = cmd.ExecCmd(fmt.Sprintf("ip rule del pref %d", pref))
}
commons.StopDefaultInterfaceChangeMonitor()
if wait {
commons.WaitForTunClose(lastTunConf.Device)
}
commons.CleanupRule()
}
tunStackListener = nil
lastTunConf = nil
}

View File

@ -1,3 +1,5 @@
//go:build !no_gvisor
package device
import (

View 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
}

View File

@ -4,24 +4,13 @@ package fdbased
import (
"fmt"
"os"
"strconv"
"github.com/Dreamacro/clash/listener/tun/device"
"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) {
fd, err := strconv.Atoi(name)
if err != nil {

View 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
}

View File

@ -0,0 +1,14 @@
//go:build no_gvisor
package fdbased
import (
"os"
)
type FD struct {
fd int
mtu uint32
file *os.File
}

View File

@ -7,7 +7,6 @@ import (
"os"
"github.com/Dreamacro/clash/listener/tun/device"
"github.com/Dreamacro/clash/listener/tun/device/iobased"
)
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 {
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
return newEp(f)
}
func (f *FD) useIOBased() error {

View 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
}

View 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")
}

View File

@ -1,3 +1,5 @@
//go:build !no_gvisor
// Package iobased provides the implementation of io.ReadWriter
// based data-link layer endpoints.
package iobased

View File

@ -0,0 +1 @@
package iobased

View 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)
}

View 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

View 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

View 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

View 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

View File

@ -1,11 +1,10 @@
//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
*
* Copyright (C) 2019-2021 WireGuard LLC. All Rights Reserved.
*/
package wintun
package driver

View File

@ -3,28 +3,15 @@
package tun
import (
"errors"
"fmt"
"os"
"runtime"
"github.com/Dreamacro/clash/listener/tun/device"
"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 Open(name string, mtu uint32) (_ device.Device, err error) {
defer func() {
if r := recover(); r != nil {
@ -43,11 +30,11 @@ func Open(name string, mtu uint32) (_ device.Device, err error) {
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
if err != nil && runtime.GOOS == "windows" && errors.Is(err, os.ErrExist) {
nt, err = tun.CreateTUN(t.name, forcedMTU)
// retry if abnormal exit at last time on Windows
if err != nil && runtime.GOOS == "windows" && os.IsExist(err) {
nt, err = newDevice(t.name, forcedMTU)
}
if err != nil {
@ -92,11 +79,7 @@ func (t *TUN) Write(packet []byte) (int, error) {
}
func (t *TUN) Close() error {
defer func(ep *iobased.Endpoint) {
if ep != nil {
ep.Close()
}
}(t.Endpoint)
defer closeIO(t)
return t.nt.Close()
}
@ -106,12 +89,7 @@ func (t *TUN) Name() string {
}
func (t *TUN) UseEndpoint() 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
return newEq(t)
}
func (t *TUN) UseIOBased() error {

View 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
}

View 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
}

View File

@ -2,7 +2,13 @@
package tun
import "golang.zx2c4.com/wireguard/tun"
const (
offset = 4 /* 4 bytes TUN_PI */
defaultMTU = 1500
)
func newDevice(name string, mtu int) (tun.Device, error) {
return tun.CreateTUN(name, mtu)
}

View File

@ -1,6 +1,8 @@
package tun
import (
"github.com/Dreamacro/clash/listener/tun/device/tun/driver"
"golang.org/x/sys/windows"
"golang.zx2c4.com/wireguard/tun"
)
@ -20,3 +22,11 @@ func init() {
func (t *TUN) LUID() uint64 {
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)
}

View File

@ -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)
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
)

View File

@ -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=

View File

@ -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)
}

View File

@ -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
}

View 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{}{}
}
}

View 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{}{}
}
}

View File

@ -2,16 +2,13 @@ package commons
import (
"fmt"
"github.com/Dreamacro/clash/log"
"net"
"time"
"github.com/Dreamacro/clash/component/dialer"
"github.com/Dreamacro/clash/log"
)
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"}
defaultInterfaceMonitorDuration = 3 * time.Second
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"}
)
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])
}
func DefaultInterfaceChangeMonitor() {
t := time.NewTicker(defaultInterfaceMonitorDuration)
func WaitForTunClose(deviceName string) {
t := time.NewTicker(600 * time.Millisecond)
defer t.Stop()
log.Debugln("[TUN] waiting for device close")
for {
<-t.C
interfaceName, err := GetAutoDetectInterface()
interfaces, err := net.Interfaces()
if err != nil {
log.Warnln("[TUN] default interface monitor exited, cause: %v", err)
continue
break
}
old := dialer.DefaultInterface.Load()
if interfaceName == old {
continue
found := false
for i := len(interfaces) - 1; i > -1; i-- {
if interfaces[i].Name == deviceName {
found = true
break
}
}
dialer.DefaultInterface.Store(interfaceName)
log.Warnln("[TUN] default interface changed by monitor, %s => %s", old, interfaceName)
if !found {
break
}
}
}

View File

@ -2,86 +2,139 @@ package commons
import (
"fmt"
"github.com/Dreamacro/clash/common/cmd"
"github.com/Dreamacro/clash/listener/tun/device"
"github.com/Dreamacro/clash/log"
"github.com/vishvananda/netlink"
"net"
"net/netip"
"strconv"
"strings"
)
func GetAutoDetectInterface() (ifn string, err error) {
cmdRes, err := cmd.ExecCmd("ip route get 1.1.1.1 uid 4294967295")
sps := strings.Split(cmdRes, " ")
if len(sps) > 4 {
ifn = sps[4]
routes, err := netlink.RouteGetWithOptions(
net.ParseIP("1.1.1.1"),
&netlink.RouteGetOptions{
Uid: &netlink.UID{Uid: 4294967295},
})
if err != nil {
return "", err
}
if ifn == "" {
err = fmt.Errorf("interface not found")
for _, route := range routes {
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 (
interfaceName = dev.Name()
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 {
return err
}
_, err = cmd.ExecCmd(fmt.Sprintf("ip link set %s up", interfaceName))
naddr, err := netlink.ParseAddr(addr.String())
if err != nil {
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
}
if autoRoute {
err = configInterfaceRouting(interfaceName, addr, autoDetectInterface)
err = configInterfaceRouting(metaLink.Attrs().Index, interfaceName, ip)
}
return err
}
func configInterfaceRouting(interfaceName string, addr netip.Prefix, autoDetectInterface bool) error {
linkIP := addr.Masked().Addr().Next()
func configInterfaceRouting(index int, interfaceName string, ip netip.Addr) error {
const tableId = 1981801
var pref = 9000
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
}
}
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 {
go DefaultInterfaceChangeMonitor()
logIfErr := func(e error) {
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
}
func execAddRuleCmd(rule string) {
_, err := cmd.ExecCmd("ip rule add " + rule)
if err != nil {
log.Warnln("%s", err)
func CleanupRule() {
r := netlink.NewRule()
for i := 0; i < 5; i++ {
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
}

View File

@ -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")
}
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() {
return fmt.Errorf("supported ipv4 only")
}
@ -37,12 +37,12 @@ func ConfigInterfaceAddress(dev device.Device, addr netip.Prefix, forceMTU int,
}
if autoRoute {
err = configInterfaceRouting(interfaceName, addr, autoDetectInterface)
err = configInterfaceRouting(interfaceName, addr)
}
return err
}
func configInterfaceRouting(interfaceName string, addr netip.Prefix, autoDetectInterface bool) error {
func configInterfaceRouting(interfaceName string, addr netip.Prefix) error {
var (
routes = append(defaultRoutes, addr.String())
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)
}
@ -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))
return err
}
func CleanupRule() {}

View File

@ -4,58 +4,86 @@ package commons
import (
"fmt"
"github.com/Dreamacro/clash/common/cmd"
"github.com/Dreamacro/clash/listener/tun/device"
"github.com/vishvananda/netlink"
"net"
"net/netip"
)
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 (
interfaceName = dev.Name()
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
}
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
}
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
}
if autoRoute {
_ = configInterfaceRouting(interfaceName, addr, autoDetectInterface)
_ = configInterfaceRouting(metaLink.Attrs().Index, ip)
}
return nil
}
func configInterfaceRouting(interfaceName string, addr netip.Prefix, autoDetectInterface bool) error {
linkIP := addr.Masked().Addr().Next()
func configInterfaceRouting(index int, ip netip.Addr) error {
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
}
}
if autoDetectInterface {
go DefaultInterfaceChangeMonitor()
}
return nil
}
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
}
func CleanupRule() {}

View File

@ -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)
}
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)
}
func CleanupRule() {}

View File

@ -27,7 +27,7 @@ func GetAutoDetectInterface() (string, error) {
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()
tryTimes := 0
var err error
@ -205,10 +205,6 @@ startOver:
wintunInterfaceName = dev.Name()
if autoDetectInterface {
go DefaultInterfaceChangeMonitor()
}
return nil
}
@ -273,3 +269,5 @@ func getAutoDetectInterfaceByFamily(family winipcfg.AddressFamily) (string, erro
return "", errors.New("ethernet interface not found")
}
func CleanupRule() {}

View File

@ -1,3 +1,5 @@
//go:build !no_gvisor
package adapter
import (

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