Android: patch
This commit is contained in:
parent
79a29826a4
commit
a551d7ebd9
30 changed files with 512 additions and 59 deletions
|
@ -12,7 +12,7 @@ func NewPacket(target socks5.Addr, packet C.UDPPacket, source C.Type, additions
|
||||||
metadata.Type = source
|
metadata.Type = source
|
||||||
ApplyAdditions(metadata, WithSrcAddr(packet.LocalAddr()))
|
ApplyAdditions(metadata, WithSrcAddr(packet.LocalAddr()))
|
||||||
if p, ok := packet.(C.UDPPacketInAddr); ok {
|
if p, ok := packet.(C.UDPPacketInAddr); ok {
|
||||||
ApplyAdditions(metadata, WithInAddr(p.InAddr()))
|
ApplyAdditions(metadata, WithInAddr(p.InAddr()), WithDstAddr(metadata.RawDstAddr))
|
||||||
}
|
}
|
||||||
ApplyAdditions(metadata, additions...)
|
ApplyAdditions(metadata, additions...)
|
||||||
|
|
||||||
|
|
7
adapter/outbound/patch.go
Normal file
7
adapter/outbound/patch.go
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package outbound
|
||||||
|
|
||||||
|
import "net"
|
||||||
|
|
||||||
|
func (c *conn) RawConn() (net.Conn, bool) {
|
||||||
|
return c.ExtendedConn, true
|
||||||
|
}
|
62
adapter/outboundgroup/patch.go
Normal file
62
adapter/outboundgroup/patch.go
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
package outboundgroup
|
||||||
|
|
||||||
|
import (
|
||||||
|
C "github.com/Dreamacro/clash/constant"
|
||||||
|
"github.com/Dreamacro/clash/constant/provider"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ProxyGroup interface {
|
||||||
|
C.ProxyAdapter
|
||||||
|
|
||||||
|
Providers() []provider.ProxyProvider
|
||||||
|
Proxies() []C.Proxy
|
||||||
|
Now() string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Fallback) Providers() []provider.ProxyProvider {
|
||||||
|
return f.providers
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lb *LoadBalance) Providers() []provider.ProxyProvider {
|
||||||
|
return lb.providers
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Fallback) Proxies() []C.Proxy {
|
||||||
|
return f.GetProxies(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lb *LoadBalance) Proxies() []C.Proxy {
|
||||||
|
return lb.GetProxies(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lb *LoadBalance) Now() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Relay) Providers() []provider.ProxyProvider {
|
||||||
|
return r.providers
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Relay) Proxies() []C.Proxy {
|
||||||
|
return r.GetProxies(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Relay) Now() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Selector) Providers() []provider.ProxyProvider {
|
||||||
|
return s.providers
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Selector) Proxies() []C.Proxy {
|
||||||
|
return s.GetProxies(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *URLTest) Providers() []provider.ProxyProvider {
|
||||||
|
return u.providers
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *URLTest) Proxies() []C.Proxy {
|
||||||
|
return u.GetProxies(false)
|
||||||
|
}
|
|
@ -18,6 +18,7 @@ import (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultURLTestTimeout = time.Second * 5
|
defaultURLTestTimeout = time.Second * 5
|
||||||
|
defaultURLTestURL = "https://www.gstatic.com/generate_204"
|
||||||
)
|
)
|
||||||
|
|
||||||
type HealthCheckOption struct {
|
type HealthCheckOption struct {
|
||||||
|
@ -148,6 +149,11 @@ func (hc *HealthCheck) stop() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hc *HealthCheck) check() {
|
func (hc *HealthCheck) check() {
|
||||||
|
|
||||||
|
if len(hc.proxies) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
_, _, _ = hc.singleDo.Do(func() (struct{}, error) {
|
_, _, _ = hc.singleDo.Do(func() (struct{}, error) {
|
||||||
id := utils.NewUUIDV4().String()
|
id := utils.NewUUIDV4().String()
|
||||||
log.Debugln("Start New Health Checking {%s}", id)
|
log.Debugln("Start New Health Checking {%s}", id)
|
||||||
|
@ -223,6 +229,7 @@ func NewHealthCheck(proxies []C.Proxy, url string, interval uint, lazy bool, exp
|
||||||
if len(url) == 0 {
|
if len(url) == 0 {
|
||||||
interval = 0
|
interval = 0
|
||||||
expectedStatus = nil
|
expectedStatus = nil
|
||||||
|
url = defaultURLTestURL
|
||||||
}
|
}
|
||||||
|
|
||||||
return &HealthCheck{
|
return &HealthCheck{
|
||||||
|
|
|
@ -27,7 +27,7 @@ type healthCheckSchema struct {
|
||||||
|
|
||||||
type proxyProviderSchema struct {
|
type proxyProviderSchema struct {
|
||||||
Type string `provider:"type"`
|
Type string `provider:"type"`
|
||||||
Path string `provider:"path,omitempty"`
|
Path string `provider:"path"`
|
||||||
URL string `provider:"url,omitempty"`
|
URL string `provider:"url,omitempty"`
|
||||||
Interval int `provider:"interval,omitempty"`
|
Interval int `provider:"interval,omitempty"`
|
||||||
Filter string `provider:"filter,omitempty"`
|
Filter string `provider:"filter,omitempty"`
|
||||||
|
@ -59,23 +59,14 @@ func ParseProxyProvider(name string, mapping map[string]any) (types.ProxyProvide
|
||||||
hcInterval = uint(schema.HealthCheck.Interval)
|
hcInterval = uint(schema.HealthCheck.Interval)
|
||||||
}
|
}
|
||||||
hc := NewHealthCheck([]C.Proxy{}, schema.HealthCheck.URL, hcInterval, schema.HealthCheck.Lazy, expectedStatus)
|
hc := NewHealthCheck([]C.Proxy{}, schema.HealthCheck.URL, hcInterval, schema.HealthCheck.Lazy, expectedStatus)
|
||||||
|
path := C.Path.Resolve(schema.Path)
|
||||||
|
|
||||||
var vehicle types.Vehicle
|
var vehicle types.Vehicle
|
||||||
switch schema.Type {
|
switch schema.Type {
|
||||||
case "file":
|
case "file":
|
||||||
path := C.Path.Resolve(schema.Path)
|
|
||||||
vehicle = resource.NewFileVehicle(path)
|
vehicle = resource.NewFileVehicle(path)
|
||||||
case "http":
|
case "http":
|
||||||
if schema.Path != "" {
|
|
||||||
path := C.Path.Resolve(schema.Path)
|
|
||||||
if !C.Path.IsSafePath(path) {
|
|
||||||
return nil, fmt.Errorf("%w: %s", errSubPath, path)
|
|
||||||
}
|
|
||||||
vehicle = resource.NewHTTPVehicle(schema.URL, path)
|
vehicle = resource.NewHTTPVehicle(schema.URL, path)
|
||||||
} else {
|
|
||||||
path := C.Path.GetPathByHash("proxies", schema.URL)
|
|
||||||
vehicle = resource.NewHTTPVehicle(schema.URL, path)
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("%w: %s", errVehicleType, schema.Type)
|
return nil, fmt.Errorf("%w: %s", errVehicleType, schema.Type)
|
||||||
}
|
}
|
||||||
|
|
34
adapter/provider/patch.go
Normal file
34
adapter/provider/patch.go
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
package provider
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
suspended bool
|
||||||
|
)
|
||||||
|
|
||||||
|
type UpdatableProvider interface {
|
||||||
|
UpdatedAt() time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pp *proxySetProvider) UpdatedAt() time.Time {
|
||||||
|
return pp.Fetcher.UpdatedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pp *proxySetProvider) Close() error {
|
||||||
|
pp.healthCheck.close()
|
||||||
|
pp.Fetcher.Destroy()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cp *compatibleProvider) Close() error {
|
||||||
|
cp.healthCheck.close()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Suspend(s bool) {
|
||||||
|
suspended = s
|
||||||
|
}
|
11
common/net/patch.go
Normal file
11
common/net/patch.go
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package net
|
||||||
|
|
||||||
|
import "net"
|
||||||
|
|
||||||
|
func (c *BufferedConn) RawConn() (net.Conn, bool) {
|
||||||
|
if c.r.Buffered() == 0 {
|
||||||
|
return c.ExtendedConn, true
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, false
|
||||||
|
}
|
|
@ -70,6 +70,9 @@ func DialContext(ctx context.Context, network, address string, options ...Option
|
||||||
}
|
}
|
||||||
|
|
||||||
func ListenPacket(ctx context.Context, network, address string, options ...Option) (net.PacketConn, error) {
|
func ListenPacket(ctx context.Context, network, address string, options ...Option) (net.PacketConn, error) {
|
||||||
|
if DefaultSocketHook != nil {
|
||||||
|
return listenPacketHooked(ctx, network, address)
|
||||||
|
}
|
||||||
cfg := applyOptions(options...)
|
cfg := applyOptions(options...)
|
||||||
|
|
||||||
lc := &net.ListenConfig{}
|
lc := &net.ListenConfig{}
|
||||||
|
@ -110,6 +113,9 @@ func GetTcpConcurrent() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func dialContext(ctx context.Context, network string, destination netip.Addr, port string, opt *option) (net.Conn, error) {
|
func dialContext(ctx context.Context, network string, destination netip.Addr, port string, opt *option) (net.Conn, error) {
|
||||||
|
if DefaultSocketHook != nil {
|
||||||
|
return dialContextHooked(ctx, network, destination, port)
|
||||||
|
}
|
||||||
address := net.JoinHostPort(destination.String(), port)
|
address := net.JoinHostPort(destination.String(), port)
|
||||||
|
|
||||||
netDialer := opt.netDialer
|
netDialer := opt.netDialer
|
||||||
|
|
47
component/dialer/patch.go
Normal file
47
component/dialer/patch.go
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
package dialer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
"net/netip"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TunnelDialer func(context context.Context, network, address string) (net.Conn, error)
|
||||||
|
type SocketControl func(network, address string, conn syscall.RawConn) error
|
||||||
|
|
||||||
|
var DefaultTunnelDialer TunnelDialer
|
||||||
|
var DefaultSocketHook SocketControl
|
||||||
|
|
||||||
|
func DialTunnelContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||||
|
if dialer := DefaultTunnelDialer; dialer != nil {
|
||||||
|
return dialer(ctx, network, address)
|
||||||
|
}
|
||||||
|
|
||||||
|
return DialContext(ctx, network, address)
|
||||||
|
}
|
||||||
|
|
||||||
|
func dialContextHooked(ctx context.Context, network string, destination netip.Addr, port string) (net.Conn, error) {
|
||||||
|
dialer := &net.Dialer{
|
||||||
|
Control: DefaultSocketHook,
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := dialer.DialContext(ctx, network, net.JoinHostPort(destination.String(), port))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if t, ok := conn.(*net.TCPConn); ok {
|
||||||
|
t.SetKeepAlive(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func listenPacketHooked(ctx context.Context, network, address string) (net.PacketConn, error) {
|
||||||
|
lc := &net.ListenConfig{
|
||||||
|
Control: DefaultSocketHook,
|
||||||
|
}
|
||||||
|
|
||||||
|
return lc.ListenPacket(ctx, network, address)
|
||||||
|
}
|
|
@ -54,7 +54,7 @@ func Verify() bool {
|
||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Instance() Reader {
|
func DefaultInstance() Reader {
|
||||||
once.Do(func() {
|
once.Do(func() {
|
||||||
mmdbPath := C.Path.MMDB()
|
mmdbPath := C.Path.MMDB()
|
||||||
log.Debugln("Load MMDB file: %s", mmdbPath)
|
log.Debugln("Load MMDB file: %s", mmdbPath)
|
||||||
|
|
31
component/mmdb/patch.go
Normal file
31
component/mmdb/patch.go
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
package mmdb
|
||||||
|
|
||||||
|
import (
|
||||||
|
C "github.com/Dreamacro/clash/constant"
|
||||||
|
"github.com/Dreamacro/clash/log"
|
||||||
|
"github.com/oschwald/geoip2-golang"
|
||||||
|
"github.com/oschwald/maxminddb-golang"
|
||||||
|
)
|
||||||
|
|
||||||
|
var overrideMmdb *geoip2.Reader
|
||||||
|
|
||||||
|
func InstallOverride(override *geoip2.Reader) {
|
||||||
|
overrideMmdb = overrideMmdb
|
||||||
|
}
|
||||||
|
|
||||||
|
func Instance() Reader {
|
||||||
|
once.Do(func() {
|
||||||
|
mmdb, err := maxminddb.Open(C.Path.MMDB())
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Can't load mmdb: %s", err.Error())
|
||||||
|
}
|
||||||
|
reader = Reader{Reader: mmdb}
|
||||||
|
if mmdb.Metadata.DatabaseType == "sing-geoip" {
|
||||||
|
reader.databaseType = typeSing
|
||||||
|
} else {
|
||||||
|
reader.databaseType = typeMaxmind
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return reader
|
||||||
|
}
|
15
component/process/patch.go
Normal file
15
component/process/patch.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package process
|
||||||
|
|
||||||
|
import "github.com/Dreamacro/clash/constant"
|
||||||
|
|
||||||
|
type PackageNameResolver func(metadata *constant.Metadata) (string, error)
|
||||||
|
|
||||||
|
var DefaultPackageNameResolver PackageNameResolver
|
||||||
|
|
||||||
|
func FindPackageName(metadata *constant.Metadata) (string, error) {
|
||||||
|
if resolver := DefaultPackageNameResolver; resolver != nil {
|
||||||
|
return resolver(metadata)
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", ErrPlatformNotSupport
|
||||||
|
}
|
|
@ -13,6 +13,10 @@ import (
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
minInterval = time.Minute * 5
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
fileMode os.FileMode = 0o666
|
fileMode os.FileMode = 0o666
|
||||||
dirMode os.FileMode = 0o755
|
dirMode os.FileMode = 0o755
|
||||||
|
@ -24,8 +28,7 @@ type Fetcher[V any] struct {
|
||||||
resourceType string
|
resourceType string
|
||||||
name string
|
name string
|
||||||
vehicle types.Vehicle
|
vehicle types.Vehicle
|
||||||
UpdatedAt *time.Time
|
UpdatedAt time.Time
|
||||||
ticker *time.Ticker
|
|
||||||
done chan struct{}
|
done chan struct{}
|
||||||
hash [16]byte
|
hash [16]byte
|
||||||
parser Parser[V]
|
parser Parser[V]
|
||||||
|
@ -56,7 +59,7 @@ func (f *Fetcher[V]) Initial() (V, error) {
|
||||||
if stat, fErr := os.Stat(f.vehicle.Path()); fErr == nil {
|
if stat, fErr := os.Stat(f.vehicle.Path()); fErr == nil {
|
||||||
buf, err = os.ReadFile(f.vehicle.Path())
|
buf, err = os.ReadFile(f.vehicle.Path())
|
||||||
modTime := stat.ModTime()
|
modTime := stat.ModTime()
|
||||||
f.UpdatedAt = &modTime
|
f.UpdatedAt = modTime
|
||||||
isLocal = true
|
isLocal = true
|
||||||
if f.interval != 0 && modTime.Add(f.interval).Before(time.Now()) {
|
if f.interval != 0 && modTime.Add(f.interval).Before(time.Now()) {
|
||||||
log.Warnln("[Provider] %s not updated for a long time, force refresh", f.Name())
|
log.Warnln("[Provider] %s not updated for a long time, force refresh", f.Name())
|
||||||
|
@ -64,6 +67,7 @@ func (f *Fetcher[V]) Initial() (V, error) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
buf, err = f.vehicle.Read()
|
buf, err = f.vehicle.Read()
|
||||||
|
f.UpdatedAt = time.Now()
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -113,7 +117,7 @@ func (f *Fetcher[V]) Initial() (V, error) {
|
||||||
f.hash = md5.Sum(buf)
|
f.hash = md5.Sum(buf)
|
||||||
|
|
||||||
// pull contents automatically
|
// pull contents automatically
|
||||||
if f.ticker != nil {
|
if f.interval > 0 {
|
||||||
go f.pullLoop()
|
go f.pullLoop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,7 +133,7 @@ func (f *Fetcher[V]) Update() (V, bool, error) {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
hash := md5.Sum(buf)
|
hash := md5.Sum(buf)
|
||||||
if bytes.Equal(f.hash[:], hash[:]) {
|
if bytes.Equal(f.hash[:], hash[:]) {
|
||||||
f.UpdatedAt = &now
|
f.UpdatedAt = now
|
||||||
_ = os.Chtimes(f.vehicle.Path(), now, now)
|
_ = os.Chtimes(f.vehicle.Path(), now, now)
|
||||||
return lo.Empty[V](), true, nil
|
return lo.Empty[V](), true, nil
|
||||||
}
|
}
|
||||||
|
@ -145,23 +149,31 @@ func (f *Fetcher[V]) Update() (V, bool, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
f.UpdatedAt = &now
|
f.UpdatedAt = now
|
||||||
f.hash = hash
|
f.hash = hash
|
||||||
|
|
||||||
return contents, false, nil
|
return contents, false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Fetcher[V]) Destroy() error {
|
func (f *Fetcher[V]) Destroy() error {
|
||||||
if f.ticker != nil {
|
if f.interval > 0 {
|
||||||
f.done <- struct{}{}
|
f.done <- struct{}{}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Fetcher[V]) pullLoop() {
|
func (f *Fetcher[V]) pullLoop() {
|
||||||
|
initialInterval := f.interval - time.Since(f.UpdatedAt)
|
||||||
|
if initialInterval < minInterval {
|
||||||
|
initialInterval = minInterval
|
||||||
|
}
|
||||||
|
|
||||||
|
timer := time.NewTimer(initialInterval)
|
||||||
|
defer timer.Stop()
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-f.ticker.C:
|
case <-timer.C:
|
||||||
|
timer.Reset(f.interval)
|
||||||
elm, same, err := f.Update()
|
elm, same, err := f.Update()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorln("[Provider] %s pull error: %s", f.Name(), err.Error())
|
log.Errorln("[Provider] %s pull error: %s", f.Name(), err.Error())
|
||||||
|
@ -178,7 +190,6 @@ func (f *Fetcher[V]) pullLoop() {
|
||||||
f.OnUpdate(elm)
|
f.OnUpdate(elm)
|
||||||
}
|
}
|
||||||
case <-f.done:
|
case <-f.done:
|
||||||
f.ticker.Stop()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -197,17 +208,12 @@ func safeWrite(path string, buf []byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFetcher[V any](name string, interval time.Duration, vehicle types.Vehicle, parser Parser[V], onUpdate func(V)) *Fetcher[V] {
|
func NewFetcher[V any](name string, interval time.Duration, vehicle types.Vehicle, parser Parser[V], onUpdate func(V)) *Fetcher[V] {
|
||||||
var ticker *time.Ticker
|
|
||||||
if interval != 0 {
|
|
||||||
ticker = time.NewTicker(interval)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Fetcher[V]{
|
return &Fetcher[V]{
|
||||||
name: name,
|
name: name,
|
||||||
ticker: ticker,
|
|
||||||
vehicle: vehicle,
|
vehicle: vehicle,
|
||||||
parser: parser,
|
parser: parser,
|
||||||
done: make(chan struct{}, 1),
|
done: make(chan struct{}, 8),
|
||||||
OnUpdate: onUpdate,
|
OnUpdate: onUpdate,
|
||||||
interval: interval,
|
interval: interval,
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,6 @@ type General struct {
|
||||||
TCPConcurrent bool `json:"tcp-concurrent"`
|
TCPConcurrent bool `json:"tcp-concurrent"`
|
||||||
FindProcessMode P.FindProcessMode `json:"find-process-mode"`
|
FindProcessMode P.FindProcessMode `json:"find-process-mode"`
|
||||||
Sniffing bool `json:"sniffing"`
|
Sniffing bool `json:"sniffing"`
|
||||||
EBpf EBpf `json:"-"`
|
|
||||||
GlobalClientFingerprint string `json:"global-client-fingerprint"`
|
GlobalClientFingerprint string `json:"global-client-fingerprint"`
|
||||||
GlobalUA string `json:"global-ua"`
|
GlobalUA string `json:"global-ua"`
|
||||||
}
|
}
|
||||||
|
@ -212,11 +211,16 @@ type RawDNS struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type RawFallbackFilter struct {
|
type RawFallbackFilter struct {
|
||||||
GeoIP bool `yaml:"geoip"`
|
GeoIP bool `yaml:"geoip" json:"geoip"`
|
||||||
GeoIPCode string `yaml:"geoip-code"`
|
GeoIPCode string `yaml:"geoip-code" json:"geoip-code"`
|
||||||
IPCIDR []string `yaml:"ipcidr"`
|
IPCIDR []string `yaml:"ipcidr" json:"ipcidr"`
|
||||||
Domain []string `yaml:"domain"`
|
Domain []string `yaml:"domain" json:"domain"`
|
||||||
GeoSite []string `yaml:"geosite"`
|
GeoSite []string `yaml:"geosite" json:"geosite"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RawClashForAndroid struct {
|
||||||
|
AppendSystemDNS bool `yaml:"append-system-dns" json:"append-system-dns"`
|
||||||
|
UiSubtitlePattern string `yaml:"ui-subtitle-pattern" json:"ui-subtitle-pattern"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type RawTun struct {
|
type RawTun struct {
|
||||||
|
@ -482,7 +486,6 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) {
|
||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
config.Experimental = &rawCfg.Experimental
|
config.Experimental = &rawCfg.Experimental
|
||||||
config.Profile = &rawCfg.Profile
|
config.Profile = &rawCfg.Profile
|
||||||
config.IPTables = &rawCfg.IPTables
|
|
||||||
config.TLS = &rawCfg.RawTLS
|
config.TLS = &rawCfg.RawTLS
|
||||||
|
|
||||||
general, err := parseGeneral(rawCfg)
|
general, err := parseGeneral(rawCfg)
|
||||||
|
@ -543,11 +546,6 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) {
|
||||||
}
|
}
|
||||||
config.DNS = dnsCfg
|
config.DNS = dnsCfg
|
||||||
|
|
||||||
err = parseTun(rawCfg.Tun, config.General)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = parseTuicServer(rawCfg.TuicServer, config.General)
|
err = parseTuicServer(rawCfg.TuicServer, config.General)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -644,7 +642,6 @@ func parseGeneral(cfg *RawConfig) (*General, error) {
|
||||||
GeodataLoader: cfg.GeodataLoader,
|
GeodataLoader: cfg.GeodataLoader,
|
||||||
TCPConcurrent: cfg.TCPConcurrent,
|
TCPConcurrent: cfg.TCPConcurrent,
|
||||||
FindProcessMode: cfg.FindProcessMode,
|
FindProcessMode: cfg.FindProcessMode,
|
||||||
EBpf: cfg.EBpf,
|
|
||||||
GlobalClientFingerprint: cfg.GlobalClientFingerprint,
|
GlobalClientFingerprint: cfg.GlobalClientFingerprint,
|
||||||
GlobalUA: cfg.GlobalUA,
|
GlobalUA: cfg.GlobalUA,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
|
@ -2,7 +2,7 @@ package constant
|
||||||
|
|
||||||
var (
|
var (
|
||||||
GeodataMode bool
|
GeodataMode bool
|
||||||
GeoIpUrl string
|
GeoIpUrl = "https://testingcf.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geoip.dat"
|
||||||
MmdbUrl string
|
MmdbUrl = "https://testingcf.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/country.mmdb"
|
||||||
GeoSiteUrl string
|
GeoSiteUrl = "https://testingcf.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geosite.dat"
|
||||||
)
|
)
|
||||||
|
|
|
@ -147,6 +147,9 @@ type Metadata struct {
|
||||||
SpecialProxy string `json:"specialProxy"`
|
SpecialProxy string `json:"specialProxy"`
|
||||||
SpecialRules string `json:"specialRules"`
|
SpecialRules string `json:"specialRules"`
|
||||||
RemoteDst string `json:"remoteDestination"`
|
RemoteDst string `json:"remoteDestination"`
|
||||||
|
|
||||||
|
RawSrcAddr net.Addr `json:"-"`
|
||||||
|
RawDstAddr net.Addr `json:"-"`
|
||||||
// Only domain rule
|
// Only domain rule
|
||||||
SniffHost string `json:"sniffHost"`
|
SniffHost string `json:"sniffHost"`
|
||||||
}
|
}
|
||||||
|
|
11
constant/patch.go
Normal file
11
constant/patch.go
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package constant
|
||||||
|
|
||||||
|
import "net"
|
||||||
|
|
||||||
|
type WrappedConn interface {
|
||||||
|
RawConn() (net.Conn, bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
type WrappedPacketConn interface {
|
||||||
|
RawPacketConn() (net.PacketConn, bool)
|
||||||
|
}
|
7
context/patch.go
Normal file
7
context/patch.go
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package context
|
||||||
|
|
||||||
|
import "net"
|
||||||
|
|
||||||
|
func (c *ConnContext) RawConn() (net.Conn, bool) {
|
||||||
|
return c.conn, true
|
||||||
|
}
|
|
@ -1,3 +1,6 @@
|
||||||
|
//go:build disabled
|
||||||
|
// +build disabled
|
||||||
|
|
||||||
package dns
|
package dns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
79
dns/patch.go
Normal file
79
dns/patch.go
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
D "github.com/miekg/dns"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/common/cache"
|
||||||
|
"github.com/Dreamacro/clash/component/dhcp"
|
||||||
|
"github.com/Dreamacro/clash/component/resolver"
|
||||||
|
)
|
||||||
|
|
||||||
|
const SystemDNSPlaceholder = "system"
|
||||||
|
|
||||||
|
var systemResolver *Resolver
|
||||||
|
var isolateHandler handler
|
||||||
|
|
||||||
|
var _ dnsClient = (*dhcpClient)(nil)
|
||||||
|
|
||||||
|
type dhcpClient struct {
|
||||||
|
enable bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dhcpClient) Address() string {
|
||||||
|
return SystemDNSPlaceholder
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dhcpClient) Exchange(m *D.Msg) (msg *D.Msg, err error) {
|
||||||
|
return d.ExchangeContext(context.Background(), m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dhcpClient) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) {
|
||||||
|
if s := systemResolver; s != nil {
|
||||||
|
return s.ExchangeContext(ctx, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, dhcp.ErrNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
func ServeDNSWithDefaultServer(msg *D.Msg) (*D.Msg, error) {
|
||||||
|
if h := isolateHandler; h != nil {
|
||||||
|
return handlerWithContext(context.Background(), h, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, D.ErrTime
|
||||||
|
}
|
||||||
|
|
||||||
|
func FlushCacheWithDefaultResolver() {
|
||||||
|
if r := resolver.DefaultResolver; r != nil {
|
||||||
|
r.(*Resolver).lruCache = cache.New[string, *D.Msg](cache.WithSize[string, *D.Msg](4096), cache.WithStale[string, *D.Msg](true))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateSystemDNS(addr []string) {
|
||||||
|
if len(addr) == 0 {
|
||||||
|
systemResolver = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ns := make([]NameServer, 0, len(addr))
|
||||||
|
for _, d := range addr {
|
||||||
|
ns = append(ns, NameServer{Addr: d})
|
||||||
|
}
|
||||||
|
|
||||||
|
systemResolver = NewResolver(Config{Main: ns})
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateIsolateHandler(resolver *Resolver, mapper *ResolverEnhancer) {
|
||||||
|
if resolver == nil {
|
||||||
|
isolateHandler = nil
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
isolateHandler = NewHandler(resolver, mapper)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDHCPClient(ifaceName string) *dhcpClient {
|
||||||
|
return &dhcpClient{enable: ifaceName == SystemDNSPlaceholder}
|
||||||
|
}
|
|
@ -49,6 +49,7 @@ func (s *Server) SetHandler(handler handler) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReCreateServer(addr string, resolver *Resolver, mapper *ResolverEnhancer) {
|
func ReCreateServer(addr string, resolver *Resolver, mapper *ResolverEnhancer) {
|
||||||
|
UpdateIsolateHandler(resolver, mapper)
|
||||||
if addr == address && resolver != nil {
|
if addr == address && resolver != nil {
|
||||||
handler := NewHandler(resolver, mapper)
|
handler := NewHandler(resolver, mapper)
|
||||||
server.SetHandler(handler)
|
server.SetHandler(handler)
|
||||||
|
|
|
@ -172,7 +172,7 @@ func updateListeners(general *config.General, listeners map[string]C.InboundList
|
||||||
listener.ReCreateHTTP(general.Port, tunnel.Tunnel)
|
listener.ReCreateHTTP(general.Port, tunnel.Tunnel)
|
||||||
listener.ReCreateSocks(general.SocksPort, tunnel.Tunnel)
|
listener.ReCreateSocks(general.SocksPort, tunnel.Tunnel)
|
||||||
listener.ReCreateRedir(general.RedirPort, tunnel.Tunnel)
|
listener.ReCreateRedir(general.RedirPort, tunnel.Tunnel)
|
||||||
listener.ReCreateAutoRedir(general.EBpf.AutoRedir, tunnel.Tunnel)
|
// listener.ReCreateAutoRedir(general.EBpf.AutoRedir, tunnel.Tunnel)
|
||||||
listener.ReCreateTProxy(general.TProxyPort, tunnel.Tunnel)
|
listener.ReCreateTProxy(general.TProxyPort, tunnel.Tunnel)
|
||||||
listener.ReCreateMixed(general.MixedPort, tunnel.Tunnel)
|
listener.ReCreateMixed(general.MixedPort, tunnel.Tunnel)
|
||||||
listener.ReCreateShadowSocks(general.ShadowSocksConfig, tunnel.Tunnel)
|
listener.ReCreateShadowSocks(general.ShadowSocksConfig, tunnel.Tunnel)
|
||||||
|
|
7
listener/http/patch.go
Normal file
7
listener/http/patch.go
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package http
|
||||||
|
|
||||||
|
import "net"
|
||||||
|
|
||||||
|
func (l *Listener) Listener() net.Listener {
|
||||||
|
return l.listener
|
||||||
|
}
|
|
@ -65,6 +65,9 @@ func NewWithAuthenticate(addr string, tunnel C.Tunnel, authenticate bool, additi
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if t, ok := conn.(*net.TCPConn); ok {
|
||||||
|
t.SetKeepAlive(false)
|
||||||
|
}
|
||||||
go HandleConn(conn, tunnel, c, additions...)
|
go HandleConn(conn, tunnel, c, additions...)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
|
@ -18,7 +18,7 @@ var (
|
||||||
type ruleProviderSchema struct {
|
type ruleProviderSchema struct {
|
||||||
Type string `provider:"type"`
|
Type string `provider:"type"`
|
||||||
Behavior string `provider:"behavior"`
|
Behavior string `provider:"behavior"`
|
||||||
Path string `provider:"path,omitempty"`
|
Path string `provider:"path"`
|
||||||
URL string `provider:"url,omitempty"`
|
URL string `provider:"url,omitempty"`
|
||||||
Format string `provider:"format,omitempty"`
|
Format string `provider:"format,omitempty"`
|
||||||
Interval int `provider:"interval,omitempty"`
|
Interval int `provider:"interval,omitempty"`
|
||||||
|
@ -54,23 +54,13 @@ func ParseRuleProvider(name string, mapping map[string]interface{}, parse func(t
|
||||||
return nil, fmt.Errorf("unsupported format type: %s", schema.Format)
|
return nil, fmt.Errorf("unsupported format type: %s", schema.Format)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
path := C.Path.Resolve(schema.Path)
|
||||||
var vehicle P.Vehicle
|
var vehicle P.Vehicle
|
||||||
switch schema.Type {
|
switch schema.Type {
|
||||||
case "file":
|
case "file":
|
||||||
path := C.Path.Resolve(schema.Path)
|
|
||||||
vehicle = resource.NewFileVehicle(path)
|
vehicle = resource.NewFileVehicle(path)
|
||||||
case "http":
|
case "http":
|
||||||
if schema.Path != "" {
|
|
||||||
path := C.Path.Resolve(schema.Path)
|
|
||||||
if !C.Path.IsSafePath(path) {
|
|
||||||
return nil, fmt.Errorf("%w: %s", errSubPath, path)
|
|
||||||
}
|
|
||||||
vehicle = resource.NewHTTPVehicle(schema.URL, path)
|
vehicle = resource.NewHTTPVehicle(schema.URL, path)
|
||||||
} else {
|
|
||||||
path := C.Path.GetPathByHash("rules", schema.URL)
|
|
||||||
vehicle = resource.NewHTTPVehicle(schema.URL, path)
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unsupported vehicle type: %s", schema.Type)
|
return nil, fmt.Errorf("unsupported vehicle type: %s", schema.Type)
|
||||||
}
|
}
|
||||||
|
|
25
rules/provider/patch.go
Normal file
25
rules/provider/patch.go
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
package provider
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
var (
|
||||||
|
suspended bool
|
||||||
|
)
|
||||||
|
|
||||||
|
type UpdatableProvider interface {
|
||||||
|
UpdatedAt() time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ruleSetProvider) UpdatedAt() time.Time {
|
||||||
|
return f.Fetcher.UpdatedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rp *ruleSetProvider) Close() error {
|
||||||
|
rp.Fetcher.Destroy()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Suspend(s bool) {
|
||||||
|
suspended = s
|
||||||
|
}
|
|
@ -83,5 +83,11 @@ func closeAllLocalCoon(lAddr string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleSocket(ctx C.ConnContext, outbound net.Conn) {
|
func handleSocket(ctx C.ConnContext, outbound net.Conn) {
|
||||||
|
left := unwrap(ctx.Conn())
|
||||||
|
right := unwrap(outbound)
|
||||||
|
|
||||||
|
if relayHijack(left, right) {
|
||||||
|
return
|
||||||
|
}
|
||||||
N.Relay(ctx.Conn(), outbound)
|
N.Relay(ctx.Conn(), outbound)
|
||||||
}
|
}
|
||||||
|
|
73
tunnel/patch.go
Normal file
73
tunnel/patch.go
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
package tunnel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
|
||||||
|
C "github.com/Dreamacro/clash/constant"
|
||||||
|
)
|
||||||
|
|
||||||
|
func relayHijack(left net.Conn, right net.Conn) bool {
|
||||||
|
var l *net.TCPConn
|
||||||
|
var r *net.TCPConn
|
||||||
|
var ok bool
|
||||||
|
|
||||||
|
if l, ok = left.(*net.TCPConn); !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if r, ok = right.(*net.TCPConn); !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
closed := make(chan struct{})
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer close(closed)
|
||||||
|
|
||||||
|
r.ReadFrom(l)
|
||||||
|
r.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
l.ReadFrom(r)
|
||||||
|
l.Close()
|
||||||
|
|
||||||
|
<-closed
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func unwrap(conn net.Conn) net.Conn {
|
||||||
|
r := conn
|
||||||
|
|
||||||
|
for {
|
||||||
|
w, ok := r.(C.WrappedConn)
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
rc, ok := w.RawConn()
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
r = rc
|
||||||
|
}
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func unwrapPacket(conn net.PacketConn) net.PacketConn {
|
||||||
|
r := conn
|
||||||
|
|
||||||
|
for {
|
||||||
|
w, ok := r.(C.WrappedPacketConn)
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
rc, ok := w.RawPacketConn()
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
r = rc
|
||||||
|
}
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
25
tunnel/statistic/patch.go
Normal file
25
tunnel/statistic/patch.go
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
package statistic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (m *Manager) Total() (up, down int64) {
|
||||||
|
return m.uploadTotal.Load(), m.downloadTotal.Load()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tt *tcpTracker) RawConn() (net.Conn, bool) {
|
||||||
|
if tt.Chain.Last() == "DIRECT" {
|
||||||
|
return tt.Conn, true
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ut *udpTracker) RawPacketConn() (net.PacketConn, bool) {
|
||||||
|
if ut.Chain.Last() == "DIRECT" {
|
||||||
|
return ut.PacketConn, true
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, false
|
||||||
|
}
|
|
@ -47,6 +47,8 @@ var (
|
||||||
findProcessMode P.FindProcessMode
|
findProcessMode P.FindProcessMode
|
||||||
|
|
||||||
fakeIPRange netip.Prefix
|
fakeIPRange netip.Prefix
|
||||||
|
|
||||||
|
procesCache string
|
||||||
)
|
)
|
||||||
|
|
||||||
type tunnel struct{}
|
type tunnel struct{}
|
||||||
|
@ -627,6 +629,10 @@ func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) {
|
||||||
metadata.Process = filepath.Base(path)
|
metadata.Process = filepath.Base(path)
|
||||||
metadata.ProcessPath = path
|
metadata.ProcessPath = path
|
||||||
metadata.Uid = uid
|
metadata.Uid = uid
|
||||||
|
if procesCache != metadata.Process {
|
||||||
|
log.Debugln("[Process] %s from process %s", metadata.String(), path)
|
||||||
|
}
|
||||||
|
procesCache = metadata.Process
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue