Compare commits

..

13 Commits

Author SHA1 Message Date
9cc14b80b9 chore: workflow 2022-05-01 13:25:04 +08:00
85410e1f25 chore: workflow 2022-05-01 13:22:38 +08:00
6fce893428 chore: workflow 2022-05-01 13:22:02 +08:00
2dd7a87f4c chore: modify makefile 2022-05-01 13:22:02 +08:00
c6d1b7aff4 chore: modify github workflows 2022-05-01 13:22:02 +08:00
dc0dc1cfe1 chore: modify github workflows 2022-05-01 13:22:02 +08:00
e0bec5ad2b chore: modify github workflows 2022-05-01 13:22:02 +08:00
d7e63975b9 Update prerelease.yml 2022-05-01 12:14:36 +08:00
394a297368 chore: modify github workflows 2022-05-01 11:58:06 +08:00
10a9eab542 chore: modify github workflows 2022-05-01 11:56:45 +08:00
368a44ff73 chore: modify github workflows 2022-05-01 11:18:11 +08:00
4209aa6738 chore: modify github workflows 2022-05-01 11:16:02 +08:00
e22053ce2c fix: test error 2022-05-01 10:42:11 +08:00
75 changed files with 516 additions and 920 deletions

View File

@ -1,13 +1,11 @@
name: Docker
on:
push:
branches:
- Beta
tags:
- "v*"
on: [push]
env:
REGISTRY: docker.io
IMAGE_NAME: '{{ env.DOCKERHUB_ACCOUNT }}/{{ env.DOCKERHUB_REPO }}'
jobs:
build:
runs-on: ubuntu-latest
@ -25,8 +23,6 @@ jobs:
- name: Setup Docker buildx
uses: docker/setup-buildx-action@v1
with:
version: latest
# Extract metadata (tags, labels) for Docker
# https://github.com/docker/metadata-action
@ -34,15 +30,15 @@ jobs:
id: meta
uses: docker/metadata-action@v3
with:
images: ${{ env.REGISTRY }}/${{ secrets.DOCKERHUB_ACCOUNT }}/${{secrets.DOCKERHUB_REPO}}
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- name: Log into registry
- name: Log into registry ${{ env.REGISTRY }}
if: github.event_name != 'pull_request'
uses: docker/login-action@v1
with:
registry: ${{ env.REGISTRY }}
username: ${{ secrets.DOCKER_HUB_USER }}
password: ${{ secrets.DOCKER_HUB_TOKEN }}
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
# Build and push Docker image with Buildx (don't push on PR)
# https://github.com/docker/build-push-action
@ -51,7 +47,6 @@ jobs:
uses: docker/build-push-action@v2
with:
context: .
file: ./Dockerfile
push: ${{ github.event_name != 'pull_request' }}
platforms: |
linux/386

View File

