Android: patch

This commit is contained in:
wwqgtxx 2023-10-23 16:50:43 +08:00
parent 79a29826a4
commit a551d7ebd9
30 changed files with 512 additions and 59 deletions

View file

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

View file

@ -0,0 +1,7 @@
package outbound
import "net"
func (c *conn) RawConn() (net.Conn, bool) {
return c.ExtendedConn, true
}

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

View file

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

View file

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

View file

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

View file

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

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

View file

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

View file

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

View file

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

View file

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

@ -0,0 +1,7 @@
package context
import "net"
func (c *ConnContext) RawConn() (net.Conn, bool) {
return c.conn, true
}

View file

@ -1,3 +1,6 @@
//go:build disabled
// +build disabled
package dns package dns
import ( import (

79
dns/patch.go Normal file
View 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}
}

View file

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

View file

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

@ -0,0 +1,7 @@
package http
import "net"
func (l *Listener) Listener() net.Listener {
return l.listener
}

View file

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

View file

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

View file

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

View file

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