diff --git a/adapter/inbound/http.go b/adapter/inbound/http.go index 7f3b143f..c527c8d9 100644 --- a/adapter/inbound/http.go +++ b/adapter/inbound/http.go @@ -12,6 +12,8 @@ func NewHTTP(target socks5.Addr, srcConn net.Conn, conn net.Conn, additions ...A metadata := parseSocksAddr(target) metadata.NetWork = C.TCP metadata.Type = C.HTTP + metadata.RawSrcAddr = srcConn.RemoteAddr() + metadata.RawDstAddr = srcConn.LocalAddr() ApplyAdditions(metadata, WithSrcAddr(srcConn.RemoteAddr()), WithInAddr(conn.LocalAddr())) ApplyAdditions(metadata, additions...) return conn, metadata diff --git a/adapter/inbound/packet.go b/adapter/inbound/packet.go index 0e3f6c48..07d409da 100644 --- a/adapter/inbound/packet.go +++ b/adapter/inbound/packet.go @@ -10,6 +10,8 @@ func NewPacket(target socks5.Addr, packet C.UDPPacket, source C.Type, additions metadata := parseSocksAddr(target) metadata.NetWork = C.UDP metadata.Type = source + metadata.RawSrcAddr = packet.LocalAddr() + metadata.RawDstAddr = metadata.UDPAddr() ApplyAdditions(metadata, WithSrcAddr(packet.LocalAddr())) if p, ok := packet.(C.UDPPacketInAddr); ok { ApplyAdditions(metadata, WithInAddr(p.InAddr())) diff --git a/adapter/outboundgroup/patch.go b/adapter/outboundgroup/patch.go new file mode 100644 index 00000000..9b16fe25 --- /dev/null +++ b/adapter/outboundgroup/patch.go @@ -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) +} diff --git a/adapter/provider/healthcheck.go b/adapter/provider/healthcheck.go index feb972ab..36221049 100644 --- a/adapter/provider/healthcheck.go +++ b/adapter/provider/healthcheck.go @@ -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{ diff --git a/adapter/provider/parser.go b/adapter/provider/parser.go index d885a546..a91bd7c6 100644 --- a/adapter/provider/parser.go +++ b/adapter/provider/parser.go @@ -68,9 +68,6 @@ func ParseProxyProvider(name string, mapping map[string]any) (types.ProxyProvide 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) diff --git a/adapter/provider/patch.go b/adapter/provider/patch.go new file mode 100644 index 00000000..0fe5e878 --- /dev/null +++ b/adapter/provider/patch.go @@ -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 +} diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index 3cb8bba9..59034c15 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -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{} @@ -114,6 +117,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 diff --git a/component/dialer/patch.go b/component/dialer/patch.go new file mode 100644 index 00000000..ad076df5 --- /dev/null +++ b/component/dialer/patch.go @@ -0,0 +1,37 @@ +package dialer + +import ( + "context" + "net" + "net/netip" + "syscall" +) + +type SocketControl func(network, address string, conn syscall.RawConn) error + +var DefaultSocketHook SocketControl + +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) +} diff --git a/component/mmdb/patch.go b/component/mmdb/patch.go new file mode 100644 index 00000000..67638912 --- /dev/null +++ b/component/mmdb/patch.go @@ -0,0 +1,16 @@ +package mmdb + +import "github.com/oschwald/maxminddb-golang" + +func InstallOverride(override *maxminddb.Reader) { + newReader := Reader{Reader: override} + switch override.Metadata.DatabaseType { + case "sing-geoip": + reader.databaseType = typeSing + case "Meta-geoip0": + reader.databaseType = typeMetaV0 + default: + reader.databaseType = typeMaxmind + } + reader = newReader +} diff --git a/component/process/patch.go b/component/process/patch.go new file mode 100644 index 00000000..c468061f --- /dev/null +++ b/component/process/patch.go @@ -0,0 +1,14 @@ +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 +} diff --git a/component/resource/fetcher.go b/component/resource/fetcher.go index c92687b1..d9e70018 100644 --- a/component/resource/fetcher.go +++ b/component/resource/fetcher.go @@ -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, } diff --git a/config/config.go b/config/config.go index 11951aa1..066766a2 100644 --- a/config/config.go +++ b/config/config.go @@ -58,7 +58,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"` } @@ -193,29 +192,34 @@ type RawNTP struct { } type RawDNS struct { - Enable bool `yaml:"enable"` - PreferH3 bool `yaml:"prefer-h3"` - IPv6 bool `yaml:"ipv6"` - IPv6Timeout uint `yaml:"ipv6-timeout"` - UseHosts bool `yaml:"use-hosts"` - NameServer []string `yaml:"nameserver"` - Fallback []string `yaml:"fallback"` - FallbackFilter RawFallbackFilter `yaml:"fallback-filter"` - Listen string `yaml:"listen"` - EnhancedMode C.DNSMode `yaml:"enhanced-mode"` - FakeIPRange string `yaml:"fake-ip-range"` - FakeIPFilter []string `yaml:"fake-ip-filter"` - DefaultNameserver []string `yaml:"default-nameserver"` - NameServerPolicy map[string]any `yaml:"nameserver-policy"` - ProxyServerNameserver []string `yaml:"proxy-server-nameserver"` + Enable bool `yaml:"enable" json:"enable"` + PreferH3 bool `yaml:"prefer-h3" json:"prefer-h3"` + IPv6 bool `yaml:"ipv6" json:"ipv6"` + IPv6Timeout uint `yaml:"ipv6-timeout" json:"ipv6-timeout"` + UseHosts bool `yaml:"use-hosts" json:"use-hosts"` + NameServer []string `yaml:"nameserver" json:"nameserver"` + Fallback []string `yaml:"fallback" json:"fallback"` + FallbackFilter RawFallbackFilter `yaml:"fallback-filter" json:"fallback-filter"` + Listen string `yaml:"listen" json:"listen"` + EnhancedMode C.DNSMode `yaml:"enhanced-mode" json:"enhanced-mode"` + FakeIPRange string `yaml:"fake-ip-range" json:"fake-ip-range"` + FakeIPFilter []string `yaml:"fake-ip-filter" json:"fake-ip-filter"` + DefaultNameserver []string `yaml:"default-nameserver" json:"default-nameserver"` + NameServerPolicy map[string]any `yaml:"nameserver-policy" json:"nameserver-policy"` + ProxyServerNameserver []string `yaml:"proxy-server-nameserver" json:"proxy-server-nameserver"` } 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 { @@ -263,23 +267,23 @@ type RawTuicServer struct { } type RawConfig struct { - Port int `yaml:"port"` - SocksPort int `yaml:"socks-port"` - RedirPort int `yaml:"redir-port"` - TProxyPort int `yaml:"tproxy-port"` - MixedPort int `yaml:"mixed-port"` + Port int `yaml:"port" json:"port"` + SocksPort int `yaml:"socks-port" json:"socks-port"` + RedirPort int `yaml:"redir-port" json:"redir-port"` + TProxyPort int `yaml:"tproxy-port" json:"tproxy-port"` + MixedPort int `yaml:"mixed-port" json:"mixed-port"` ShadowSocksConfig string `yaml:"ss-config"` VmessConfig string `yaml:"vmess-config"` InboundTfo bool `yaml:"inbound-tfo"` InboundMPTCP bool `yaml:"inbound-mptcp"` - Authentication []string `yaml:"authentication"` + Authentication []string `yaml:"authentication" json:"authentication"` SkipAuthPrefixes []netip.Prefix `yaml:"skip-auth-prefixes"` - AllowLan bool `yaml:"allow-lan"` - BindAddress string `yaml:"bind-address"` - Mode T.TunnelMode `yaml:"mode"` - UnifiedDelay bool `yaml:"unified-delay"` - LogLevel log.LogLevel `yaml:"log-level"` - IPv6 bool `yaml:"ipv6"` + AllowLan bool `yaml:"allow-lan" json:"allow-lan"` + BindAddress string `yaml:"bind-address" json:"bind-address"` + Mode T.TunnelMode `yaml:"mode" json:"mode"` + UnifiedDelay bool `yaml:"unified-delay" json:"unified-delay"` + LogLevel log.LogLevel `yaml:"log-level" json:"log-level"` + IPv6 bool `yaml:"ipv6" json:"ipv6"` ExternalController string `yaml:"external-controller"` ExternalControllerTLS string `yaml:"external-controller-tls"` ExternalUI string `yaml:"external-ui"` @@ -289,20 +293,20 @@ type RawConfig struct { Interface string `yaml:"interface-name"` RoutingMark int `yaml:"routing-mark"` Tunnels []LC.Tunnel `yaml:"tunnels"` - GeodataMode bool `yaml:"geodata-mode"` - GeodataLoader string `yaml:"geodata-loader"` + GeodataMode bool `yaml:"geodata-mode" json:"geodata-mode"` + GeodataLoader string `yaml:"geodata-loader" json:"geodata-loader"` TCPConcurrent bool `yaml:"tcp-concurrent" json:"tcp-concurrent"` FindProcessMode P.FindProcessMode `yaml:"find-process-mode" json:"find-process-mode"` GlobalClientFingerprint string `yaml:"global-client-fingerprint"` GlobalUA string `yaml:"global-ua"` KeepAliveInterval int `yaml:"keep-alive-interval"` - Sniffer RawSniffer `yaml:"sniffer"` + Sniffer RawSniffer `yaml:"sniffer" json:"sniffer"` ProxyProvider map[string]map[string]any `yaml:"proxy-providers"` RuleProvider map[string]map[string]any `yaml:"rule-providers"` - Hosts map[string]any `yaml:"hosts"` - NTP RawNTP `yaml:"ntp"` - DNS RawDNS `yaml:"dns"` + Hosts map[string]any `yaml:"hosts" json:"hosts"` + NTP RawNTP `yaml:"ntp" json:"ntp"` + DNS RawDNS `yaml:"dns" json:"dns"` Tun RawTun `yaml:"tun"` TuicServer RawTuicServer `yaml:"tuic-server"` EBpf EBpf `yaml:"ebpf"` @@ -316,6 +320,8 @@ type RawConfig struct { SubRules map[string][]string `yaml:"sub-rules"` RawTLS TLS `yaml:"tls"` Listeners []map[string]any `yaml:"listeners"` + + ClashForAndroid RawClashForAndroid `yaml:"clash-for-android" json:"clash-for-android"` } type GeoXUrl struct { @@ -483,7 +489,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) @@ -544,11 +549,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 @@ -645,7 +645,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 diff --git a/constant/geodata.go b/constant/geodata.go index 72452270..538df090 100644 --- a/constant/geodata.go +++ b/constant/geodata.go @@ -2,7 +2,7 @@ package constant var ( GeodataMode bool - GeoIpUrl string - MmdbUrl string - GeoSiteUrl string + GeoIpUrl = "https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geoip.dat" + MmdbUrl = "https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geoip.metadb" + GeoSiteUrl = "https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geosite.dat" ) diff --git a/constant/metadata.go b/constant/metadata.go index 5f472205..9c97899c 100644 --- a/constant/metadata.go +++ b/constant/metadata.go @@ -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"` } diff --git a/dns/dhcp.go b/dns/dhcp.go index 70f9aeeb..269c6df4 100644 --- a/dns/dhcp.go +++ b/dns/dhcp.go @@ -1,3 +1,6 @@ +//go:build disabled +// +build disabled + package dns import ( diff --git a/dns/patch.go b/dns/patch.go new file mode 100644 index 00000000..311a57fb --- /dev/null +++ b/dns/patch.go @@ -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} +} diff --git a/dns/server.go b/dns/server.go index 5c5970db..314536e2 100644 --- a/dns/server.go +++ b/dns/server.go @@ -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) diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 87e0e0b1..5ab4b1cf 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -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) diff --git a/listener/http/patch.go b/listener/http/patch.go new file mode 100644 index 00000000..bba87301 --- /dev/null +++ b/listener/http/patch.go @@ -0,0 +1,7 @@ +package http + +import "net" + +func (l *Listener) Listener() net.Listener { + return l.listener +} diff --git a/listener/http/server.go b/listener/http/server.go index 0377d3b6..85b5cbed 100644 --- a/listener/http/server.go +++ b/listener/http/server.go @@ -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...) } }() diff --git a/listener/inner/tcp.go b/listener/inner/tcp.go index 8973c431..653d0c86 100644 --- a/listener/inner/tcp.go +++ b/listener/inner/tcp.go @@ -27,6 +27,8 @@ func HandleTcp(address string) (conn net.Conn, err error) { metadata.Type = C.INNER metadata.DNSMode = C.DNSNormal metadata.Process = C.ClashName + metadata.RawSrcAddr = conn1.RemoteAddr() + metadata.RawDstAddr = conn1.LocalAddr() if h, port, err := net.SplitHostPort(address); err == nil { if port, err := strconv.ParseUint(port, 10, 16); err == nil { metadata.DstPort = uint16(port) diff --git a/rules/provider/parse.go b/rules/provider/parse.go index 0fbfb2cc..81b9cb49 100644 --- a/rules/provider/parse.go +++ b/rules/provider/parse.go @@ -62,15 +62,11 @@ func ParseRuleProvider(name string, mapping map[string]interface{}, parse func(t 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) } - default: return nil, fmt.Errorf("unsupported vehicle type: %s", schema.Type) } diff --git a/rules/provider/patch.go b/rules/provider/patch.go new file mode 100644 index 00000000..f44ebf00 --- /dev/null +++ b/rules/provider/patch.go @@ -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 +} diff --git a/tunnel/statistic/patch.go b/tunnel/statistic/patch.go new file mode 100644 index 00000000..c1e345fd --- /dev/null +++ b/tunnel/statistic/patch.go @@ -0,0 +1,5 @@ +package statistic + +func (m *Manager) Total() (up, down int64) { + return m.uploadTotal.Load(), m.downloadTotal.Load() +} diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index fe37d75e..bbcf203b 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -5,7 +5,6 @@ import ( "fmt" "net" "net/netip" - "path/filepath" "runtime" "sync" "time" @@ -47,6 +46,8 @@ var ( findProcessMode P.FindProcessMode fakeIPRange netip.Prefix + + procesCache string ) type tunnel struct{} @@ -593,8 +594,8 @@ func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) { configMux.RLock() defer configMux.RUnlock() var ( - resolved bool - attemptProcessLookup = true + resolved bool + processFound bool ) if node, ok := resolver.DefaultHosts.Search(metadata.Host, false); ok { @@ -618,15 +619,17 @@ func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) { }() } - if attemptProcessLookup && !findProcessMode.Off() && (findProcessMode.Always() || rule.ShouldFindProcess()) { - attemptProcessLookup = false - uid, path, err := P.FindProcessName(metadata.NetWork.String(), metadata.SrcIP, int(metadata.SrcPort)) + if !findProcessMode.Off() && !processFound && (findProcessMode.Always() || rule.ShouldFindProcess()) { + pkg, err := P.FindPackageName(metadata) if err != nil { log.Debugln("[Process] find process %s: %v", metadata.String(), err) } else { - metadata.Process = filepath.Base(path) - metadata.ProcessPath = path - metadata.Uid = uid + metadata.Process = pkg + processFound = true + if procesCache != metadata.Process { + log.Debugln("[Process] %s from process %s", metadata.String(), metadata.Process) + } + procesCache = metadata.Process } }