@ -1,4 +1,4 @@
name: Prerelease
name: prerelease
on:
push:
branches:
@ -31,13 +31,10 @@ jobs:
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Test
if: ${{github.ref_name=='Beta'}}
if: ${{env.GITHUB_REF_NAME=='Beta'}}
run: |
go test ./...
- name: Build
if: success()
env:
@ -49,13 +46,13 @@ jobs:
uses: andreaswilli/delete-release-assets-action@v2.0.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
tag: Prerelease-${{ github.ref_name }}
tag: ${{ env.GITHUB_REF_NAME }}
deleteOnlyFromDrafts: false
- name: Tag Repo
uses: richardsimko/update-tag@v1
with:
tag_name: Prerelease-${{ github.ref_name }}
tag_name: ${{env.GITHUB_REF_NAME}}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@ -63,8 +60,8 @@ jobs:
uses: softprops/action-gh-release@v1
if: ${{ success() }}
with:
tag: ${{ github.ref_name }}
tag_name: Prerelease-${{ github.ref_name }}
tag: ${{env.GITHUB_REF_NAME}}
tag_name: ${{env.GITHUB_REF_NAME}}
files: bin/*
prerelease: true
generate_release_notes: true

View File

@ -1,4 +1,4 @@
name: Release
name: release
on:
push:
tags:
@ -35,7 +35,7 @@ jobs:
BINDIR: bin
run: make -j releases
- name: Upload Release
- name: Upload Alpha
uses: softprops/action-gh-release@v1
if: ${{ success() && startsWith(github.ref, 'refs/tags/')}}
with:

View File

@ -1,26 +1,41 @@
FROM golang:alpine as builder
ARG TARGETOS
ARG TARGETARCH
RUN apk add --no-cache make git && \
mkdir /clash-config && \
wget -O /clash-config/Country.mmdb https://raw.githubusercontent.com/Loyalsoldier/geoip/release/Country.mmdb && \
wget -O /clash-config/geosite.dat https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat && \
wget -O /clash-config/geoip.dat https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat
COPY . /clash-src
WORKDIR /clash-src
RUN go mod download &&\
make docker &&\
mv ./bin/Clash.Meta-docker /clash
COPY . /clash-src
RUN go mod download
RUN /bin/ash -c 'set -ex && \
if [ "$TARGETARCH" == "amd64" ]; then \
GOOS=$TARGETOS GOARCH=$TARGETARCH GOAMD64=v1 make docker && \
mv ./bin/Clash.Meta-docker ./bin/clash-amd64v1 && \
GOOS=$TARGETOS GOARCH=$TARGETARCH GOAMD64=v2 make docker && \
mv ./bin/Clash.Meta-docker ./bin/clash-amd64v2 && \
GOOS=$TARGETOS GOARCH=$TARGETARCH GOAMD64=v3 make docker && \
mv ./bin/Clash.Meta-docker ./bin/clash-amd64v3 && \
ln -s clash-amd64v3 ./bin/clash-amd64v4 && \
mv check_amd64.sh ./bin/ && \
printf "#!/bin/sh\\nsh ./check_amd64.sh\\nexec ./clash-amd64v\$? \$@" > ./bin/clash && \
chmod +x ./bin/check_amd64.sh ./bin/clash; \
else \
GOOS=$TARGETOS GOARCH=$TARGETARCH make docker && \
mv ./bin/Clash.Meta-docker ./bin/clash; \
fi'
FROM alpine:latest
LABEL org.opencontainers.image.source="https://github.com/MetaCubeX/Clash.Meta"
RUN apk add --no-cache ca-certificates tzdata
VOLUME ["/root/.config/clash/"]
EXPOSE 7890/tcp
COPY --from=builder /clash-config/ /root/.config/clash/
COPY --from=builder /clash /clash
RUN chmod +x /clash
COPY --from=builder /clash-src/bin/ /
ENTRYPOINT [ "/clash" ]

View File

@ -1,11 +1,11 @@
NAME=Clash.Meta
BINDIR=bin
BRANCH=$(shell git branch --show-current)
BRANCH=$(shell git rev-parse --abbrev-ref HEAD)
ifeq ($(BRANCH),Alpha)
VERSION=alpha-$(shell git rev-parse --short HEAD)
else ifeq ($(BRANCH),Beta)
VERSION=beta-$(shell git rev-parse --short HEAD)
else ifeq ($(BRANCH),)
else ifeq ($(BRANCH),Meta)
VERSION=$(shell git describe --tags)
else
VERSION=unknown
@ -52,7 +52,7 @@ all:linux-amd64 linux-arm64\
windows-amd64 windows-arm64\
docker:
GOAMD64=v3 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@
$(GOBUILD) -o $(BINDIR)/$(NAME)-$@
darwin-amd64v3:
GOARCH=amd64 GOOS=darwin GOAMD64=v3 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@

View File

@ -2,10 +2,12 @@ package outbound
import (
"context"
"crypto/sha1"
"encoding/hex"
"encoding/json"
"errors"
"github.com/gofrs/uuid"
"net"
"regexp"
"github.com/Dreamacro/clash/component/dialer"
C "github.com/Dreamacro/clash/constant"
@ -18,7 +20,6 @@ type Base struct {
tp C.AdapterType
udp bool
rmark int
id string
}
// Name implements C.ProxyAdapter
@ -26,20 +27,6 @@ func (b *Base) Name() string {
return b.name
}
// Id implements C.ProxyAdapter
func (b *Base) Id() string {
if b.id == "" {
id, err := uuid.NewV6()
if err != nil {
b.id = b.name
} else {
b.id = id.String()
}
}
return b.id
}
// Type implements C.ProxyAdapter
func (b *Base) Type() C.AdapterType {
return b.tp
@ -74,7 +61,6 @@ func (b *Base) SupportUDP() bool {
func (b *Base) MarshalJSON() ([]byte, error) {
return json.Marshal(map[string]string{
"type": b.Type().String(),
"id": b.Id(),
})
}
@ -163,3 +149,28 @@ func (c *packetConn) AppendToChains(a C.ProxyAdapter) {
func newPacketConn(pc net.PacketConn, a C.ProxyAdapter) C.PacketConn {
return &packetConn{pc, []string{a.Name()}}
}
func uuidMap(str string) string {
match, _ := regexp.MatchString(`[\da-f]{8}(-[\da-f]{4}){3}-[\da-f]{12}$`, str)
if !match {
var Nil [16]byte
h := sha1.New()
h.Write(Nil[:])
h.Write([]byte(str))
u := h.Sum(nil)[:16]
u[6] = (u[6] & 0x0f) | (5 << 4)
u[8] = u[8]&(0xff>>2) | (0x02 << 6)
buf := make([]byte, 36)
hex.Encode(buf[0:8], u[0:4])
buf[8] = '-'
hex.Encode(buf[9:13], u[4:6])
buf[13] = '-'
hex.Encode(buf[14:18], u[6:8])
buf[18] = '-'
hex.Encode(buf[19:23], u[8:10])
buf[23] = '-'
hex.Encode(buf[24:], u[10:])
return string(buf)
}
return str
}

View File

@ -56,3 +56,13 @@ func NewCompatible() *Direct {
},
}
}
func NewPass() *Direct {
return &Direct{
Base: &Base{
name: "PASS",
tp: C.Pass,
udp: true,
},
}
}

View File

@ -34,16 +34,6 @@ func NewReject() *Reject {
}
}
func NewPass() *Reject {
return &Reject{
Base: &Base{
name: "PASS",
tp: C.Pass,
udp: true,
},
}
}
type nopConn struct{}
func (rw *nopConn) Read(b []byte) (int, error) {

View File

@ -99,13 +99,7 @@ func (s *Snell) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o
tcpKeepAlive(c)
c = streamConn(c, streamOption{s.psk, s.version, s.addr, s.obfsOption})
err = snell.WriteUDPHeader(c, s.version)
if err != nil {
return nil, err
}
pc := snell.PacketConn(c)
return newPacketConn(pc, s), nil
return s.ListenPacketOnStreamConn(c, metadata)
}
// ListenPacketOnStreamConn implements C.ProxyAdapter

View File

@ -13,6 +13,8 @@ import (
"github.com/Dreamacro/clash/transport/gun"
"github.com/Dreamacro/clash/transport/trojan"
"github.com/Dreamacro/clash/transport/vless"
"golang.org/x/net/http2"
)
type Trojan struct {
@ -23,7 +25,7 @@ type Trojan struct {
// for gun mux
gunTLSConfig *tls.Config
gunConfig *gun.Config
transport *gun.TransportWrap
transport *http2.Transport
}
type TrojanOption struct {
@ -159,13 +161,7 @@ func (t *Trojan) ListenPacketContext(ctx context.Context, metadata *C.Metadata,
}
}
err = t.instance.WriteHeader(c, trojan.CommandUDP, serializesSocksAddr(metadata))
if err != nil {
return nil, err
}
pc := t.instance.PacketConn(c)
return newPacketConn(pc, t), err
return t.ListenPacketOnStreamConn(c, metadata)
}
// ListenPacketOnStreamConn implements C.ProxyAdapter

View File

@ -18,6 +18,8 @@ import (
"github.com/Dreamacro/clash/transport/gun"
"github.com/Dreamacro/clash/transport/vless"
"github.com/Dreamacro/clash/transport/vmess"
"golang.org/x/net/http2"
)
const (
@ -33,7 +35,7 @@ type Vless struct {
// for gun mux
gunTLSConfig *tls.Config
gunConfig *gun.Config
transport *gun.TransportWrap
transport *http2.Transport
}
type VlessOption struct {
@ -394,7 +396,7 @@ func NewVless(option VlessOption) (*Vless, error) {
}
}
client, err := vless.NewClient(option.UUID, addons, option.FlowShow)
client, err := vless.NewClient(uuidMap(option.UUID), addons, option.FlowShow)
if err != nil {
return nil, err
}

View File

@ -15,6 +15,8 @@ import (
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/transport/gun"
"github.com/Dreamacro/clash/transport/vmess"
"golang.org/x/net/http2"
)
type Vmess struct {
@ -25,7 +27,7 @@ type Vmess struct {
// for gun mux
gunTLSConfig *tls.Config
gunConfig *gun.Config
transport *gun.TransportWrap
transport *http2.Transport
}
type VmessOption struct {
@ -274,7 +276,7 @@ func (v *Vmess) SupportUOT() bool {
func NewVmess(option VmessOption) (*Vmess, error) {
security := strings.ToLower(option.Cipher)
client, err := vmess.NewClient(vmess.Config{
UUID: option.UUID,
UUID: uuidMap(option.UUID),
AlterID: uint16(option.AlterID),
Security: security,
HostName: option.Server,

View File

@ -3,6 +3,10 @@ package outboundgroup
import (
"context"
"encoding/json"
"github.com/Dreamacro/clash/log"
"go.uber.org/atomic"
"time"
"github.com/Dreamacro/clash/adapter/outbound"
"github.com/Dreamacro/clash/component/dialer"
C "github.com/Dreamacro/clash/constant"
@ -11,7 +15,9 @@ import (
type Fallback struct {
*GroupBase
disableUDP bool
disableUDP bool
failedTimes *atomic.Int32
failedTime *atomic.Int64
}
func (f *Fallback) Now() string {
@ -25,7 +31,8 @@ func (f *Fallback) DialContext(ctx context.Context, metadata *C.Metadata, opts .
c, err := proxy.DialContext(ctx, metadata, f.Base.DialOptions(opts...)...)
if err == nil {
c.AppendToChains(f)
f.onDialSuccess()
f.failedTimes.Store(-1)
f.failedTime.Store(-1)
} else {
f.onDialFailed()
}
@ -39,11 +46,41 @@ func (f *Fallback) ListenPacketContext(ctx context.Context, metadata *C.Metadata
pc, err := proxy.ListenPacketContext(ctx, metadata, f.Base.DialOptions(opts...)...)
if err == nil {
pc.AppendToChains(f)
f.failedTimes.Store(-1)
f.failedTime.Store(-1)
} else {
f.onDialFailed()
}
return pc, err
}
func (f *Fallback) onDialFailed() {
if f.failedTime.Load() == -1 {
log.Warnln("%s first failed", f.Name())
now := time.Now().UnixMilli()
f.failedTime.Store(now)
f.failedTimes.Store(1)
} else {
if f.failedTime.Load()-time.Now().UnixMilli() > 5*time.Second.Milliseconds() {
f.failedTimes.Store(-1)
f.failedTime.Store(-1)
} else {
failedCount := f.failedTimes.Inc()
log.Warnln("%s failed count: %d", f.Name(), failedCount)
if failedCount >= 5 {
log.Warnln("because %s failed multiple times, active health check", f.Name())
for _, proxyProvider := range f.providers {
go proxyProvider.HealthCheck()
}
f.failedTimes.Store(-1)
f.failedTime.Store(-1)
}
}
}
}
// SupportUDP implements C.ProxyAdapter
func (f *Fallback) SupportUDP() bool {
if f.disableUDP {
@ -96,6 +133,8 @@ func NewFallback(option *GroupCommonOption, providers []provider.ProxyProvider)
option.Filter,
providers,
}),
disableUDP: option.DisableUDP,
disableUDP: option.DisableUDP,
failedTimes: atomic.NewInt32(-1),
failedTime: atomic.NewInt64(-1),
}
}

View File

@ -5,24 +5,17 @@ import (
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/constant/provider"
types "github.com/Dreamacro/clash/constant/provider"
"github.com/Dreamacro/clash/log"
"github.com/Dreamacro/clash/tunnel"
"github.com/dlclark/regexp2"
"go.uber.org/atomic"
"sync"
"time"
)
type GroupBase struct {
*outbound.Base
filter *regexp2.Regexp
providers []provider.ProxyProvider
versions sync.Map // map[string]uint
proxies sync.Map // map[string][]C.Proxy
failedTestMux sync.Mutex
failedTimes int
failedTime time.Time
failedTesting *atomic.Bool
filter *regexp2.Regexp
providers []provider.ProxyProvider
versions sync.Map // map[string]uint
proxies sync.Map // map[string][]C.Proxy
}
type GroupBaseOption struct {
@ -37,10 +30,9 @@ func NewGroupBase(opt GroupBaseOption) *GroupBase {
filter = regexp2.MustCompile(opt.filter, 0)
}
return &GroupBase{
Base: outbound.NewBase(opt.BaseOption),
filter: filter,
providers: opt.providers,
failedTesting: atomic.NewBool(false),
Base: outbound.NewBase(opt.BaseOption),
filter: filter,
providers: opt.providers,
}
}
@ -59,7 +51,7 @@ func (gb *GroupBase) GetProxies(touch bool) []C.Proxy {
}
return proxies
}
//TODO("Touch Version 没变的")
for _, pd := range gb.providers {
if pd.VehicleType() == types.Compatible {
if touch {
@ -104,61 +96,3 @@ func (gb *GroupBase) GetProxies(touch bool) []C.Proxy {
}
return proxies
}
func (gb *GroupBase) onDialFailed() {
if gb.failedTesting.Load() {
return
}
go func() {
gb.failedTestMux.Lock()
defer gb.failedTestMux.Unlock()
gb.failedTimes++
if gb.failedTimes == 1 {
log.Warnln("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)
if gb.failedTimes >= gb.maxFailedTimes() {
gb.failedTesting.Store(true)
log.Warnln("because %s failed multiple times, active health check", gb.Name())
wg := sync.WaitGroup{}
for _, proxyProvider := range gb.providers {
wg.Add(1)
proxyProvider := proxyProvider
go func() {
defer wg.Done()
proxyProvider.HealthCheck()
}()
}
wg.Wait()
gb.failedTesting.Store(false)
gb.failedTimes = 0
}
}
}()
}
func (gb *GroupBase) failedIntervalTime() int64 {
return 5 * time.Second.Milliseconds()
}
func (gb *GroupBase) onDialSuccess() {
if !gb.failedTesting.Load() {
gb.failedTimes = 0
}
}
func (gb *GroupBase) maxFailedTimes() int {
return 5
}
func (gb *GroupBase) failedTimeoutInterval() time.Duration {
return 5 * time.Second
}

View File

@ -5,9 +5,7 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/Dreamacro/clash/common/cache"
"net"
"time"
"github.com/Dreamacro/clash/adapter/outbound"
"github.com/Dreamacro/clash/common/murmur3"
@ -38,10 +36,6 @@ func parseStrategy(config map[string]any) string {
}
func getKey(metadata *C.Metadata) string {
if metadata == nil {
return ""
}
if metadata.Host != "" {
// ip host
if ip := net.ParseIP(metadata.Host); ip != nil {
@ -60,16 +54,6 @@ func getKey(metadata *C.Metadata) string {
return metadata.DstIP.String()
}
func getKeyWithSrcAndDst(metadata *C.Metadata) string {
dst := getKey(metadata)
src := ""
if metadata != nil {
src = metadata.SrcIP.String()
}
return fmt.Sprintf("%s%s", src, dst)
}
func jumpHash(key uint64, buckets int32) int32 {
var b, j int64
@ -87,9 +71,6 @@ func (lb *LoadBalance) DialContext(ctx context.Context, metadata *C.Metadata, op
defer func() {
if err == nil {
c.AppendToChains(lb)
lb.onDialSuccess()
} else {
lb.onDialFailed()
}
}()
@ -149,35 +130,6 @@ func strategyConsistentHashing() strategyFn {
}
}
func strategyStickySessions() strategyFn {
ttl := time.Minute * 10
c := cache.New[uint64, int](1 * time.Second)
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)))
}
for i := 0; i < length; i++ {
nowIdx := (idx + 1) % length
proxy := proxies[nowIdx]
if proxy.Alive() {
if nowIdx != idx {
c.Put(key, idx, -1)
c.Put(key, nowIdx, ttl)
}
return proxy
}
}
return proxies[0]
}
}
// Unwrap implements C.ProxyAdapter
func (lb *LoadBalance) Unwrap(metadata *C.Metadata) C.Proxy {
proxies := lb.GetProxies(true)
@ -186,7 +138,7 @@ func (lb *LoadBalance) Unwrap(metadata *C.Metadata) C.Proxy {
// MarshalJSON implements C.ProxyAdapter
func (lb *LoadBalance) MarshalJSON() ([]byte, error) {
var all []string
all := []string{}
for _, proxy := range lb.GetProxies(false) {
all = append(all, proxy.Name())
}
@ -203,8 +155,6 @@ func NewLoadBalance(option *GroupCommonOption, providers []provider.ProxyProvide
strategyFn = strategyConsistentHashing()
case "round-robin":
strategyFn = strategyRoundRobin()
case "sticky-sessions":
strategyFn = strategyStickySessions()
default:
return nil, fmt.Errorf("%w: %s", errStrategy, strategy)
}

View File

@ -170,11 +170,6 @@ func (r *Relay) proxies(metadata *C.Metadata, touch bool) ([]C.Proxy, []C.Proxy)
return targetProxies, chainProxies
}
func (r *Relay) Addr() string {
proxies, _ := r.proxies(nil, true)
return proxies[len(proxies)-1].Addr()
}
func NewRelay(option *GroupCommonOption, providers []provider.ProxyProvider) *Relay {
return &Relay{
GroupBase: NewGroupBase(GroupBaseOption{

View File

@ -3,6 +3,8 @@ package outboundgroup
import (
"context"
"encoding/json"
"github.com/Dreamacro/clash/log"
"go.uber.org/atomic"
"time"
"github.com/Dreamacro/clash/adapter/outbound"
@ -22,10 +24,12 @@ func urlTestWithTolerance(tolerance uint16) urlTestOption {
type URLTest struct {
*GroupBase
tolerance uint16
disableUDP bool
fastNode C.Proxy
fastSingle *singledo.Single[C.Proxy]
tolerance uint16
disableUDP bool
fastNode C.Proxy
fastSingle *singledo.Single[C.Proxy]
failedTimes *atomic.Int32
failedTime *atomic.Int64
}
func (u *URLTest) Now() string {
@ -37,7 +41,8 @@ func (u *URLTest) DialContext(ctx context.Context, metadata *C.Metadata, opts ..
c, err = u.fast(true).DialContext(ctx, metadata, u.Base.DialOptions(opts...)...)
if err == nil {
c.AppendToChains(u)
u.onDialSuccess()
u.failedTimes.Store(-1)
u.failedTime.Store(-1)
} else {
u.onDialFailed()
}
@ -49,8 +54,11 @@ func (u *URLTest) ListenPacketContext(ctx context.Context, metadata *C.Metadata,
pc, err := u.fast(true).ListenPacketContext(ctx, metadata, u.Base.DialOptions(opts...)...)
if err == nil {
pc.AppendToChains(u)
u.failedTimes.Store(-1)
u.failedTime.Store(-1)
} else {
u.onDialFailed()
}
return pc, err
}
@ -115,6 +123,32 @@ func (u *URLTest) MarshalJSON() ([]byte, error) {
})
}
func (u *URLTest) onDialFailed() {
if u.failedTime.Load() == -1 {
log.Warnln("%s first failed", u.Name())
now := time.Now().UnixMilli()
u.failedTime.Store(now)
u.failedTimes.Store(1)
} else {
if u.failedTime.Load()-time.Now().UnixMilli() > 5*1000 {
u.failedTimes.Store(-1)
u.failedTime.Store(-1)
} else {
failedCount := u.failedTimes.Inc()
log.Warnln("%s failed count: %d", u.Name(), failedCount)
if failedCount >= 5 {
log.Warnln("because %s failed multiple times, active health check", u.Name())
for _, proxyProvider := range u.providers {
go proxyProvider.HealthCheck()
}
u.failedTimes.Store(-1)
u.failedTime.Store(-1)
}
}
}
}
func parseURLTestOption(config map[string]any) []urlTestOption {
opts := []urlTestOption{}
@ -137,12 +171,13 @@ func NewURLTest(option *GroupCommonOption, providers []provider.ProxyProvider, o
Interface: option.Interface,
RoutingMark: option.RoutingMark,
},
option.Filter,
providers,
}),
fastSingle: singledo.NewSingle[C.Proxy](time.Second * 10),
disableUDP: option.DisableUDP,
fastSingle: singledo.NewSingle[C.Proxy](time.Second * 10),
disableUDP: option.DisableUDP,
failedTimes: atomic.NewInt32(-1),
failedTime: atomic.NewInt64(-1),
}
for _, option := range options {

View File

@ -43,14 +43,6 @@ func (f *fetcher) Initial() (any, error) {
err error
isLocal bool
)
defer func() {
// pull proxies automatically
if f.ticker != nil {
go f.pullLoop()
}
}()
if stat, fErr := os.Stat(f.vehicle.Path()); fErr == nil {
buf, err = os.ReadFile(f.vehicle.Path())
modTime := stat.ModTime()
@ -92,6 +84,11 @@ func (f *fetcher) Initial() (any, error) {
f.hash = md5.Sum(buf)
// pull proxies automatically
if f.ticker != nil {
go f.pullLoop()
}
return proxies, nil
}

View File

@ -73,7 +73,7 @@ func (h *HTTPVehicle) Read() ([]byte, error) {
transport := &http.Transport{
// from http.DefaultTransport
MaxIdleConns: 100,
IdleConnTimeout: 30 * time.Second,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
DialContext: func(ctx context.Context, network, address string) (net.Conn, error) {

View File

@ -159,19 +159,9 @@ func (d *Decoder) decodeSlice(name string, data any, val reflect.Value) error {
for valSlice.Len() <= i {
valSlice = reflect.Append(valSlice, reflect.Zero(valElemType))
}
fieldName := fmt.Sprintf("%s[%d]", name, i)
if currentData == nil {
// in weakly type mode, null will convert to zero value
if d.option.WeaklyTypedInput {
continue
}
// in non-weakly type mode, null will convert to nil if element's zero value is nil, otherwise return an error
if elemKind := valElemType.Kind(); elemKind == reflect.Map || elemKind == reflect.Slice {
continue
}
return fmt.Errorf("'%s' can not be null", fieldName)
}
currentField := valSlice.Index(i)
fieldName := fmt.Sprintf("%s[%d]", name, i)
if err := d.decode(fieldName, currentData, currentField); err != nil {
return err
}

View File

@ -1,16 +0,0 @@
package utils
import (
"github.com/gofrs/uuid"
)
var uuidNamespace, _ = uuid.FromString("00000000-0000-0000-0000-000000000000")
// UUIDMap https://github.com/XTLS/Xray-core/issues/158#issue-783294090
func UUIDMap(str string) (uuid.UUID, error) {
u, err := uuid.FromString(str)
if err != nil {
return uuid.NewV5(uuidNamespace, str), nil
}
return u, nil
}

View File

@ -1,74 +0,0 @@
package utils
import (
"github.com/gofrs/uuid"
"reflect"
"testing"
)
func TestUUIDMap(t *testing.T) {
type args struct {
str string
}
tests := []struct {
name string
args args
want uuid.UUID
wantErr bool
}{
{
name: "uuid-test-1",
args: args{
str: "82410302-039e-41b6-98b0-d964084b4170",
},
want: uuid.FromStringOrNil("82410302-039e-41b6-98b0-d964084b4170"),
wantErr: false,
},
{
name: "uuid-test-2",
args: args{
str: "88c502e6-d7eb-4c8e-8259-94cb13d83c77",
},
want: uuid.FromStringOrNil("88c502e6-d7eb-4c8e-8259-94cb13d83c77"),
wantErr: false,
},
{
name: "uuid-map-1",
args: args{
str: "123456",
},
want: uuid.FromStringOrNil("f8598425-92f2-5508-a071-4fc67f9040ac"),
wantErr: false,
},
// GENERATED BY 'xray uuid -i'
{
name: "uuid-map-2",
args: args{
str: "a9dk23bz0",
},
want: uuid.FromStringOrNil("c91481b6-fc0f-5d9e-b166-5ddf07b9c3c5"),
wantErr: false,
},
{
name: "uuid-map-2",
args: args{
str: "中文123",
},
want: uuid.FromStringOrNil("145c544c-2229-59e5-8dbb-3f33b7610d26"),
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := UUIDMap(tt.args.str)
if (err != nil) != tt.wantErr {
t.Errorf("UUIDMap() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("UUIDMap() got = %v, want %v", got, tt.want)
}
})
}
}

View File

@ -253,7 +253,7 @@ func concurrentDialContext(ctx context.Context, network string, ips []netip.Addr
}
}
return nil, fmt.Errorf("all ips %v tcp shake hands failed", ips)
return nil, errors.New("all ip tcp shake hands failed")
}
func singleDialContext(ctx context.Context, network string, address string, opt *option) (net.Conn, error) {

View File

@ -33,10 +33,9 @@ func domainToMatcher(domain *Domain) (strmatcher.Matcher, error) {
type DomainMatcher struct {
matchers strmatcher.IndexMatcher
not bool
}
func NewMphMatcherGroup(domains []*Domain, not bool) (*DomainMatcher, error) {
func NewMphMatcherGroup(domains []*Domain) (*DomainMatcher, error) {
g := strmatcher.NewMphMatcherGroup()
for _, d := range domains {
matcherType, f := matcherTypeMap[d.Type]
@ -51,12 +50,11 @@ func NewMphMatcherGroup(domains []*Domain, not bool) (*DomainMatcher, error) {
g.Build()
return &DomainMatcher{
matchers: g,
not: not,
}, nil
}
// NewDomainMatcher new domain matcher.
func NewDomainMatcher(domains []*Domain, not bool) (*DomainMatcher, error) {
func NewDomainMatcher(domains []*Domain) (*DomainMatcher, error) {
g := new(strmatcher.MatcherGroup)
for _, d := range domains {
m, err := domainToMatcher(d)
@ -68,16 +66,11 @@ func NewDomainMatcher(domains []*Domain, not bool) (*DomainMatcher, error) {
return &DomainMatcher{
matchers: g,
not: not,
}, nil
}
func (m *DomainMatcher) ApplyDomain(domain string) bool {
isMatched := len(m.matchers.Match(strings.ToLower(domain))) > 0
if m.not {
isMatched = !isMatched
}
return isMatched
return len(m.matchers.Match(strings.ToLower(domain))) > 0
}
// CIDRList is an alias of []*CIDR to provide sort.Interface.

View File

@ -1,9 +1,9 @@
package geodata
import (
"fmt"
"github.com/Dreamacro/clash/component/geodata/router"
C "github.com/Dreamacro/clash/constant"
"strings"
)
var geoLoaderName = "memconservative"
@ -35,16 +35,6 @@ func Verify(name string) bool {
}
func LoadGeoSiteMatcher(countryCode string) (*router.DomainMatcher, int, error) {
if len(countryCode) == 0 {
return nil, 0, fmt.Errorf("country code could not be empty")
}
not := false
if countryCode[0] == '!' {
not = true
countryCode = countryCode[1:]
}
geoLoader, err := GetGeoDataLoader(geoLoaderName)
if err != nil {
return nil, 0, err
@ -60,7 +50,7 @@ func LoadGeoSiteMatcher(countryCode string) (*router.DomainMatcher, int, error)
matcher, err := router.NewDomainMatcher(domains)
mphminimal perfect hash algorithm
*/
matcher, err := router.NewMphMatcherGroup(domains, not)
matcher, err := router.NewMphMatcherGroup(domains)
if err != nil {
return nil, 0, err
}
@ -69,21 +59,12 @@ func LoadGeoSiteMatcher(countryCode string) (*router.DomainMatcher, int, error)
}
func LoadGeoIPMatcher(country string) (*router.GeoIPMatcher, int, error) {
if len(country) == 0 {
return nil, 0, fmt.Errorf("country code could not be empty")
}
geoLoader, err := GetGeoDataLoader(geoLoaderName)
if err != nil {
return nil, 0, err
}
not := false
if country[0] == '!' {
not = true
country = country[1:]
}
records, err := geoLoader.LoadGeoIP(country)
records, err := geoLoader.LoadGeoIP(strings.ReplaceAll(country, "!", ""))
if err != nil {
return nil, 0, err
}
@ -91,7 +72,7 @@ func LoadGeoIPMatcher(country string) (*router.GeoIPMatcher, int, error) {
geoIP := &router.GeoIP{
CountryCode: country,
Cidr: records,
ReverseMatch: not,
ReverseMatch: strings.Contains(country, "!"),
}
matcher, err := router.NewGeoIPMatcher(geoIP)

View File

@ -2,7 +2,6 @@ package sniffer
import (
"errors"
"github.com/Dreamacro/clash/constant/sniffer"
"net"
"net/netip"
"strconv"
@ -20,7 +19,6 @@ import (
var (
ErrorUnsupportedSniffer = errors.New("unsupported sniffer")
ErrorSniffFailed = errors.New("all sniffer failed")
ErrNoClue = errors.New("not enough information for making a decision")
)
var Dispatcher SnifferDispatcher
@ -29,7 +27,7 @@ type (
SnifferDispatcher struct {
enable bool
sniffers []sniffer.Sniffer
sniffers []C.Sniffer
foreDomain *trie.DomainTrie[bool]
skipSNI *trie.DomainTrie[bool]
@ -86,6 +84,7 @@ func (sd *SnifferDispatcher) replaceDomain(metadata *C.Metadata, host string) {
metadata.Host = host
metadata.DNSMode = C.DNSMapping
resolver.InsertHostByIP(metadata.DstIP, host)
metadata.DstIP = netip.Addr{}
}
func (sd *SnifferDispatcher) Enable() bool {
@ -95,16 +94,16 @@ func (sd *SnifferDispatcher) Enable() bool {
func (sd *SnifferDispatcher) sniffDomain(conn *CN.BufferedConn, metadata *C.Metadata) (string, error) {
for _, sniffer := range sd.sniffers {
if sniffer.SupportNetwork() == C.TCP {
_ = conn.SetReadDeadline(time.Now().Add(3 * time.Second))
conn.SetReadDeadline(time.Now().Add(3 * time.Second))
_, err := conn.Peek(1)
_ = conn.SetReadDeadline(time.Time{})
conn.SetReadDeadline(time.Time{})
if err != nil {
_, ok := err.(*net.OpError)
if ok {
log.Errorln("[Sniffer] [%s] may not have any sent data, Consider adding skip", metadata.DstIP.String())
_ = conn.Close()
log.Errorln("[Sniffer] [%s] Maybe read timeout, Consider adding skip", metadata.DstIP.String())
conn.Close()
}
log.Errorln("[Sniffer] %v", err)
return "", err
}
@ -117,13 +116,7 @@ func (sd *SnifferDispatcher) sniffDomain(conn *CN.BufferedConn, metadata *C.Meta
host, err := sniffer.SniffTCP(bytes)
if err != nil {
//log.Debugln("[Sniffer] [%s] Sniff data failed %s", sniffer.Protocol(), metadata.DstIP)
continue
}
_, err = netip.ParseAddr(host)
if err == nil {
//log.Debugln("[Sniffer] [%s] Sniff data failed %s", sniffer.Protocol(), metadata.DstIP)
log.Debugln("[Sniffer] [%s] Sniff data failed %s", sniffer.Protocol(), metadata.DstIP)
continue
}
@ -142,7 +135,7 @@ func NewCloseSnifferDispatcher() (*SnifferDispatcher, error) {
return &dispatcher, nil
}
func NewSnifferDispatcher(needSniffer []sniffer.Type, forceDomain *trie.DomainTrie[bool],
func NewSnifferDispatcher(needSniffer []C.SnifferType, forceDomain *trie.DomainTrie[bool],
skipSNI *trie.DomainTrie[bool], ports *[]utils.Range[uint16]) (*SnifferDispatcher, error) {
dispatcher := SnifferDispatcher{
enable: true,
@ -164,12 +157,10 @@ func NewSnifferDispatcher(needSniffer []sniffer.Type, forceDomain *trie.DomainTr
return &dispatcher, nil
}
func NewSniffer(name sniffer.Type) (sniffer.Sniffer, error) {
func NewSniffer(name C.SnifferType) (C.Sniffer, error) {
switch name {
case sniffer.TLS:
case C.TLS:
return &TLSSniffer{}, nil
case sniffer.HTTP:
return &HTTPSniffer{}, nil
default:
return nil, ErrorUnsupportedSniffer
}

View File

@ -1,100 +0,0 @@
package sniffer
import (
"bytes"
"errors"
C "github.com/Dreamacro/clash/constant"
"net"
"strings"
)
var (
// refer to https://pkg.go.dev/net/http@master#pkg-constants
methods = [...]string{"get", "post", "head", "put", "delete", "options", "connect", "patch", "trace"}
errNotHTTPMethod = errors.New("not an HTTP method")
)
type version byte
const (
HTTP1 version = iota
HTTP2
)
type HTTPSniffer struct {
version version
host string
}
func (http *HTTPSniffer) Protocol() string {
switch http.version {
case HTTP1:
return "http1"
case HTTP2:
return "http2"
default:
return "unknown"
}
}
func (http *HTTPSniffer) SupportNetwork() C.NetWork {
return C.TCP
}
func (http *HTTPSniffer) SniffTCP(bytes []byte) (string, error) {
domain, err := SniffHTTP(bytes)
if err == nil {
return *domain, nil
} else {
return "", err
}
}
func beginWithHTTPMethod(b []byte) error {
for _, m := range &methods {
if len(b) >= len(m) && strings.EqualFold(string(b[:len(m)]), m) {
return nil
}
if len(b) < len(m) {
return ErrNoClue
}
}
return errNotHTTPMethod
}
func SniffHTTP(b []byte) (*string, error) {
if err := beginWithHTTPMethod(b); err != nil {
return nil, err
}
_ = &HTTPSniffer{
version: HTTP1,
}
headers := bytes.Split(b, []byte{'\n'})
for i := 1; i < len(headers); i++ {
header := headers[i]
if len(header) == 0 {
break
}
parts := bytes.SplitN(header, []byte{':'}, 2)
if len(parts) != 2 {
continue
}
key := strings.ToLower(string(parts[0]))
if key == "host" {
rawHost := strings.ToLower(string(bytes.TrimSpace(parts[1])))
host, _, err := net.SplitHostPort(rawHost)
if err != nil {
if addrError, ok := err.(*net.AddrError); ok && strings.Contains(addrError.Err, "missing port") {
host = rawHost
} else {
return nil, err
}
}
return &host, nil
}
}
return nil, ErrNoClue
}

View File

@ -1,3 +0,0 @@
package sniffer
//TODO

View File

@ -11,6 +11,7 @@ import (
var (
errNotTLS = errors.New("not TLS header")
errNotClientHello = errors.New("not client hello")
ErrNoClue = errors.New("not enough information for making a decision")
)
type TLSSniffer struct {

View File

@ -4,7 +4,6 @@ import (
"container/list"
"errors"
"fmt"
"github.com/Dreamacro/clash/constant/sniffer"
"github.com/Dreamacro/clash/listener/tun/ipstack/commons"
"net"
"net/netip"
@ -31,7 +30,6 @@ import (
"github.com/Dreamacro/clash/component/trie"
C "github.com/Dreamacro/clash/constant"
providerTypes "github.com/Dreamacro/clash/constant/provider"
snifferTypes "github.com/Dreamacro/clash/constant/sniffer"
"github.com/Dreamacro/clash/dns"
"github.com/Dreamacro/clash/log"
T "github.com/Dreamacro/clash/tunnel"
@ -52,7 +50,6 @@ type General struct {
GeodataMode bool `json:"geodata-mode"`
GeodataLoader string `json:"geodata-loader"`
TCPConcurrent bool `json:"tcp-concurrent"`
Tun Tun `json:"tun"`
}
// Inbound config
@ -99,6 +96,12 @@ type FallbackFilter struct {
GeoSite []*router.DomainMatcher `yaml:"geosite"`
}
var (
GroupsList = list.New()
ProxiesList = list.New()
ParsingProxiesCallback func(groupsList *list.List, proxiesList *list.List)
)
// Profile config
type Profile struct {
StoreSelected bool `yaml:"store-selected"`
@ -124,10 +127,11 @@ type IPTables struct {
type Sniffer struct {
Enable bool
Sniffers []sniffer.Type
Force bool
Sniffers []C.SnifferType
Reverses *trie.DomainTrie[bool]
ForceDomain *trie.DomainTrie[bool]
SkipDomain *trie.DomainTrie[bool]
SkipSNI *trie.DomainTrie[bool]
Ports *[]utils.Range[uint16]
}
@ -206,7 +210,7 @@ type RawConfig struct {
GeodataLoader string `yaml:"geodata-loader"`
TCPConcurrent bool `yaml:"tcp-concurrent" json:"tcp-concurrent"`
Sniffer RawSniffer `yaml:"sniffer"`
Sniffer SnifferRaw `yaml:"sniffer"`
ProxyProvider map[string]map[string]any `yaml:"proxy-providers"`
RuleProvider map[string]map[string]any `yaml:"rule-providers"`
Hosts map[string]string `yaml:"hosts"`
@ -220,11 +224,13 @@ type RawConfig struct {
Rule []string `yaml:"rules"`
}
type RawSniffer struct {
type SnifferRaw struct {
Enable bool `yaml:"enable" json:"enable"`
Sniffing []string `yaml:"sniffing" json:"sniffing"`
Force bool `yaml:"force" json:"force"`
Reverse []string `yaml:"reverses" json:"reverses"`
ForceDomain []string `yaml:"force-domain" json:"force-domain"`
SkipDomain []string `yaml:"skip-domain" json:"skip-domain"`
SkipSNI []string `yaml:"skip-sni" json:"skip-sni"`
Ports []string `yaml:"port-whitelist" json:"port-whitelist"`
}
@ -257,10 +263,10 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) {
Tun: RawTun{
Enable: false,
Device: "",
AutoDetectInterface: true,
Stack: C.TunGvisor,
DNSHijack: []string{"0.0.0.0:53"}, // default hijack all dns query
AutoRoute: false,
AutoDetectInterface: false,
AutoRoute: true,
},
IPTables: IPTables{
Enable: false,
@ -294,11 +300,13 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) {
"www.msftconnecttest.com",
},
},
Sniffer: RawSniffer{
Sniffer: SnifferRaw{
Enable: false,
Force: false,
Sniffing: []string{},
Reverse: []string{},
ForceDomain: []string{},
SkipDomain: []string{},
SkipSNI: []string{},
Ports: []string{},
},
Profile: Profile{
@ -420,8 +428,8 @@ func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[
providersConfig := cfg.ProxyProvider
var proxyList []string
proxiesList := list.New()
groupsList := list.New()
_proxiesList := list.New()
_groupsList := list.New()
proxies["DIRECT"] = adapter.NewProxy(outbound.NewDirect())
proxies["REJECT"] = adapter.NewProxy(outbound.NewReject())
@ -441,7 +449,7 @@ func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[
}
proxies[proxy.Name()] = proxy
proxyList = append(proxyList, proxy.Name())
proxiesList.PushBack(mapping)
_proxiesList.PushBack(mapping)
}
// keep the original order of ProxyGroups in config file
@ -451,7 +459,7 @@ func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[
return nil, nil, fmt.Errorf("proxy group %d: missing name", idx)
}
proxyList = append(proxyList, groupName)
groupsList.PushBack(mapping)
_groupsList.PushBack(mapping)
}
// check if any loop exists and sort the ProxyGroups
@ -506,7 +514,12 @@ func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[
[]providerTypes.ProxyProvider{pd},
)
proxies["GLOBAL"] = adapter.NewProxy(global)
ProxiesList = _proxiesList
GroupsList = _groupsList
if ParsingProxiesCallback != nil {
// refresh tray menu
go ParsingProxiesCallback(GroupsList, ProxiesList)
}
return proxies, providersMap, nil
}
@ -526,6 +539,7 @@ func parseRules(cfg *RawConfig, proxies map[string]C.Proxy) ([]C.Rule, map[strin
var rules []C.Rule
rulesConfig := cfg.Rule
mode := cfg.Mode
// parse rules
for idx, line := range rulesConfig {
@ -537,6 +551,10 @@ func parseRules(cfg *RawConfig, proxies map[string]C.Proxy) ([]C.Rule, map[strin
ruleName = strings.ToUpper(rule[0])
)
if mode == T.Script && ruleName != "GEOSITE" {
continue
}
l := len(rule)
if ruleName == "NOT" || ruleName == "OR" || ruleName == "AND" {
@ -902,9 +920,10 @@ func parseTun(rawTun RawTun, general *General) (*Tun, error) {
}, nil
}
func parseSniffer(snifferRaw RawSniffer) (*Sniffer, error) {
func parseSniffer(snifferRaw SnifferRaw) (*Sniffer, error) {
sniffer := &Sniffer{
Enable: snifferRaw.Enable,
Force: snifferRaw.Force,
}
var ports []utils.Range[uint16]
@ -935,11 +954,11 @@ func parseSniffer(snifferRaw RawSniffer) (*Sniffer, error) {
sniffer.Ports = &ports
loadSniffer := make(map[snifferTypes.Type]struct{})
loadSniffer := make(map[C.SnifferType]struct{})
for _, snifferName := range snifferRaw.Sniffing {
find := false
for _, snifferType := range snifferTypes.List {
for _, snifferType := range C.SnifferList {
if snifferType.String() == strings.ToUpper(snifferName) {
find = true
loadSniffer[snifferType] = struct{}{}
@ -954,6 +973,7 @@ func parseSniffer(snifferRaw RawSniffer) (*Sniffer, error) {
for st := range loadSniffer {
sniffer.Sniffers = append(sniffer.Sniffers, st)
}
sniffer.ForceDomain = trie.New[bool]()
for _, domain := range snifferRaw.ForceDomain {
err := sniffer.ForceDomain.Insert(domain, true)
@ -962,13 +982,35 @@ func parseSniffer(snifferRaw RawSniffer) (*Sniffer, error) {
}
}
sniffer.SkipDomain = trie.New[bool]()
for _, domain := range snifferRaw.SkipDomain {
err := sniffer.SkipDomain.Insert(domain, true)
sniffer.SkipSNI = trie.New[bool]()
for _, domain := range snifferRaw.SkipSNI {
err := sniffer.SkipSNI.Insert(domain, true)
if err != nil {
return nil, fmt.Errorf("error domian[%s] in force-domain, error:%v", domain, err)
}
}
// Compatibility, remove it when release
if strings.Contains(C.Version, "alpha") || strings.Contains(C.Version, "develop") || strings.Contains(C.Version, "1.10.0") {
log.Warnln("Sniffer param force and reverses deprecated, will be removed in the release version, see https://github.com/MetaCubeX/Clash.Meta/commit/48a01adb7a4f38974b9d9639f931d0d245aebf28")
if snifferRaw.Force {
// match all domain
sniffer.ForceDomain.Insert("+", true)
for _, domain := range snifferRaw.Reverse {
err := sniffer.SkipSNI.Insert(domain, true)
if err != nil {
return nil, fmt.Errorf("error domian[%s], error:%v", domain, err)
}
}
} else {
for _, domain := range snifferRaw.Reverse {
err := sniffer.ForceDomain.Insert(domain, true)
if err != nil {
return nil, fmt.Errorf("error domian[%s], error:%v", domain, err)
}
}
}
}
return sniffer, nil
}

View File

@ -86,7 +86,6 @@ type Metadata struct {
Uid *int32 `json:"uid"`
Process string `json:"process"`
ProcessPath string `json:"processPath"`
RemoteDst string `json:"remoteDestination"`
}
func (m *Metadata) RemoteAddress() string {
@ -105,7 +104,7 @@ func (m *Metadata) SourceDetail() string {
if m.Process != "" && m.Uid != nil {
return fmt.Sprintf("%s(%s, uid=%d)", m.SourceAddress(), m.Process, *m.Uid)
} else if m.Uid != nil {
return fmt.Sprintf("%s(uid=%d)", m.SourceAddress(), *m.Uid)
return fmt.Sprintf("%s(%d)", m.SourceAddress(), *m.Uid)
} else if m.Process != "" {
return fmt.Sprintf("%s(%s)", m.SourceAddress(), m.Process)
} else {

View File

@ -29,6 +29,7 @@ var Path = func() *path {
type path struct {
homeDir string
configFile string
scriptDir string
}
// SetHomeDir is used to set the configuration path
@ -122,6 +123,23 @@ func (p *path) GeoSite() string {
return P.Join(p.homeDir, "GeoSite.dat")
}
func (p *path) ScriptDir() string {
if len(p.scriptDir) != 0 {
return p.scriptDir
}
if dir, err := os.MkdirTemp("", Name+"-"); err == nil {
p.scriptDir = dir
} else {
p.scriptDir = P.Join(os.TempDir(), Name)
_ = os.MkdirAll(p.scriptDir, 0o644)
}
return p.scriptDir
}
func (p *path) Script() string {
return P.Join(p.ScriptDir(), "clash_script.py")
}
func (p *path) GetAssetLocation(file string) string {
return P.Join(p.homeDir, file)
}

View File

@ -13,6 +13,7 @@ const (
DstPort
Process
ProcessPath
Script
RuleSet
Network
Uid
@ -48,6 +49,8 @@ func (rt RuleType) String() string {
return "Process"
case ProcessPath:
return "ProcessPath"
case Script:
return "Script"
case MATCH:
return "Match"
case RuleSet:

View File

@ -46,11 +46,3 @@ func (re *RuleExtra) NotMatchProcessName(processName string) bool {
type RuleGeoSite interface {
GetDomainMatcher() *router.DomainMatcher
}
type RuleGeoIP interface {
GetIPMatcher() *router.GeoIPMatcher
}
type RuleGroup interface {
GetRecodeSize() int
}

26
constant/sniffer.go Normal file
View File

@ -0,0 +1,26 @@
package constant
type Sniffer interface {
SupportNetwork() NetWork
SniffTCP(bytes []byte) (string, error)
Protocol() string
}
const (
TLS SnifferType = iota
)
var (
SnifferList = []SnifferType{TLS}
)
type SnifferType int
func (rt SnifferType) String() string {
switch rt {
case TLS:
return "TLS"
default:
return "Unknown"
}
}

View File

@ -1,31 +0,0 @@
package sniffer
import "github.com/Dreamacro/clash/constant"
type Sniffer interface {
SupportNetwork() constant.NetWork
SniffTCP(bytes []byte) (string, error)
Protocol() string
}
const (
TLS Type = iota
HTTP
)
var (
List = []Type{TLS, HTTP}
)
type Type int
func (rt Type) String() string {
switch rt {
case TLS:
return "TLS"
case HTTP:
return "HTTP"
default:
return "Unknown"
}
}

View File

@ -138,8 +138,6 @@ func (dc *quicClient) openSession() (quic.Connection, error) {
quicConfig := &quic.Config{
ConnectionIDLength: 12,
HandshakeIdleTimeout: time.Second * 8,
MaxIncomingStreams: 4,
MaxIdleTimeout: time.Second * 45,
}
log.Debugln("opening session to %s", dc.addr)
@ -177,7 +175,7 @@ func (dc *quicClient) openSession() (quic.Connection, error) {
return nil, fmt.Errorf("quio create packet failed")
}
udp = wrapConn
udp = wrapConn.PacketConn
}
session, err := quic.Dial(udp, &udpAddr, host, tlsConfig, quicConfig)

View File

@ -164,7 +164,6 @@ func withResolver(resolver *Resolver) handler {
msg.SetRcode(r, msg.Rcode)
msg.Authoritative = true
log.Debugln("[DNS] %s --> %s", msgToDomain(r), msgToIP(msg))
return msg, nil
}
}

View File

@ -1,16 +0,0 @@
package dns
import D "github.com/miekg/dns"
type LocalServer struct {
handler handler
}
// ServeMsg implement resolver.LocalServer ResolveMsg
func (s *LocalServer) ServeMsg(msg *D.Msg) (*D.Msg, error) {
return handlerWithContext(s.handler, msg)
}
func NewLocalServer(resolver *Resolver, mapper *ResolverEnhancer) *LocalServer {
return &LocalServer{handler: NewHandler(resolver, mapper)}
}

View File

@ -7,6 +7,7 @@ import (
"go.uber.org/atomic"
"math/rand"
"net/netip"
"strings"
"time"
"github.com/Dreamacro/clash/common/cache"
@ -231,7 +232,7 @@ func (r *Resolver) matchPolicy(m *D.Msg) []dnsClient {
return nil
}
domain := msgToDomain(m)
domain := r.msgToDomain(m)
if domain == "" {
return nil
}
@ -250,7 +251,7 @@ func (r *Resolver) shouldOnlyQueryFallback(m *D.Msg) bool {
return false
}
domain := msgToDomain(m)
domain := r.msgToDomain(m)
if domain == "" {
return false
@ -331,6 +332,14 @@ func (r *Resolver) resolveIP(host string, dnsType uint16) (ips []netip.Addr, err
return
}
func (r *Resolver) msgToDomain(msg *D.Msg) string {
if len(msg.Question) > 0 {
return strings.TrimRight(msg.Question[0].Name, ".")
}
return ""
}
func (r *Resolver) asyncExchange(ctx context.Context, client []dnsClient, msg *D.Msg) <-chan *result {
ch := make(chan *result, 1)
go func() {

View File

@ -6,7 +6,6 @@ import (
"fmt"
"net"
"net/netip"
"strings"
"time"
"github.com/Dreamacro/clash/common/cache"
@ -117,14 +116,6 @@ func msgToIP(msg *D.Msg) []netip.Addr {
return ips
}
func msgToDomain(msg *D.Msg) string {
if len(msg.Question) > 0 {
return strings.TrimRight(msg.Question[0].Name, ".")
}
return ""
}
type wrapPacketConn struct {
net.PacketConn
rAddr net.Addr

14
go.mod
View File

@ -10,7 +10,7 @@ require (
github.com/go-chi/render v1.0.1
github.com/gofrs/uuid v4.2.0+incompatible
github.com/gorilla/websocket v1.5.0
github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f
github.com/insomniacslk/dhcp v0.0.0-20220405050111-12fbdcb11b41
github.com/lucas-clemente/quic-go v0.27.0
github.com/miekg/dns v1.1.48
github.com/oschwald/geoip2-golang v1.7.0
@ -20,23 +20,23 @@ require (
go.etcd.io/bbolt v1.3.6
go.uber.org/atomic v1.9.0
go.uber.org/automaxprocs v1.5.1
golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122
golang.org/x/exp v0.0.0-20220428152302-39d4317da171
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4
golang.org/x/exp v0.0.0-20220414153411-bcd21879b8fd
golang.org/x/net v0.0.0-20220421235706-1d1ef9303861
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150
golang.org/x/time v0.0.0-20220411224347-583f2d630306
golang.zx2c4.com/wireguard v0.0.0-20220407013110-ef5c587f782d
golang.zx2c4.com/wireguard/windows v0.5.4-0.20220317000008-6432784c2469
google.golang.org/protobuf v1.28.0
gopkg.in/yaml.v2 v2.4.0
gvisor.dev/gvisor v0.0.0-20220506231117-8ef340c14150
gvisor.dev/gvisor v0.0.0-20220422224113-2cca6b79d9f4
)
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/fsnotify/fsnotify v1.5.1 // 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

29
go.sum
View File

@ -31,8 +31,8 @@ github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8=
@ -88,8 +88,8 @@ github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:Fecb
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis=
github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f h1:l1QCwn715k8nYkj4Ql50rzEog3WnMdrd4YYMMwemxEo=
github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E=
github.com/insomniacslk/dhcp v0.0.0-20220405050111-12fbdcb11b41 h1:Yg3n3AI7GoHnWt7dyjsLPU+TEuZfPAg0OdiA3MJUV6I=
github.com/insomniacslk/dhcp v0.0.0-20220405050111-12fbdcb11b41/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E=
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=
github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ=
@ -221,11 +221,11 @@ golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122 h1:NvGWuYG8dkDHFSKksI1P9faiVJ9rayE6l0+ouWVIDs8=
golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA=
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20220428152302-39d4317da171 h1:TfdoLivD44QwvssI9Sv1xwa5DcL5XQr4au4sZ2F2NV4=
golang.org/x/exp v0.0.0-20220428152302-39d4317da171/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
golang.org/x/exp v0.0.0-20220414153411-bcd21879b8fd h1:zVFyTKZN/Q7mNRWSs1GOYnHM9NiFSJ54YVRsD0rNWT4=
golang.org/x/exp v0.0.0-20220414153411-bcd21879b8fd/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
@ -257,8 +257,8 @@ golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220421235706-1d1ef9303861 h1:yssD99+7tqHWO5Gwh81phT+67hg+KttniBr6UnEXOY8=
golang.org/x/net v0.0.0-20220421235706-1d1ef9303861/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@ -304,9 +304,8 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 h1:nonptSpoQ4vQjyraW20DXPAglgQfVnM9ZC6MmNLMR60=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 h1:xHms4gcpe1YE7A3yIllJXP16CMAGuqwO2lX1mTyyRRc=
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -385,8 +384,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
gvisor.dev/gvisor v0.0.0-20220506231117-8ef340c14150 h1:bspdBY1iCLtW6JXold8yhXHkAiE9UoWfmHShNkTc9JA=
gvisor.dev/gvisor v0.0.0-20220506231117-8ef340c14150/go.mod h1:tWwEcFvJavs154OdjFCw78axNrsDlz4Zh8jvPqwcpGI=
gvisor.dev/gvisor v0.0.0-20220422224113-2cca6b79d9f4 h1:CSkd548jw5hmVwdJ+JuUhMtRV56oQBER7sbkIOePP2Y=
gvisor.dev/gvisor v0.0.0-20220422224113-2cca6b79d9f4/go.mod h1:tWwEcFvJavs154OdjFCw78axNrsDlz4Zh8jvPqwcpGI=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@ -1,5 +0,0 @@
//go:build !386 && !amd64 && !arm64 && !arm64be && !mipsle && !mips
package executor
const concurrentCount = 5

View File

@ -1,5 +0,0 @@
//go:build mips || mipsle
package executor
const concurrentCount = 1

View File

@ -1,7 +0,0 @@
//go:build 386 || amd64 || arm64 || arm64be
package executor
import "math"
const concurrentCount = math.MaxInt

View File

@ -2,7 +2,6 @@ package executor
import (
"fmt"
"github.com/Dreamacro/clash/listener/inner"
"net/netip"
"os"
"runtime"
@ -77,24 +76,19 @@ func ApplyConfig(cfg *config.Config, force bool) {
updateProxies(cfg.Proxies, cfg.Providers)
updateRules(cfg.Rules, cfg.RuleProviders)
updateSniffer(cfg.Sniffer)
updateHosts(cfg.Hosts)
initInnerTcp()
updateDNS(cfg.DNS, cfg.General.IPv6)
loadProxyProvider(cfg.Providers)
updateProfile(cfg)
loadRuleProvider(cfg.RuleProviders)
updateDNS(cfg.DNS)
updateGeneral(cfg.General, force)
updateIPTables(cfg)
updateTun(cfg.Tun, cfg.DNS)
updateExperimental(cfg)
updateHosts(cfg.Hosts)
loadProxyProvider(cfg.Providers)
updateProfile(cfg)
loadRuleProvider(cfg.RuleProviders)
log.SetLevel(cfg.General.LogLevel)
}
func initInnerTcp() {
inner.New(tunnel.TCPIn())
}
func GetGeneral() *config.General {
ports := P.GetPorts()
var authenticator []string
@ -117,7 +111,6 @@ func GetGeneral() *config.General {
LogLevel: log.Level(),
IPv6: !resolver.DisableIPv6,
GeodataLoader: G.LoaderName(),
Tun: P.GetTunConf(),
}
return general
@ -125,16 +118,12 @@ func GetGeneral() *config.General {
func updateExperimental(c *config.Config) {}
func updateDNS(c *config.DNS, generalIPv6 bool) {
func updateDNS(c *config.DNS) {
if !c.Enable {
resolver.DisableIPv6 = !generalIPv6
resolver.DefaultResolver = nil
resolver.DefaultHostMapper = nil
resolver.DefaultLocalServer = nil
dns.ReCreateServer("", nil, nil)
return
} else {
resolver.DisableIPv6 = !c.IPv6
}
cfg := dns.Config{
@ -156,6 +145,8 @@ func updateDNS(c *config.DNS, generalIPv6 bool) {
ProxyServer: c.ProxyServerNameserver,
}
resolver.DisableIPv6 = !cfg.IPv6
r := dns.NewResolver(cfg)
pr := dns.NewProxyServerHostResolver(r)
m := dns.NewEnhancer(cfg)
@ -167,7 +158,6 @@ func updateDNS(c *config.DNS, generalIPv6 bool) {
resolver.DefaultResolver = r
resolver.DefaultHostMapper = m
resolver.DefaultLocalServer = dns.NewLocalServer(r, m)
if pr.HasProxyServer() {
resolver.ProxyServerHostResolver = pr
@ -195,7 +185,7 @@ func loadProvider(pv provider.Provider) {
log.Infoln("Start initial provider %s", (pv).Name())
}
if err := pv.Initial(); err != nil {
if err := (pv).Initial(); err != nil {
switch pv.Type() {
case provider.Proxy:
{
@ -211,50 +201,24 @@ func loadProvider(pv provider.Provider) {
}
func loadRuleProvider(ruleProviders map[string]provider.RuleProvider) {
wg := sync.WaitGroup{}
ch := make(chan struct{}, concurrentCount)
for _, ruleProvider := range ruleProviders {
ruleProvider := ruleProvider
wg.Add(1)
ch <- struct{}{}
go func() {
defer func() { <-ch; wg.Done() }()
loadProvider(ruleProvider)
}()
loadProvider(ruleProvider)
}
wg.Wait()
}
func loadProxyProvider(proxyProviders map[string]provider.ProxyProvider) {
// limit concurrent size
wg := sync.WaitGroup{}
ch := make(chan struct{}, concurrentCount)
for _, proxyProvider := range proxyProviders {
proxyProvider := proxyProvider
wg.Add(1)
ch <- struct{}{}
go func() {
defer func() { <-ch; wg.Done() }()
loadProvider(proxyProvider)
}()
func loadProxyProvider(ruleProviders map[string]provider.ProxyProvider) {
for _, ruleProvider := range ruleProviders {
loadProvider(ruleProvider)
}
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())
P.ReCreateTun(tun, dns, tunnel.TCPIn(), tunnel.UDPIn())
}
func updateSniffer(sniffer *config.Sniffer) {
if sniffer.Enable {
dispatcher, err := SNI.NewSnifferDispatcher(sniffer.Sniffers, sniffer.ForceDomain, sniffer.SkipDomain, sniffer.Ports)
dispatcher, err := SNI.NewSnifferDispatcher(sniffer.Sniffers, sniffer.ForceDomain, sniffer.SkipSNI, sniffer.Ports)
if err != nil {
log.Warnln("initial sniffer failed, err:%v", err)
}

View File

@ -1,7 +1,6 @@
package route
import (
"github.com/Dreamacro/clash/constant"
"net/http"
"github.com/Dreamacro/clash/tunnel"
@ -20,23 +19,17 @@ type Rule struct {
Type string `json:"type"`
Payload string `json:"payload"`
Proxy string `json:"proxy"`
Size int `json:"Size"`
}
func getRules(w http.ResponseWriter, r *http.Request) {
rawRules := tunnel.Rules()
rules := []Rule{}
for _, rule := range rawRules {
r := Rule{
rules = append(rules, Rule{
Type: rule.RuleType().String(),
Payload: rule.Payload(),
Proxy: rule.Adapter(),
Size: -1,
}
if rule.RuleType() == constant.GEOIP || rule.RuleType() == constant.GEOSITE {
r.Size = rule.(constant.RuleGroup).GetRecodeSize()
}
rules = append(rules, r)
})
}

16
hub/route/script.go Normal file
View File

@ -0,0 +1,16 @@
package route
import (
"github.com/go-chi/chi/v5"
"net/http"
)
func scriptRouter() http.Handler {
r := chi.NewRouter()
r.Get("/", getScript)
return r
}
func getScript(writer http.ResponseWriter, request *http.Request) {
writer.WriteHeader(http.StatusMethodNotAllowed)
}

View File

@ -72,6 +72,7 @@ func Start(addr string, secret string) {
r.Mount("/connections", connectionRouter())
r.Mount("/providers/proxies", proxyProviderRouter())
r.Mount("/providers/rules", ruleProviderRouter())
r.Mount("/script", scriptRouter())
r.Mount("/cache", cacheRouter())
})

View File

@ -5,9 +5,8 @@ import (
"github.com/Dreamacro/clash/common/cmd"
"github.com/Dreamacro/clash/listener/inner"
"net"
"net/netip"
"os"
"runtime"
"sort"
"strconv"
"sync"
@ -25,10 +24,8 @@ import (
)
var (
allowLan = false
bindAddress = "*"
lastTunConf *config.Tun
lastTunAddressPrefix *netip.Prefix
allowLan = false
bindAddress = "*"
socksListener *socks.Listener
socksUDPListener *socks.UDPListener
@ -58,15 +55,6 @@ type Ports struct {
MixedPort int `json:"mixed-port"`
}
func GetTunConf() config.Tun {
if lastTunConf == nil {
return config.Tun{
Enable: false,
}
}
return *lastTunConf
}
func AllowLan() bool {
return allowLan
}
@ -83,10 +71,6 @@ func SetBindAddress(host string) {
bindAddress = host
}
func NewInner(tcpIn chan<- C.ConnContext) {
inner.New(tcpIn)
}
func ReCreateHTTP(port int, tcpIn chan<- C.ConnContext) {
httpMux.Lock()
defer httpMux.Unlock()
@ -131,6 +115,7 @@ func ReCreateSocks(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.P
log.Errorln("Start SOCKS server error: %s", err.Error())
}
}()
inner.New(tcpIn)
addr := genAddr(bindAddress, port, allowLan)
@ -327,7 +312,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, dnsCfg *config.DNS, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) {
tunMux.Lock()
defer tunMux.Unlock()
@ -335,35 +320,22 @@ func ReCreateTun(tunConf *config.Tun, tunAddressPrefix *netip.Prefix, tcpIn chan
defer func() {
if err != nil {
log.Errorln("Start TUN listening error: %s", err.Error())
os.Exit(2)
}
}()
if tunAddressPrefix == nil {
tunAddressPrefix = lastTunAddressPrefix
}
if !hasTunConfigChange(tunConf, tunAddressPrefix) {
return
}
if tunStackListener != nil {
_ = tunStackListener.Close()
tunStackListener.Close()
tunStackListener = nil
lastTunConf = nil
lastTunAddressPrefix = nil
}
if !tunConf.Enable {
return
}
tunStackListener, err = tun.New(tunConf, tunAddressPrefix, tcpIn, udpIn)
tunStackListener, err = tun.New(tunConf, dnsCfg, tcpIn, udpIn)
if err != nil {
log.Warnln("Failed to start TUN interface: %s", err)
log.Warnln("Failed to start TUN interface: %s", err.Error())
}
lastTunConf = tunConf
lastTunAddressPrefix = tunAddressPrefix
}
// GetPorts return the ports of proxy servers
@ -422,47 +394,6 @@ 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 {
if lastTunConf == nil {
return true
}
if len(lastTunConf.DNSHijack) != len(tunConf.DNSHijack) {
return true
}
sort.Slice(lastTunConf.DNSHijack, func(i, j int) bool {
return lastTunConf.DNSHijack[i].Addr().Less(lastTunConf.DNSHijack[j].Addr())
})
sort.Slice(tunConf.DNSHijack, func(i, j int) bool {
return tunConf.DNSHijack[i].Addr().Less(tunConf.DNSHijack[j].Addr())
})
for i, dns := range tunConf.DNSHijack {
if dns != lastTunConf.DNSHijack[i] {
return true
}
}
if lastTunConf.Enable != tunConf.Enable ||
lastTunConf.Device != tunConf.Device ||
lastTunConf.Stack != tunConf.Stack ||
lastTunConf.AutoRoute != tunConf.AutoRoute {
return true
}
if (tunAddressPrefix != nil && lastTunAddressPrefix == nil) || (tunAddressPrefix == nil && lastTunAddressPrefix != nil) {
return true
}
if tunAddressPrefix != nil && lastTunAddressPrefix != nil && *tunAddressPrefix != *lastTunAddressPrefix {
return true
}
return false
}
func Cleanup() {
if tunStackListener != nil {
_ = tunStackListener.Close()

View File

@ -33,7 +33,7 @@ func DefaultInterfaceChangeMonitor() {
interfaceName, err := GetAutoDetectInterface()
if err != nil {
log.Warnln("[TUN] default interface monitor exited, cause: %v", err)
continue
break
}
old := dialer.DefaultInterface.Load()

View File

@ -10,18 +10,18 @@ import (
"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]
func GetAutoDetectInterface() (string, error) {
res, err := cmd.ExecCmd("sh -c ip route | awk '{print $3}' | xargs echo -n")
if err != nil {
return "", err
}
if ifn == "" {
err = fmt.Errorf("interface not found")
ifaces := strings.Split(res, " ")
for _, iface := range ifaces {
if iface == "wlan0" {
return "wlan0", nil
}
}
return
return ifaces[0], nil
}
func ConfigInterfaceAddress(dev device.Device, addr netip.Prefix, forceMTU int, autoRoute, autoDetectInterface bool) error {
@ -40,10 +40,6 @@ func ConfigInterfaceAddress(dev device.Device, addr netip.Prefix, forceMTU int,
return err
}
if err = execRouterCmd("add", addr.Masked().String(), interfaceName, ip.String(), "main"); err != nil {
return err
}
if autoRoute {
err = configInterfaceRouting(interfaceName, addr, autoDetectInterface)
}

View File

@ -19,22 +19,20 @@ func ConfigInterfaceAddress(dev device.Device, addr netip.Prefix, forceMTU int,
ip = addr.Masked().Addr().Next()
)
if _, err := cmd.ExecCmd(fmt.Sprintf("ip addr add %s dev %s", ip.String(), interfaceName)); err != nil {
_, err := cmd.ExecCmd(fmt.Sprintf("ip addr add %s dev %s", ip.String(), interfaceName))
if err != nil {
return err
}
if _, err := cmd.ExecCmd(fmt.Sprintf("ip link set %s up", interfaceName)); err != nil {
return err
}
if err := execRouterCmd("add", addr.Masked().String(), interfaceName, ip.String(), "main"); err != nil {
_, err = cmd.ExecCmd(fmt.Sprintf("ip link set %s up", interfaceName))
if err != nil {
return err
}
if autoRoute {
_ = configInterfaceRouting(interfaceName, addr, autoDetectInterface)
err = configInterfaceRouting(interfaceName, addr, autoDetectInterface)
}
return nil
return err
}
func configInterfaceRouting(interfaceName string, addr netip.Prefix, autoDetectInterface bool) error {

View File

@ -12,6 +12,7 @@ import (
C "github.com/Dreamacro/clash/constant"
D "github.com/Dreamacro/clash/listener/tun/ipstack/commons"
"github.com/Dreamacro/clash/listener/tun/ipstack/gvisor/adapter"
"github.com/Dreamacro/clash/log"
"github.com/Dreamacro/clash/transport/socks5"
)
@ -38,6 +39,8 @@ func (gh *gvHandler) HandleTCP(tunConn adapter.TCPConn) {
if D.ShouldHijackDns(gh.dnsHijack, rAddrPort) {
go func() {
log.Debugln("[TUN] hijack dns tcp: %s", rAddrPort.String())
buf := pool.Get(pool.UDPBufferSize)
defer func() {
_ = pool.Put(buf)
@ -120,6 +123,8 @@ func (gh *gvHandler) HandleUDP(tunConn adapter.UDPConn) {
}
_, _ = tunConn.WriteTo(msg, addr)
log.Debugln("[TUN] hijack dns udp: %s", rAddr.String())
}()
continue

View File

@ -93,6 +93,8 @@ func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Pref
if D.ShouldHijackDns(dnsAddr, rAddrPort) {
go func() {
log.Debugln("[TUN] hijack dns tcp: %s", rAddrPort.String())
buf := pool.Get(pool.UDPBufferSize)
defer func() {
_ = pool.Put(buf)
@ -184,6 +186,8 @@ func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Pref
_, _ = stack.UDP().WriteTo(msg, rAddr, lAddr)
_ = pool.Put(buf)
log.Debugln("[TUN] hijack dns udp: %s", rAddrPort.String())
}()
continue

View File

@ -23,14 +23,18 @@ import (
)
// New TunAdapter
func New(tunConf *config.Tun, tunAddressPrefix *netip.Prefix, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (ipstack.Stack, error) {
func New(tunConf *config.Tun, dnsConf *config.DNS, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (ipstack.Stack, error) {
var tunAddressPrefix string
if dnsConf.FakeIPRange != nil {
tunAddressPrefix = dnsConf.FakeIPRange.IPNet().String()
}
var (
tunAddress = netip.Prefix{}
devName = tunConf.Device
stackType = tunConf.Stack
autoRoute = tunConf.AutoRoute
mtu = 9000
tunAddress, _ = netip.ParsePrefix(tunAddressPrefix)
devName = tunConf.Device
stackType = tunConf.Stack
autoRoute = tunConf.AutoRoute
mtu = 9000
tunDevice device.Device
tunStack ipstack.Stack
@ -38,10 +42,6 @@ func New(tunConf *config.Tun, tunAddressPrefix *netip.Prefix, tcpIn chan<- C.Con
err error
)
if tunAddressPrefix != nil {
tunAddress = *tunAddressPrefix
}
if devName == "" {
devName = generateDeviceName()
}

View File

@ -10,8 +10,8 @@ import (
)
var (
logCh = make(chan Event)
source = observable.NewObservable[Event](logCh)
logCh = make(chan *Event)
source = observable.NewObservable[*Event](logCh)
level = INFO
)
@ -57,12 +57,12 @@ func Fatalln(format string, v ...any) {
log.Fatalf(format, v...)
}
func Subscribe() observable.Subscription[Event] {
func Subscribe() observable.Subscription[*Event] {
sub, _ := source.Subscribe()
return sub
}
func UnSubscribe(sub observable.Subscription[Event]) {
func UnSubscribe(sub observable.Subscription[*Event]) {
source.UnSubscribe(sub)
}
@ -74,7 +74,7 @@ func SetLevel(newLevel LogLevel) {
level = newLevel
}
func print(data Event) {
func print(data *Event) {
if data.LogLevel < level {
return
}
@ -91,9 +91,15 @@ func print(data Event) {
}
}
func newLog(logLevel LogLevel, format string, v ...any) Event {
return Event{
func newLog(logLevel LogLevel, format string, v ...any) *Event {
return &Event{
LogLevel: logLevel,
Payload: fmt.Sprintf(format, v...),
}
}
func PrintLog(logLevel LogLevel, format string, v ...interface{}) {
event := newLog(logLevel, format, v...)
logCh <- event
print(event)
}

View File

@ -18,7 +18,6 @@ type GEOIP struct {
adapter string
noResolveIP bool
geoIPMatcher *router.GeoIPMatcher
recodeSize int
}
func (g *GEOIP) RuleType() C.RuleType {
@ -66,10 +65,6 @@ func (g *GEOIP) GetIPMatcher() *router.GeoIPMatcher {
return g.geoIPMatcher
}
func (g *GEOIP) GetRecodeSize() int {
return g.recodeSize
}
func NewGEOIP(country string, adapter string, noResolveIP bool) (*GEOIP, error) {
if !C.GeodataMode {
geoip := &GEOIP{
@ -81,19 +76,18 @@ func NewGEOIP(country string, adapter string, noResolveIP bool) (*GEOIP, error)
return geoip, nil
}
geoIPMatcher, size, err := geodata.LoadGeoIPMatcher(country)
geoIPMatcher, recordsCount, err := geodata.LoadGeoIPMatcher(country)
if err != nil {
return nil, fmt.Errorf("[GeoIP] %s", err.Error())
}
log.Infoln("Start initial GeoIP rule %s => %s, records: %d", country, adapter, size)
log.Infoln("Start initial GeoIP rule %s => %s, records: %d", country, adapter, recordsCount)
geoip := &GEOIP{
Base: &Base{},
country: country,
adapter: adapter,
noResolveIP: noResolveIP,
geoIPMatcher: geoIPMatcher,
recodeSize: size,
}
return geoip, nil
}

View File

@ -14,10 +14,9 @@ import (
type GEOSITE struct {
*Base
country string
adapter string
matcher *router.DomainMatcher
recodeSize int
country string
adapter string
matcher *router.DomainMatcher
}
func (gs *GEOSITE) RuleType() C.RuleType {
@ -45,24 +44,19 @@ func (gs *GEOSITE) GetDomainMatcher() *router.DomainMatcher {
return gs.matcher
}
func (gs *GEOSITE) GetRecodeSize() int {
return gs.recodeSize
}
func NewGEOSITE(country string, adapter string) (*GEOSITE, error) {
matcher, size, err := geodata.LoadGeoSiteMatcher(country)
matcher, recordsCount, err := geodata.LoadGeoSiteMatcher(country)
if err != nil {
return nil, fmt.Errorf("load GeoSite data error, %s", err.Error())
}
log.Infoln("Start initial GeoSite rule %s => %s, records: %d", country, adapter, size)
log.Infoln("Start initial GeoSite rule %s => %s, records: %d", country, adapter, recordsCount)
geoSite := &GEOSITE{
Base: &Base{},
country: country,
adapter: adapter,
matcher: matcher,
recodeSize: size,
Base: &Base{},
country: country,
adapter: adapter,
matcher: matcher,
}
return geoSite, nil

View File

@ -102,8 +102,7 @@ func parseRule(tp, payload string, params []string) (C.Rule, error) {
case "PROCESS-PATH":
parsed, parseErr = RC.NewProcess(payload, "", false)
case "RULE-SET":
noResolve := RC.HasNoResolve(params)
parsed, parseErr = provider.NewRuleSet(payload, "", noResolve)
parsed, parseErr = provider.NewRuleSet(payload, "")
case "NOT":
parsed, parseErr = NewNOT(payload, "")
case "AND":

View File

@ -39,6 +39,10 @@ func ParseRule(tp, payload, target string, params []string) (C.Rule, error) {
parsed, parseErr = RC.NewProcess(payload, target, true)
case "PROCESS-PATH":
parsed, parseErr = RC.NewProcess(payload, target, false)
case "MATCH":
parsed = RC.NewMatch(target)
case "RULE-SET":
parsed, parseErr = RP.NewRuleSet(payload, target)
case "NETWORK":
parsed, parseErr = RC.NewNetworkType(payload, target)
case "UID":
@ -49,11 +53,6 @@ func ParseRule(tp, payload, target string, params []string) (C.Rule, error) {
parsed, parseErr = logic.NewOR(payload, target)
case "NOT":
parsed, parseErr = logic.NewNOT(payload, target)
case "RULE-SET":
noResolve := RC.HasNoResolve(params)
parsed, parseErr = RP.NewRuleSet(payload, target, noResolve)
case "MATCH":
parsed = RC.NewMatch(target)
default:
parseErr = fmt.Errorf("unsupported rule type %s", tp)
}

View File

@ -30,20 +30,26 @@ func (c *classicalStrategy) ShouldResolveIP() bool {
}
func (c *classicalStrategy) OnUpdate(rules []string) {
var classicalRules []C.Rule
shouldResolveIP := false
count := 0
for _, rawRule := range rules {
ruleType, rule, params := ruleParse(rawRule)
r, err := parseRule(ruleType, rule, "", params)
if err != nil {
log.Warnln("parse rule error:[%s]", err.Error())
} else {
if !c.shouldResolveIP {
c.shouldResolveIP = r.ShouldResolveIP()
if !shouldResolveIP {
shouldResolveIP = shouldResolveIP || r.ShouldResolveIP()
}
c.rules = append(c.rules, r)
c.count++
classicalRules = append(classicalRules, r)
count++
}
}
c.rules = classicalRules
c.count = count
}
func NewClassicalStrategy() *classicalStrategy {

View File

@ -8,8 +8,9 @@ import (
)
type domainStrategy struct {
count int
domainRules *trie.DomainTrie[bool]
shouldResolveIP bool
count int
domainRules *trie.DomainTrie[bool]
}
func (d *domainStrategy) Match(metadata *C.Metadata) bool {
@ -21,7 +22,7 @@ func (d *domainStrategy) Count() int {
}
func (d *domainStrategy) ShouldResolveIP() bool {
return false
return d.shouldResolveIP
}
func (d *domainStrategy) OnUpdate(rules []string) {
@ -54,5 +55,5 @@ func ruleParse(ruleRaw string) (string, string, []string) {
}
func NewDomainStrategy() *domainStrategy {
return &domainStrategy{}
return &domainStrategy{shouldResolveIP: false}
}

View File

@ -44,12 +44,6 @@ func (f *fetcher) Initial() (interface{}, error) {
err error
)
defer func() {
if f.ticker != nil {
go f.pullLoop()
}
}()
if stat, fErr := os.Stat(f.vehicle.Path()); fErr == nil {
buf, err = ioutil.ReadFile(f.vehicle.Path())
modTime := stat.ModTime()
@ -89,6 +83,9 @@ func (f *fetcher) Initial() (interface{}, error) {
}
f.hash = md5.Sum(buf)
if f.ticker != nil {
go f.pullLoop()
}
return rules, nil
}

View File

@ -64,9 +64,6 @@ func parseRule(tp, payload, target string, params []string) (C.Rule, error) {
parsed = RC.NewDomainSuffix(payload, target)
case "DOMAIN-KEYWORD":
parsed = RC.NewDomainKeyword(payload, target)
case "GEOIP":
noResolve := RC.HasNoResolve(params)
parsed, parseErr = RC.NewGEOIP(payload, target, noResolve)
case "GEOSITE":
parsed, parseErr = RC.NewGEOSITE(payload, target)
case "IP-CIDR", "IP-CIDR6":
@ -82,8 +79,9 @@ func parseRule(tp, payload, target string, params []string) (C.Rule, error) {
parsed, parseErr = RC.NewProcess(payload, target, true)
case "PROCESS-PATH":
parsed, parseErr = RC.NewProcess(payload, target, false)
case "NETWORK":
parsed, parseErr = RC.NewNetworkType(payload, target)
case "GEOIP":
noResolve := RC.HasNoResolve(params)
parsed, parseErr = RC.NewGEOIP(payload, target, noResolve)
default:
parseErr = fmt.Errorf("unsupported rule type %s", tp)
}

View File

@ -28,8 +28,7 @@ type RulePayload struct {
key: Domain or IP Cidr
value: Rule type or is empty
*/
Rules []string `yaml:"payload"`
Rules2 []string `yaml:"rules"`
Rules []string `yaml:"payload"`
}
type ruleStrategy interface {
@ -118,8 +117,7 @@ func NewRuleSetProvider(name string, behavior P.RuleType, interval time.Duration
rp,
}
final := func(provider *RuleSetProvider) { rp.fetcher.Destroy() }
runtime.SetFinalizer(wrapper, final)
runtime.SetFinalizer(wrapper, rp.fetcher.Destroy())
return wrapper
}
@ -146,5 +144,5 @@ func rulesParse(buf []byte) (interface{}, error) {
return nil, err
}
return append(rulePayload.Rules, rulePayload.Rules2...), nil
return rulePayload.Rules, nil
}

View File

@ -12,7 +12,6 @@ type RuleSet struct {
ruleProviderName string
adapter string
ruleProvider P.RuleProvider
noResolveIP bool
}
func (rs *RuleSet) ShouldFindProcess() bool {
@ -36,7 +35,7 @@ func (rs *RuleSet) Payload() string {
}
func (rs *RuleSet) ShouldResolveIP() bool {
return !rs.noResolveIP && rs.getProviders().ShouldResolveIP()
return rs.getProviders().ShouldResolveIP()
}
func (rs *RuleSet) getProviders() P.RuleProvider {
if rs.ruleProvider == nil {
@ -47,7 +46,7 @@ func (rs *RuleSet) getProviders() P.RuleProvider {
return rs.ruleProvider
}
func NewRuleSet(ruleProviderName string, adapter string, noResolveIP bool) (*RuleSet, error) {
func NewRuleSet(ruleProviderName string, adapter string) (*RuleSet, error) {
rp, ok := RuleProviders()[ruleProviderName]
if !ok {
return nil, fmt.Errorf("rule set %s not found", ruleProviderName)
@ -57,6 +56,5 @@ func NewRuleSet(ruleProviderName string, adapter string, noResolveIP bool) (*Rul
ruleProviderName: ruleProviderName,
adapter: adapter,
ruleProvider: rp,
noResolveIP: noResolveIP,
}, nil
}

View File

@ -39,13 +39,14 @@ type DialFn = func(network, addr string) (net.Conn, error)
type Conn struct {
response *http.Response
request *http.Request
transport *TransportWrap
transport *http2.Transport
writer *io.PipeWriter
once sync.Once
close *atomic.Bool
err error
remain int
br *bufio.Reader
// deadlines
deadline *time.Timer
}
@ -149,8 +150,8 @@ func (g *Conn) Close() error {
return g.writer.Close()
}
func (g *Conn) LocalAddr() net.Addr { return g.transport.LocalAddr() }
func (g *Conn) RemoteAddr() net.Addr { return g.transport.RemoteAddr() }
func (g *Conn) LocalAddr() net.Addr { return &net.TCPAddr{IP: net.IPv4zero, Port: 0} }
func (g *Conn) RemoteAddr() net.Addr { return &net.TCPAddr{IP: net.IPv4zero, Port: 0} }
func (g *Conn) SetReadDeadline(t time.Time) error { return g.SetDeadline(t) }
func (g *Conn) SetWriteDeadline(t time.Time) error { return g.SetDeadline(t) }
@ -166,15 +167,13 @@ func (g *Conn) SetDeadline(t time.Time) error {
return nil
}
func NewHTTP2Client(dialFn DialFn, tlsConfig *tls.Config) *TransportWrap {
wrap := TransportWrap{}
func NewHTTP2Client(dialFn DialFn, tlsConfig *tls.Config) *http2.Transport {
dialFunc := func(network, addr string, cfg *tls.Config) (net.Conn, error) {
pconn, err := dialFn(network, addr)
if err != nil {
return nil, err
}
wrap.remoteAddr = pconn.RemoteAddr()
cn := tls.Client(pconn, cfg)
// fix tls handshake not timeout
@ -192,18 +191,16 @@ func NewHTTP2Client(dialFn DialFn, tlsConfig *tls.Config) *TransportWrap {
return cn, nil
}
wrap.Transport = &http2.Transport{
return &http2.Transport{
DialTLS: dialFunc,
TLSClientConfig: tlsConfig,
AllowHTTP: false,
DisableCompression: true,
PingTimeout: 0,
}
return &wrap
}
func StreamGunWithTransport(transport *TransportWrap, cfg *Config) (net.Conn, error) {
func StreamGunWithTransport(transport *http2.Transport, cfg *Config) (net.Conn, error) {
serviceName := "GunService"
if cfg.ServiceName != "" {
serviceName = cfg.ServiceName

View File

@ -12,15 +12,13 @@ import (
"golang.org/x/net/http2"
)
func NewHTTP2XTLSClient(dialFn DialFn, tlsConfig *tls.Config) *TransportWrap {
wrap := TransportWrap{}
func NewHTTP2XTLSClient(dialFn DialFn, tlsConfig *tls.Config) *http2.Transport {
dialFunc := func(network, addr string, cfg *tls.Config) (net.Conn, error) {
pconn, err := dialFn(network, addr)
if err != nil {
return nil, err
}
wrap.remoteAddr = pconn.RemoteAddr()
xtlsConfig := &xtls.Config{
InsecureSkipVerify: cfg.InsecureSkipVerify,
ServerName: cfg.ServerName,
@ -39,15 +37,13 @@ func NewHTTP2XTLSClient(dialFn DialFn, tlsConfig *tls.Config) *TransportWrap {
return cn, nil
}
wrap.Transport = &http2.Transport{
return &http2.Transport{
DialTLS: dialFunc,
TLSClientConfig: tlsConfig,
AllowHTTP: false,
DisableCompression: true,
PingTimeout: 0,
}
return &wrap
}
func StreamGunWithXTLSConn(conn net.Conn, tlsConfig *tls.Config, cfg *Config) (net.Conn, error) {

View File

@ -1,20 +0,0 @@
package gun
import (
"golang.org/x/net/http2"
"net"
)
type TransportWrap struct {
*http2.Transport
remoteAddr net.Addr
localAddr net.Addr
}
func (tw *TransportWrap) RemoteAddr() net.Addr {
return tw.remoteAddr
}
func (tw *TransportWrap) LocalAddr() net.Addr {
return tw.localAddr
}

View File

@ -1,7 +1,6 @@
package vless
import (
"github.com/Dreamacro/clash/common/utils"
"net"
"github.com/gofrs/uuid"
@ -50,7 +49,7 @@ func (c *Client) StreamConn(conn net.Conn, dst *DstAddr) (net.Conn, error) {
// NewClient return Client instance
func NewClient(uuidStr string, addons *Addons, xtlsShow bool) (*Client, error) {
uid, err := utils.UUIDMap(uuidStr)
uid, err := uuid.FromString(uuidStr)
if err != nil {
return nil, err
}

View File

@ -2,7 +2,6 @@ package vmess
import (
"fmt"
"github.com/Dreamacro/clash/common/utils"
"math/rand"
"net"
"runtime"
@ -83,7 +82,7 @@ func (c *Client) StreamConn(conn net.Conn, dst *DstAddr) (net.Conn, error) {
// NewClient return Client instance
func NewClient(config Config) (*Client, error) {
uid, err := utils.UUIDMap(config.UUID)
uid, err := uuid.FromString(config.UUID)
if err != nil {
return nil, err
}

View File

@ -12,12 +12,14 @@ type TunnelMode int
var ModeMapping = map[string]TunnelMode{
Global.String(): Global,
Rule.String(): Rule,
Script.String(): Script,
Direct.String(): Direct,
}
const (
Global TunnelMode = iota
Rule
Script
Direct
)
@ -61,6 +63,8 @@ func (m TunnelMode) String() string {
return "global"
case Rule:
return "rule"
case Script:
return "script"
case Direct:
return "direct"
default:

View File

@ -9,7 +9,6 @@ import (
"path/filepath"
"runtime"
"strconv"
"strings"
"sync"
"time"
@ -179,7 +178,7 @@ func preHandleMetadata(metadata *C.Metadata) error {
} else {
metadata.Process = filepath.Base(path)
metadata.ProcessPath = path
if procesCache != metadata.Process {
if procesCache == metadata.Process {
log.Debugln("[Process] %s from process %s", metadata.String(), path)
}
procesCache = metadata.Process
@ -270,18 +269,6 @@ func handleUDPConn(packet *inbound.PacketAdapter) {
return
}
pCtx.InjectPacketConn(rawPc)
actualProxy := proxy.Unwrap(metadata)
if actualProxy != nil {
if dst, _, err := net.SplitHostPort(actualProxy.Addr()); err == nil {
metadata.RemoteDst = dst
} else {
if addrError, ok := err.(*net.AddrError); ok && strings.Contains(addrError.Err, "missing port") {
metadata.RemoteDst = actualProxy.Addr()
}
}
}
pc := statistic.NewUDPTracker(rawPc, statistic.DefaultManager, metadata, rule)
switch true {
@ -291,6 +278,8 @@ func handleUDPConn(packet *inbound.PacketAdapter) {
} else {
log.Infoln("[UDP] %s --> %s match %s using %s", metadata.SourceDetail(), metadata.RemoteAddress(), rule.Payload(), rawPc.Chains().String())
}
case mode == Script:
log.Infoln("[UDP] %s --> %s using SCRIPT %s", metadata.SourceDetail(), metadata.RemoteAddress(), rawPc.Chains().String())
case mode == Global:
log.Infoln("[UDP] %s --> %s using GLOBAL", metadata.SourceDetail(), metadata.RemoteAddress())
case mode == Direct:
@ -343,11 +332,6 @@ func handleTCPConn(connCtx C.ConnContext) {
}
return
}
if tcpAddr, ok := remoteConn.RemoteAddr().(*net.TCPAddr); ok {
metadata.RemoteDst = tcpAddr.IP.String()
}
remoteConn = statistic.NewTCPTracker(remoteConn, statistic.DefaultManager, metadata, rule)
defer func(remoteConn C.Conn) {
_ = remoteConn.Close()
@ -360,6 +344,8 @@ func handleTCPConn(connCtx C.ConnContext) {
} else {
log.Infoln("[TCP] %s --> %s match %s using %s", metadata.SourceDetail(), metadata.RemoteAddress(), rule.RuleType().String(), remoteConn.Chains().String())
}
case mode == Script:
log.Infoln("[TCP] %s --> %s using SCRIPT %s", metadata.SourceDetail(), metadata.RemoteAddress(), remoteConn.Chains().String())
case mode == Global:
log.Infoln("[TCP] %s --> %s using GLOBAL", metadata.SourceDetail(), metadata.RemoteAddress())
case mode == Direct: