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
|
||||
ApplyAdditions(metadata, WithSrcAddr(packet.LocalAddr()))
|
||||
if p, ok := packet.(C.UDPPacketInAddr); ok {
|
||||
ApplyAdditions(metadata, WithInAddr(p.InAddr()))
|
||||
ApplyAdditions(metadata, WithInAddr(p.InAddr()), WithDstAddr(metadata.RawDstAddr))
|
||||
}
|
||||
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 (
|
||||
defaultURLTestTimeout = time.Second * 5
|
||||
defaultURLTestURL = "https://www.gstatic.com/generate_204"
|
||||
)
|
||||
|
||||
type HealthCheckOption struct {
|
||||
|
@ -148,6 +149,11 @@ func (hc *HealthCheck) stop() {
|
|||
}
|
||||
|
||||
func (hc *HealthCheck) check() {
|
||||
|
||||
if len(hc.proxies) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
_, _, _ = hc.singleDo.Do(func() (struct{}, error) {
|
||||
id := utils.NewUUIDV4().String()
|
||||
log.Debugln("Start New Health Checking {%s}", id)
|
||||
|
@ -223,6 +229,7 @@ func NewHealthCheck(proxies []C.Proxy, url string, interval uint, lazy bool, exp
|
|||
if len(url) == 0 {
|
||||
interval = 0
|
||||
expectedStatus = nil
|
||||
url = defaultURLTestURL
|
||||
}
|
||||
|
||||
return &HealthCheck{
|
||||
|
|
|
@ -27,7 +27,7 @@ type healthCheckSchema struct {
|
|||
|
||||
type proxyProviderSchema struct {
|
||||
Type string `provider:"type"`
|
||||
Path string `provider:"path,omitempty"`
|
||||
Path string `provider:"path"`
|
||||
URL string `provider:"url,omitempty"`
|
||||
Interval int `provider:"interval,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)
|
||||
}
|
||||
hc := NewHealthCheck([]C.Proxy{}, schema.HealthCheck.URL, hcInterval, schema.HealthCheck.Lazy, expectedStatus)
|
||||
path := C.Path.Resolve(schema.Path)
|
||||
|
||||
var vehicle types.Vehicle
|
||||
switch schema.Type {
|
||||
case "file":
|
||||
path := C.Path.Resolve(schema.Path)
|
||||
vehicle = resource.NewFileVehicle(path)
|
||||
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)
|
||||
} else {
|
||||
path := C.Path.GetPathByHash("proxies", schema.URL)
|
||||
vehicle = resource.NewHTTPVehicle(schema.URL, path)
|
||||
}
|
||||
vehicle = resource.NewHTTPVehicle(schema.URL, path)
|
||||
default:
|
||||
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) {
|
||||
if DefaultSocketHook != nil {
|
||||
return listenPacketHooked(ctx, network, address)
|
||||
}
|
||||
cfg := applyOptions(options...)
|
||||
|
||||
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) {
|
||||
if DefaultSocketHook != nil {
|
||||
return dialContextHooked(ctx, network, destination, port)
|
||||
}
|
||||
address := net.JoinHostPort(destination.String(), port)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func Instance() Reader {
|
||||
func DefaultInstance() Reader {
|
||||
once.Do(func() {
|
||||
mmdbPath := C.Path.MMDB()
|
||||
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"
|
||||
)
|
||||
|
||||
const (
|
||||
minInterval = time.Minute * 5
|
||||
)
|
||||
|
||||
var (
|
||||
fileMode os.FileMode = 0o666
|
||||
dirMode os.FileMode = 0o755
|
||||
|
@ -24,8 +28,7 @@ type Fetcher[V any] struct {
|
|||
resourceType string
|
||||
name string
|
||||
vehicle types.Vehicle
|
||||
UpdatedAt *time.Time
|
||||
ticker *time.Ticker
|
||||
UpdatedAt time.Time
|
||||
done chan struct{}
|
||||
hash [16]byte
|
||||
parser Parser[V]
|
||||
|
@ -56,7 +59,7 @@ func (f *Fetcher[V]) Initial() (V, error) {
|
|||
if stat, fErr := os.Stat(f.vehicle.Path()); fErr == nil {
|
||||
buf, err = os.ReadFile(f.vehicle.Path())
|
||||
modTime := stat.ModTime()
|
||||
f.UpdatedAt = &modTime
|
||||
f.UpdatedAt = modTime
|
||||
isLocal = true
|
||||
if f.interval != 0 && modTime.Add(f.interval).Before(time.Now()) {
|
||||
log.Warnln("[Provider] %s not updated for a long time, force refresh", f.Name())
|
||||
|
@ -64,6 +67,7 @@ func (f *Fetcher[V]) Initial() (V, error) {
|
|||
}
|
||||
} else {
|
||||
buf, err = f.vehicle.Read()
|
||||
f.UpdatedAt = time.Now()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
@ -113,7 +117,7 @@ func (f *Fetcher[V]) Initial() (V, error) {
|
|||
f.hash = md5.Sum(buf)
|
||||
|
||||
// pull contents automatically
|
||||
if f.ticker != nil {
|
||||
if f.interval > 0 {
|
||||
go f.pullLoop()
|
||||
}
|
||||
|
||||
|
@ -129,7 +133,7 @@ func (f *Fetcher[V]) Update() (V, bool, error) {
|
|||
now := time.Now()
|
||||
hash := md5.Sum(buf)
|
||||
if bytes.Equal(f.hash[:], hash[:]) {
|
||||
f.UpdatedAt = &now
|
||||
f.UpdatedAt = now
|
||||
_ = os.Chtimes(f.vehicle.Path(), now, now)
|
||||
return lo.Empty[V](), true, nil
|
||||
}
|
||||
|
@ -145,23 +149,31 @@ func (f *Fetcher[V]) Update() (V, bool, error) {
|
|||
}
|
||||
}
|
||||
|
||||
f.UpdatedAt = &now
|
||||
f.UpdatedAt = now
|
||||
f.hash = hash
|
||||
|
||||
return contents, false, nil
|
||||
}
|
||||
|
||||
func (f *Fetcher[V]) Destroy() error {
|
||||
if f.ticker != nil {
|
||||
if f.interval > 0 {
|
||||
f.done <- struct{}{}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Fetcher[V]) pullLoop() {
|
||||
initialInterval := f.interval - time.Since(f.UpdatedAt)
|
||||
if initialInterval < minInterval {
|
||||
initialInterval = minInterval
|
||||
}
|
||||
|
||||
timer := time.NewTimer(initialInterval)
|
||||
defer timer.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-f.ticker.C:
|
||||
case <-timer.C:
|
||||
timer.Reset(f.interval)
|
||||
elm, same, err := f.Update()
|
||||
if err != nil {
|
||||
log.Errorln("[Provider] %s pull error: %s", f.Name(), err.Error())
|
||||
|
@ -178,7 +190,6 @@ func (f *Fetcher[V]) pullLoop() {
|
|||
f.OnUpdate(elm)
|
||||
}
|
||||
case <-f.done:
|
||||
f.ticker.Stop()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -197,17 +208,12 @@ func safeWrite(path string, buf []byte) error {
|
|||
}
|
||||
|
||||
func NewFetcher[V any](name string, interval time.Duration, vehicle types.Vehicle, parser Parser[V], onUpdate func(V)) *Fetcher[V] {
|
||||
var ticker *time.Ticker
|
||||
if interval != 0 {
|
||||
ticker = time.NewTicker(interval)
|
||||
}
|
||||
|
||||
return &Fetcher[V]{
|
||||
name: name,
|
||||
ticker: ticker,
|
||||
vehicle: vehicle,
|
||||
parser: parser,
|
||||
done: make(chan struct{}, 1),
|
||||
done: make(chan struct{}, 8),
|
||||
OnUpdate: onUpdate,
|
||||
interval: interval,
|
||||
}
|
||||
|
|
|
@ -59,7 +59,6 @@ type General struct {
|
|||
TCPConcurrent bool `json:"tcp-concurrent"`
|
||||
FindProcessMode P.FindProcessMode `json:"find-process-mode"`
|
||||
Sniffing bool `json:"sniffing"`
|
||||
EBpf EBpf `json:"-"`
|
||||
GlobalClientFingerprint string `json:"global-client-fingerprint"`
|
||||
GlobalUA string `json:"global-ua"`
|
||||
}
|
||||
|
@ -212,11 +211,16 @@ type RawDNS struct {
|
|||
}
|
||||
|
||||
type RawFallbackFilter struct {
|
||||
GeoIP bool `yaml:"geoip"`
|
||||
GeoIPCode string `yaml:"geoip-code"`
|
||||
IPCIDR []string `yaml:"ipcidr"`
|
||||
Domain []string `yaml:"domain"`
|
||||
GeoSite []string `yaml:"geosite"`
|
||||
GeoIP bool `yaml:"geoip" json:"geoip"`
|
||||
GeoIPCode string `yaml:"geoip-code" json:"geoip-code"`
|
||||
IPCIDR []string `yaml:"ipcidr" json:"ipcidr"`
|
||||
Domain []string `yaml:"domain" json:"domain"`
|
||||
GeoSite []string `yaml:"geosite" json:"geosite"`
|
||||
}
|
||||
|
||||
type RawClashForAndroid struct {
|
||||
AppendSystemDNS bool `yaml:"append-system-dns" json:"append-system-dns"`
|
||||
UiSubtitlePattern string `yaml:"ui-subtitle-pattern" json:"ui-subtitle-pattern"`
|
||||
}
|
||||
|
||||
type RawTun struct {
|
||||
|
@ -482,7 +486,6 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) {
|
|||
startTime := time.Now()
|
||||
config.Experimental = &rawCfg.Experimental
|
||||
config.Profile = &rawCfg.Profile
|
||||
config.IPTables = &rawCfg.IPTables
|
||||
config.TLS = &rawCfg.RawTLS
|
||||
|
||||
general, err := parseGeneral(rawCfg)
|
||||
|
@ -543,11 +546,6 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) {
|
|||
}
|
||||
config.DNS = dnsCfg
|
||||
|
||||
err = parseTun(rawCfg.Tun, config.General)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = parseTuicServer(rawCfg.TuicServer, config.General)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -644,7 +642,6 @@ func parseGeneral(cfg *RawConfig) (*General, error) {
|
|||
GeodataLoader: cfg.GeodataLoader,
|
||||
TCPConcurrent: cfg.TCPConcurrent,
|
||||
FindProcessMode: cfg.FindProcessMode,
|
||||
EBpf: cfg.EBpf,
|
||||
GlobalClientFingerprint: cfg.GlobalClientFingerprint,
|
||||
GlobalUA: cfg.GlobalUA,
|
||||
}, nil
|
||||
|
|
|
@ -2,7 +2,7 @@ package constant
|
|||
|
||||
var (
|
||||
GeodataMode bool
|
||||
GeoIpUrl string
|
||||
MmdbUrl string
|
||||
GeoSiteUrl string
|
||||
GeoIpUrl = "https://testingcf.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geoip.dat"
|
||||
MmdbUrl = "https://testingcf.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/country.mmdb"
|
||||
GeoSiteUrl = "https://testingcf.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geosite.dat"
|
||||
)
|
||||
|
|
|
@ -147,6 +147,9 @@ type Metadata struct {
|
|||
SpecialProxy string `json:"specialProxy"`
|
||||
SpecialRules string `json:"specialRules"`
|
||||
RemoteDst string `json:"remoteDestination"`
|
||||
|
||||
RawSrcAddr net.Addr `json:"-"`
|
||||
RawDstAddr net.Addr `json:"-"`
|
||||
// Only domain rule
|
||||
SniffHost string `json:"sniffHost"`
|
||||
}
|
||||
|
|
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
|
||||
|
||||
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) {
|
||||
UpdateIsolateHandler(resolver, mapper)
|
||||
if addr == address && resolver != nil {
|
||||
handler := NewHandler(resolver, mapper)
|
||||
server.SetHandler(handler)
|
||||
|
|
|
@ -172,7 +172,7 @@ func updateListeners(general *config.General, listeners map[string]C.InboundList
|
|||
listener.ReCreateHTTP(general.Port, tunnel.Tunnel)
|
||||
listener.ReCreateSocks(general.SocksPort, tunnel.Tunnel)
|
||||
listener.ReCreateRedir(general.RedirPort, tunnel.Tunnel)
|
||||
listener.ReCreateAutoRedir(general.EBpf.AutoRedir, tunnel.Tunnel)
|
||||
// listener.ReCreateAutoRedir(general.EBpf.AutoRedir, tunnel.Tunnel)
|
||||
listener.ReCreateTProxy(general.TProxyPort, tunnel.Tunnel)
|
||||
listener.ReCreateMixed(general.MixedPort, tunnel.Tunnel)
|
||||
listener.ReCreateShadowSocks(general.ShadowSocksConfig, tunnel.Tunnel)
|
||||
|
|
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
|
||||
}
|
||||
if t, ok := conn.(*net.TCPConn); ok {
|
||||
t.SetKeepAlive(false)
|
||||
}
|
||||
go HandleConn(conn, tunnel, c, additions...)
|
||||
}
|
||||
}()
|
||||
|
|
|
@ -18,7 +18,7 @@ var (
|
|||
type ruleProviderSchema struct {
|
||||
Type string `provider:"type"`
|
||||
Behavior string `provider:"behavior"`
|
||||
Path string `provider:"path,omitempty"`
|
||||
Path string `provider:"path"`
|
||||
URL string `provider:"url,omitempty"`
|
||||
Format string `provider:"format,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)
|
||||
}
|
||||
|
||||
path := C.Path.Resolve(schema.Path)
|
||||
var vehicle P.Vehicle
|
||||
switch schema.Type {
|
||||
case "file":
|
||||
path := C.Path.Resolve(schema.Path)
|
||||
vehicle = resource.NewFileVehicle(path)
|
||||
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)
|
||||
} else {
|
||||
path := C.Path.GetPathByHash("rules", schema.URL)
|
||||
vehicle = resource.NewHTTPVehicle(schema.URL, path)
|
||||
}
|
||||
|
||||
vehicle = resource.NewHTTPVehicle(schema.URL, path)
|
||||
default:
|
||||
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) {
|
||||
left := unwrap(ctx.Conn())
|
||||
right := unwrap(outbound)
|
||||
|
||||
if relayHijack(left, right) {
|
||||
return
|
||||
}
|
||||
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
|
||||
|
||||
fakeIPRange netip.Prefix
|
||||
|
||||
procesCache string
|
||||
)
|
||||
|
||||
type tunnel struct{}
|
||||
|
@ -627,6 +629,10 @@ func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) {
|
|||
metadata.Process = filepath.Base(path)
|
||||
metadata.ProcessPath = path
|
||||
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