From 1cf4f04df9873623532f9d0ca8effec735211ba5 Mon Sep 17 00:00:00 2001 From: tanmingxin <1121170088@qq.com> Date: Fri, 24 May 2024 23:14:11 +0800 Subject: [PATCH] update --- adapter/inbound/myinbound.go | 114 ++++++ adapter/outbound/trojan.go | 43 --- adapter/provider/provider.go | 18 +- common/convert/converter.go | 2 +- compile/build_linux_amd64.bat | 8 + compile/build_linux_arm.bat | 8 + compile/build_linux_arm64.bat | 8 + compile/build_windows.bat | 9 + component/dialer/dialer.go | 13 +- component/dialer/reject_conn.go | 23 ++ component/proxydialer/proxydialer.go | 24 +- config/config.go | 58 +++- config/initial.go | 30 +- config/update_geo.go | 90 +++++ config/update_ui.go | 145 ++++++++ config/utils.go | 23 ++ constant/metadata.go | 9 + constant/path.go | 25 +- constant/rule.go | 12 + docs/config.yaml | 156 ++++----- go.mod | 10 + go.sum | 35 ++ hub/executor/executor.go | 20 +- hub/hub.go | 2 +- hub/route/configs.go | 50 ++- hub/route/connections.go | 27 +- hub/route/server.go | 107 ++++-- hub/route/upgrade.go | 10 +- hub/updater/limitedreader.go | 67 ++++ hub/updater/updater.go | 496 +++++++++++++++++++++++++++ listener/auth/auth.go | 8 +- listener/mixed/conn_wrap.go | 96 ++++++ listener/mixed/handle_tls.go | 202 +++++++++++ listener/mixed/sshd.go | 210 ++++++++++++ listener/sing_tun/server.go | 12 +- listener/socks/tcp.go | 20 +- listener/tls_listener.go | 40 +++ listener/tproxy/packet.go | 2 +- log/log.go | 45 ++- main.go | 87 +++-- rules/common/domain_regex.go | 3 +- rules/common/fileDi.go | 63 ++++ rules/common/fileDiRecord.go | 227 ++++++++++++ rules/common/geoip.go | 1 - rules/common/geoip_iso_empty.go | 53 +++ rules/common/must_resolve.go | 42 +++ rules/domain/domain.go | 64 ++++ rules/domain/domain_test.go | 63 ++++ rules/domain/sskv.go | 186 ++++++++++ rules/parser.go | 8 + service/service.go | 246 +++++++++++++ test/go.mod | 114 +++--- test/go.sum | 124 +++++++ tunnel/statistic/manager.go | 120 ++++--- tunnel/statistic/tracker.go | 6 +- tunnel/tunnel.go | 56 ++- 56 files changed, 3343 insertions(+), 397 deletions(-) create mode 100644 adapter/inbound/myinbound.go create mode 100644 compile/build_linux_amd64.bat create mode 100644 compile/build_linux_arm.bat create mode 100644 compile/build_linux_arm64.bat create mode 100644 compile/build_windows.bat create mode 100644 component/dialer/reject_conn.go create mode 100644 config/update_geo.go create mode 100644 config/update_ui.go create mode 100644 hub/updater/limitedreader.go create mode 100644 hub/updater/updater.go create mode 100644 listener/mixed/conn_wrap.go create mode 100644 listener/mixed/handle_tls.go create mode 100644 listener/mixed/sshd.go create mode 100644 listener/tls_listener.go create mode 100644 rules/common/fileDi.go create mode 100644 rules/common/fileDiRecord.go create mode 100644 rules/common/geoip_iso_empty.go create mode 100644 rules/common/must_resolve.go create mode 100644 rules/domain/domain.go create mode 100644 rules/domain/domain_test.go create mode 100644 rules/domain/sskv.go create mode 100644 service/service.go diff --git a/adapter/inbound/myinbound.go b/adapter/inbound/myinbound.go new file mode 100644 index 00000000..f7d70338 --- /dev/null +++ b/adapter/inbound/myinbound.go @@ -0,0 +1,114 @@ +package inbound + +import ( + "fmt" + C "github.com/metacubex/mihomo/constant" + gossh "golang.org/x/crypto/ssh" + "net" + "net/netip" + "time" +) + +type LocalForwardChannelData struct { + DestAddr string + DestPort uint32 + + OriginAddr string + OriginPort uint32 +} + +type MySSHConn struct { + gossh.Channel + LocalAddr_ net.Addr + RemoteAddr_ net.Addr +} + +var channelCloseBytes = [1]byte{97} + +func (m *MySSHConn) Close() error { + //m.Write(channelCloseBytes[:]) + return m.Channel.Close() +} + +func (m *MySSHConn) LocalAddr() net.Addr { + return m.LocalAddr_ +} + +func (m *MySSHConn) RemoteAddr() net.Addr { + return m.RemoteAddr_ +} + +func (m *MySSHConn) SetDeadline(t time.Time) error { + return nil +} + +func (m *MySSHConn) SetReadDeadline(t time.Time) error { + return nil +} + +func (m *MySSHConn) SetWriteDeadline(t time.Time) error { + return nil +} +func NewSSH(data *LocalForwardChannelData, conn net.Conn, source C.Type) *C.Metadata { + metadata := &C.Metadata{} + metadata.NetWork = C.TCP + metadata.Host = data.DestAddr + metadata.DstPort = uint16(data.DestPort) + + if ip := net.ParseIP(metadata.Host); ip != nil { + addr, _ := netip.AddrFromSlice([]byte(ip)) + metadata.DstIP = addr + } + + metadata.Type = source + if ip, port, err := parseAddr(conn.RemoteAddr()); err == nil { + addr, _ := netip.AddrFromSlice([]byte(ip)) + metadata.SrcIP = addr + metadata.SrcPort = uint16(port) + } + + return metadata +} + +type TLSProxy struct { + Cert string `yaml:"cert"` + Key string `yaml:"key"` + Socks5Proxy *Socks5Proxy `yaml:"socks5"` + TlsTargets map[string]*TlsTarget `yaml:"proxy"` +} + +type TlsTarget struct { + ProxyProto bool `yaml:"proxyproto"` + Target string `yaml:"target"` +} + +type Socks5Proxy struct { +} + +type SSHProxy struct { + ProxyProto bool `yaml:"proxyproto"` + Target string `yaml:"target"` +} + +type SynDrive struct { + Target string `yaml:"target"` +} + +type WanInput struct { + Port int `yaml:"port"` + Authentication []string `yaml:"authentication"` + SshProxy *SSHProxy `yaml:"ssh"` + TlsProxy *TLSProxy `yaml:"tls"` + Syndrive *SynDrive `yaml:"syn-drive"` +} + +func parseAddr(addr net.Addr) (net.IP, int, error) { + switch a := addr.(type) { + case *net.TCPAddr: + return a.IP, a.Port, nil + case *net.UDPAddr: + return a.IP, a.Port, nil + default: + return nil, 0, fmt.Errorf("unknown address type %s", addr.String()) + } +} diff --git a/adapter/outbound/trojan.go b/adapter/outbound/trojan.go index 938a8858..b14761a4 100644 --- a/adapter/outbound/trojan.go +++ b/adapter/outbound/trojan.go @@ -3,7 +3,6 @@ package outbound import ( "context" "crypto/tls" - "errors" "fmt" "net" "net/http" @@ -16,7 +15,6 @@ import ( tlsC "github.com/metacubex/mihomo/component/tls" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/transport/gun" - "github.com/metacubex/mihomo/transport/shadowsocks/core" "github.com/metacubex/mihomo/transport/trojan" ) @@ -31,8 +29,6 @@ type Trojan struct { transport *gun.TransportWrap realityConfig *tlsC.RealityConfig - - ssCipher core.Cipher } type TrojanOption struct { @@ -50,17 +46,9 @@ type TrojanOption struct { RealityOpts RealityOptions `proxy:"reality-opts,omitempty"` GrpcOpts GrpcOptions `proxy:"grpc-opts,omitempty"` WSOpts WSOptions `proxy:"ws-opts,omitempty"` - SSOpts TrojanSSOption `proxy:"ss-opts,omitempty"` ClientFingerprint string `proxy:"client-fingerprint,omitempty"` } -// TrojanSSOption from https://github.com/p4gefau1t/trojan-go/blob/v0.10.6/tunnel/shadowsocks/config.go#L5 -type TrojanSSOption struct { - Enabled bool `proxy:"enabled,omitempty"` - Method string `proxy:"method,omitempty"` - Password string `proxy:"password,omitempty"` -} - func (t *Trojan) plainStream(ctx context.Context, c net.Conn) (net.Conn, error) { if t.option.Network == "ws" { host, port, _ := net.SplitHostPort(t.addr) @@ -107,10 +95,6 @@ func (t *Trojan) StreamConnContext(ctx context.Context, c net.Conn, metadata *C. return nil, fmt.Errorf("%s connect error: %w", t.addr, err) } - if t.ssCipher != nil { - c = t.ssCipher.StreamConn(c) - } - if metadata.NetWork == C.UDP { err = t.instance.WriteHeader(c, trojan.CommandUDP, serializesSocksAddr(metadata)) return c, err @@ -128,10 +112,6 @@ func (t *Trojan) DialContext(ctx context.Context, metadata *C.Metadata, opts ... return nil, err } - if t.ssCipher != nil { - c = t.ssCipher.StreamConn(c) - } - if err = t.instance.WriteHeader(c, trojan.CommandTCP, serializesSocksAddr(metadata)); err != nil { c.Close() return nil, err @@ -181,11 +161,6 @@ func (t *Trojan) ListenPacketContext(ctx context.Context, metadata *C.Metadata, defer func(c net.Conn) { safeConnClose(c, err) }(c) - - if t.ssCipher != nil { - c = t.ssCipher.StreamConn(c) - } - err = t.instance.WriteHeader(c, trojan.CommandUDP, serializesSocksAddr(metadata)) if err != nil { return nil, err @@ -218,10 +193,6 @@ func (t *Trojan) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, me return nil, fmt.Errorf("%s connect error: %w", t.addr, err) } - if t.ssCipher != nil { - c = t.ssCipher.StreamConn(c) - } - err = t.instance.WriteHeader(c, trojan.CommandUDP, serializesSocksAddr(metadata)) if err != nil { return nil, err @@ -286,20 +257,6 @@ func NewTrojan(option TrojanOption) (*Trojan, error) { } tOption.Reality = t.realityConfig - if option.SSOpts.Enabled { - if option.SSOpts.Password == "" { - return nil, errors.New("empty password") - } - if option.SSOpts.Method == "" { - option.SSOpts.Method = "AES-128-GCM" - } - ciph, err := core.PickCipher(option.SSOpts.Method, nil, option.SSOpts.Password) - if err != nil { - return nil, err - } - t.ssCipher = ciph - } - if option.Network == "grpc" { dialFn := func(network, addr string) (net.Conn, error) { var err error diff --git a/adapter/provider/provider.go b/adapter/provider/provider.go index daef017c..206b9559 100644 --- a/adapter/provider/provider.go +++ b/adapter/provider/provider.go @@ -152,15 +152,17 @@ func (pp *proxySetProvider) getSubscriptionInfo() { } func (pp *proxySetProvider) closeAllConnections() { - statistic.DefaultManager.Range(func(c statistic.Tracker) bool { - for _, chain := range c.Chains() { - if chain == pp.Name() { - _ = c.Close() - break + for _, v := range statistic.ChannelManager { + v.Range(func(c statistic.Tracker) bool { + for _, chain := range c.Chains() { + if chain == pp.Name() { + _ = c.Close() + break + } } - } - return true - }) + return true + }) + } } func stopProxyProvider(pd *ProxySetProvider) { diff --git a/common/convert/converter.go b/common/convert/converter.go index 19886d25..222dd9fa 100644 --- a/common/convert/converter.go +++ b/common/convert/converter.go @@ -333,7 +333,7 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) { case "ws", "httpupgrade": headers := make(map[string]any) wsOpts := make(map[string]any) - wsOpts["path"] = "/" + wsOpts["path"] = []string{"/"} if host, ok := values["host"]; ok && host != "" { headers["Host"] = host.(string) } diff --git a/compile/build_linux_amd64.bat b/compile/build_linux_amd64.bat new file mode 100644 index 00000000..80314f38 --- /dev/null +++ b/compile/build_linux_amd64.bat @@ -0,0 +1,8 @@ +ECHO %date:~0,4%-%date:~5,2%-%date:~8,2% %time:~0,2%:%time:~3,2%:%time:~6,2% +set RELEASE=%date:~0,4%-%date:~5,2%-%date:~8,2% %time:~0,2%:%time:~3,2%:%time:~6,2% +ECHO %RELEASE% +set GOOS=linux +set CGO_ENABLED=0 +set GOARCH=amd64 + +go build -o clash -trimpath -ldflags="-X 'github.com/metacubex/mihomo/constant.Version=%RELEASE%' -X 'github.com/metacubex/mihomo/constant.BuildTime=%RELEASE%' -w -s" .. \ No newline at end of file diff --git a/compile/build_linux_arm.bat b/compile/build_linux_arm.bat new file mode 100644 index 00000000..443d2a4d --- /dev/null +++ b/compile/build_linux_arm.bat @@ -0,0 +1,8 @@ +ECHO %date:~0,4%-%date:~5,2%-%date:~8,2% %time:~0,2%:%time:~3,2%:%time:~6,2% +set RELEASE=%date:~0,4%-%date:~5,2%-%date:~8,2% %time:~0,2%:%time:~3,2%:%time:~6,2% +ECHO %RELEASE% +set GOOS=linux +set CGO_ENABLED=0 +set GOARCH=arm + +go build -o clash -trimpath -ldflags="-X 'github.com/metacubex/mihomo/constant.Version=%RELEASE%' -X 'github.com/metacubex/mihomo/constant.BuildTime=%RELEASE%' -w -s" .. \ No newline at end of file diff --git a/compile/build_linux_arm64.bat b/compile/build_linux_arm64.bat new file mode 100644 index 00000000..59ab1d6b --- /dev/null +++ b/compile/build_linux_arm64.bat @@ -0,0 +1,8 @@ +ECHO %date:~0,4%-%date:~5,2%-%date:~8,2% %time:~0,2%:%time:~3,2%:%time:~6,2% +set RELEASE=%date:~0,4%-%date:~5,2%-%date:~8,2% %time:~0,2%:%time:~3,2%:%time:~6,2% +ECHO %RELEASE% +set GOOS=linux +set CGO_ENABLED=0 +set GOARCH=arm64 + +go build -o clash -trimpath -ldflags="-X 'github.com/metacubex/mihomo/constant.Version=%RELEASE%' -X 'github.com/metacubex/mihomo/constant.BuildTime=%RELEASE%' -w -s" .. \ No newline at end of file diff --git a/compile/build_windows.bat b/compile/build_windows.bat new file mode 100644 index 00000000..512a78c3 --- /dev/null +++ b/compile/build_windows.bat @@ -0,0 +1,9 @@ +ECHO %date:~0,4%-%date:~5,2%-%date:~8,2% %time:~0,2%:%time:~3,2%:%time:~6,2% +set RELEASE=%date:~0,4%-%date:~5,2%-%date:~8,2% %time:~0,2%:%time:~3,2%:%time:~6,2% +ECHO %RELEASE% + +set GOOS=windows +set CGO_ENABLED=0 +set GOARCH=amd64 +set GOAMD64=v3 +go build -o meta.exe -trimpath -ldflags="-X 'github.com/metacubex/mihomo/constant.Version=%RELEASE%' -X 'github.com/metacubex/mihomo/constant.BuildTime=%RELEASE%' -w -s" .. \ No newline at end of file diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index c21e638e..fa3f4928 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -63,10 +63,21 @@ func DialContext(ctx context.Context, network, address string, options ...Option network = fmt.Sprintf("%s%d", network, opt.network) } - ips, port, err := parseAddr(ctx, network, address, opt.resolver) + ips_, port, err := parseAddr(ctx, network, address, opt.resolver) if err != nil { return nil, err } + var ips []netip.Addr + for _, v := range ips_ { + if !v.IsUnspecified() { + ips = append(ips, v) + } + } + + if len(ips) == 0 { + log.Debugln("unresolved ip for %s, reject", address) + return NopConn{}, nil + } switch network { case "tcp4", "tcp6", "udp4", "udp6": diff --git a/component/dialer/reject_conn.go b/component/dialer/reject_conn.go new file mode 100644 index 00000000..5444426e --- /dev/null +++ b/component/dialer/reject_conn.go @@ -0,0 +1,23 @@ +package dialer + +import ( + "github.com/metacubex/mihomo/common/buf" + "io" + "net" + "time" +) + +type NopConn struct{} + +func (rw NopConn) Read(b []byte) (int, error) { return 0, io.EOF } + +func (rw NopConn) ReadBuffer(buffer *buf.Buffer) error { return io.EOF } + +func (rw NopConn) Write(b []byte) (int, error) { return 0, io.EOF } +func (rw NopConn) WriteBuffer(buffer *buf.Buffer) error { return io.EOF } +func (rw NopConn) Close() error { return nil } +func (rw NopConn) LocalAddr() net.Addr { return nil } +func (rw NopConn) RemoteAddr() net.Addr { return nil } +func (rw NopConn) SetDeadline(time.Time) error { return nil } +func (rw NopConn) SetReadDeadline(time.Time) error { return nil } +func (rw NopConn) SetWriteDeadline(time.Time) error { return nil } diff --git a/component/proxydialer/proxydialer.go b/component/proxydialer/proxydialer.go index 71a658b8..735beed6 100644 --- a/component/proxydialer/proxydialer.go +++ b/component/proxydialer/proxydialer.go @@ -64,7 +64,17 @@ func (p proxyDialer) DialContext(ctx context.Context, network, address string) ( return nil, err } if p.statistic { - conn = statistic.NewTCPTracker(conn, statistic.DefaultManager, currentMeta, nil, 0, 0, false) + channel := p.proxy.Name() + manager, ok := statistic.ChannelManager[channel] + if !ok { + statistic.ChannelMutex.Lock() + manager, ok = statistic.ChannelManager[channel] + if !ok { + manager = statistic.NewManager(channel) + } + statistic.ChannelMutex.Unlock() + } + conn = statistic.NewTCPTracker(conn, manager, currentMeta, nil, 0, 0, false, "") } return conn, err } @@ -87,7 +97,17 @@ func (p proxyDialer) listenPacket(ctx context.Context, currentMeta *C.Metadata) return nil, err } if p.statistic { - pc = statistic.NewUDPTracker(pc, statistic.DefaultManager, currentMeta, nil, 0, 0, false) + channel := p.proxy.Name() + manager, ok := statistic.ChannelManager[channel] + if !ok { + statistic.ChannelMutex.Lock() + manager, ok = statistic.ChannelManager[channel] + if !ok { + manager = statistic.NewManager(channel) + } + statistic.ChannelMutex.Unlock() + } + pc = statistic.NewUDPTracker(pc, manager, currentMeta, nil, 0, 0, false) } return pc, nil } diff --git a/config/config.go b/config/config.go index 9bc0afc8..d9261394 100644 --- a/config/config.go +++ b/config/config.go @@ -4,6 +4,7 @@ import ( "container/list" "errors" "fmt" + "github.com/metacubex/mihomo/adapter/inbound" "net" "net/netip" "net/url" @@ -28,7 +29,6 @@ import ( SNIFF "github.com/metacubex/mihomo/component/sniffer" tlsC "github.com/metacubex/mihomo/component/tls" "github.com/metacubex/mihomo/component/trie" - "github.com/metacubex/mihomo/component/updater" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/constant/features" providerTypes "github.com/metacubex/mihomo/constant/provider" @@ -51,6 +51,7 @@ type General struct { Controller Mode T.TunnelMode `json:"mode"` UnifiedDelay bool + Log *Log `json:"log"` LogLevel log.LogLevel `json:"log-level"` IPv6 bool `json:"ipv6"` Interface string `json:"interface-name"` @@ -194,6 +195,9 @@ type Config struct { Tunnels []LC.Tunnel Sniffer *Sniffer TLS *TLS + + WanInput *inbound.WanInput + TlsUser []auth.AuthUser } type RawNTP struct { @@ -286,7 +290,14 @@ type RawTuicServer struct { MaxUdpRelayPacketSize int `yaml:"max-udp-relay-packet-size" json:"max-udp-relay-packet-size,omitempty"` CWND int `yaml:"cwnd" json:"cwnd,omitempty"` } - +type Log struct { + File string `yaml:"file"` + Level log.LogLevel `yaml:"level"` + MaxSize int `json:"maxsize" yaml:"maxsize"` + MaxBackups int `json:"maxbackups" yaml:"maxbackups"` + MaxAge int `json:"maxage" yaml:"maxage"` + Compress bool `json:"compress" yaml:"compress"` +} type RawConfig struct { Port int `yaml:"port" json:"port"` SocksPort int `yaml:"socks-port" json:"socks-port"` @@ -305,7 +316,7 @@ type RawConfig struct { 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"` + Log Log `yaml:"log"` IPv6 bool `yaml:"ipv6" json:"ipv6"` ExternalController string `yaml:"external-controller"` ExternalControllerUnix string `yaml:"external-controller-unix"` @@ -349,6 +360,8 @@ type RawConfig struct { Listeners []map[string]any `yaml:"listeners"` ClashForAndroid RawClashForAndroid `yaml:"clash-for-android" json:"clash-for-android"` + + WanInput inbound.WanInput `yaml:"wan-input"` } type GeoXUrl struct { @@ -394,6 +407,10 @@ func Parse(buf []byte) (*Config, error) { return nil, err } + log.SetLevel(rawCfg.Log.Level) + log.SetOutput(rawCfg.Log.File, rawCfg.Log.MaxSize, + rawCfg.Log.MaxBackups, rawCfg.Log.MaxAge, rawCfg.Log.Compress) + return ParseRawConfig(rawCfg) } @@ -411,7 +428,7 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) { GeodataLoader: "memconservative", UnifiedDelay: false, Authentication: []string{}, - LogLevel: log.INFO, + Log: Log{}, Hosts: map[string]any{}, Rule: []string{}, Proxy: []map[string]any{}, @@ -513,6 +530,10 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) { GeoSite: "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geosite.dat", }, ExternalUIURL: "https://github.com/MetaCubeX/metacubexd/archive/refs/heads/gh-pages.zip", + + WanInput: inbound.WanInput{ + Port: 0, + }, } if err := yaml.Unmarshal(buf, rawCfg); err != nil { @@ -557,7 +578,7 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { log.Infoln("Geodata Loader mode: %s", geodata.LoaderName()) log.Infoln("Geosite Matcher implementation: %s", geodata.SiteMatcherName()) - ruleProviders, err := parseRuleProviders(rawCfg) + ruleProviders, err := parseRuleProviders(rawCfg, proxies) if err != nil { return nil, err } @@ -602,6 +623,11 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { config.Users = parseAuthentication(rawCfg.Authentication) + if rawCfg.WanInput.Port != 0 { + config.TlsUser = parseAuthentication(rawCfg.WanInput.Authentication) + } + config.WanInput = &rawCfg.WanInput + config.Tunnels = rawCfg.Tunnels // verify tunnels for _, t := range config.Tunnels { @@ -641,28 +667,28 @@ func parseGeneral(cfg *RawConfig) (*General, error) { N.KeepAliveInterval = time.Duration(cfg.KeepAliveInterval) * time.Second } - updater.ExternalUIPath = cfg.ExternalUI + ExternalUIPath = cfg.ExternalUI // checkout externalUI exist - if updater.ExternalUIPath != "" { - updater.ExternalUIPath = C.Path.Resolve(updater.ExternalUIPath) - if _, err := os.Stat(updater.ExternalUIPath); os.IsNotExist(err) { + if ExternalUIPath != "" { + ExternalUIPath = C.Path.Resolve(ExternalUIPath) + if _, err := os.Stat(ExternalUIPath); os.IsNotExist(err) { defaultUIpath := path.Join(C.Path.HomeDir(), "ui") - log.Warnln("external-ui: %s does not exist, creating folder in %s", updater.ExternalUIPath, defaultUIpath) + log.Warnln("external-ui: %s does not exist, creating folder in %s", ExternalUIPath, defaultUIpath) if err := os.MkdirAll(defaultUIpath, os.ModePerm); err != nil { return nil, err } - updater.ExternalUIPath = defaultUIpath + ExternalUIPath = defaultUIpath cfg.ExternalUI = defaultUIpath } } // checkout UIpath/name exist if cfg.ExternalUIName != "" { - updater.ExternalUIName = cfg.ExternalUIName + ExternalUIName = cfg.ExternalUIName } else { - updater.ExternalUIFolder = updater.ExternalUIPath + ExternalUIFolder = ExternalUIPath } if cfg.ExternalUIURL != "" { - updater.ExternalUIURL = cfg.ExternalUIURL + ExternalUIURL = cfg.ExternalUIURL } cfg.Tun.RedirectToTun = cfg.EBpf.RedirectToTun @@ -692,7 +718,7 @@ func parseGeneral(cfg *RawConfig) (*General, error) { }, UnifiedDelay: cfg.UnifiedDelay, Mode: cfg.Mode, - LogLevel: cfg.LogLevel, + Log: &cfg.Log, IPv6: cfg.IPv6, Interface: cfg.Interface, RoutingMark: cfg.RoutingMark, @@ -835,7 +861,7 @@ func parseListeners(cfg *RawConfig) (listeners map[string]C.InboundListener, err return } -func parseRuleProviders(cfg *RawConfig) (ruleProviders map[string]providerTypes.RuleProvider, err error) { +func parseRuleProviders(cfg *RawConfig, proxies map[string]C.Proxy) (ruleProviders map[string]providerTypes.RuleProvider, err error) { ruleProviders = map[string]providerTypes.RuleProvider{} // parse rule provider for name, mapping := range cfg.RuleProvider { diff --git a/config/initial.go b/config/initial.go index 61d12895..6cf2ee34 100644 --- a/config/initial.go +++ b/config/initial.go @@ -2,10 +2,13 @@ package config import ( "fmt" - "os" - + "github.com/go-co-op/gocron" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" + "github.com/metacubex/mihomo/tunnel/statistic" + "os" + P "path" + "time" ) // Init prepare necessary files @@ -28,5 +31,28 @@ func Init(dir string) error { f.Close() } + if _, err := os.Stat(C.Path.StatisticPath()); os.IsNotExist(err) { + if err = os.Mkdir(C.Path.StatisticPath(), os.ModePerm); err != nil { + log.Errorln(err.Error()) + } + } + + statistic.RestoreChannelsData(CurrentMonthStatisticFileName()) + s := gocron.NewScheduler(time.Local) + s.Every(10).Minutes().Do(func() { + ts := time.Now().AddDate(0, 0, -1) + fileName := P.Join(C.Path.StatisticPath(), ts.Format("200601")+"-statistic.json") + statistic.SaveChannelsData(fileName) + if fileName != CurrentMonthStatisticFileName() { + for _, v := range statistic.ChannelManager { + v.ResetStatistic() + } + } + }) + s.StartAsync() + return nil } +func CurrentMonthStatisticFileName() string { + return P.Join(C.Path.StatisticPath(), time.Now().Format("200601")+"-statistic.json") +} diff --git a/config/update_geo.go b/config/update_geo.go new file mode 100644 index 00000000..43cac25c --- /dev/null +++ b/config/update_geo.go @@ -0,0 +1,90 @@ +package config + +import ( + "fmt" + "runtime" + + "github.com/metacubex/mihomo/component/geodata" + _ "github.com/metacubex/mihomo/component/geodata/standard" + "github.com/metacubex/mihomo/component/mmdb" + C "github.com/metacubex/mihomo/constant" + + "github.com/oschwald/maxminddb-golang" +) + +func UpdateGeoDatabases() error { + defer runtime.GC() + geoLoader, err := geodata.GetGeoDataLoader("standard") + if err != nil { + return err + } + + if C.GeodataMode { + data, err := downloadForBytes(C.GeoIpUrl) + if err != nil { + return fmt.Errorf("can't download GeoIP database file: %w", err) + } + + if _, err = geoLoader.LoadIPByBytes(data, "cn"); err != nil { + return fmt.Errorf("invalid GeoIP database file: %s", err) + } + + if err = saveFile(data, C.Path.GeoIP()); err != nil { + return fmt.Errorf("can't save GeoIP database file: %w", err) + } + + } else { + defer mmdb.ReloadIP() + data, err := downloadForBytes(C.MmdbUrl) + if err != nil { + return fmt.Errorf("can't download MMDB database file: %w", err) + } + + instance, err := maxminddb.FromBytes(data) + if err != nil { + return fmt.Errorf("invalid MMDB database file: %s", err) + } + _ = instance.Close() + + mmdb.IPInstance().Reader.Close() // mmdb is loaded with mmap, so it needs to be closed before overwriting the file + if err = saveFile(data, C.Path.MMDB()); err != nil { + return fmt.Errorf("can't save MMDB database file: %w", err) + } + } + + if C.ASNEnable { + defer mmdb.ReloadASN() + data, err := downloadForBytes(C.ASNUrl) + if err != nil { + return fmt.Errorf("can't download ASN database file: %w", err) + } + + instance, err := maxminddb.FromBytes(data) + if err != nil { + return fmt.Errorf("invalid ASN database file: %s", err) + } + _ = instance.Close() + + mmdb.ASNInstance().Reader.Close() + if err = saveFile(data, C.Path.ASN()); err != nil { + return fmt.Errorf("can't save ASN database file: %w", err) + } + } + + data, err := downloadForBytes(C.GeoSiteUrl) + if err != nil { + return fmt.Errorf("can't download GeoSite database file: %w", err) + } + + if _, err = geoLoader.LoadSiteByBytes(data, "cn"); err != nil { + return fmt.Errorf("invalid GeoSite database file: %s", err) + } + + if err = saveFile(data, C.Path.GeoSite()); err != nil { + return fmt.Errorf("can't save GeoSite database file: %w", err) + } + + geodata.ClearCache() + + return nil +} diff --git a/config/update_ui.go b/config/update_ui.go new file mode 100644 index 00000000..cff1d6d7 --- /dev/null +++ b/config/update_ui.go @@ -0,0 +1,145 @@ +package config + +import ( + "archive/zip" + "errors" + "fmt" + "io" + "os" + "path" + "path/filepath" + "strings" + "sync" + + C "github.com/metacubex/mihomo/constant" +) + +var ( + ExternalUIURL string + ExternalUIPath string + ExternalUIFolder string + ExternalUIName string +) +var ( + ErrIncompleteConf = errors.New("ExternalUI configure incomplete") +) +var xdMutex sync.Mutex + +func UpdateUI() error { + xdMutex.Lock() + defer xdMutex.Unlock() + + err := prepare() + if err != nil { + return err + } + + data, err := downloadForBytes(ExternalUIURL) + if err != nil { + return fmt.Errorf("can't download file: %w", err) + } + + saved := path.Join(C.Path.HomeDir(), "download.zip") + if err = saveFile(data, saved); err != nil { + return fmt.Errorf("can't save zip file: %w", err) + } + defer os.Remove(saved) + + err = cleanup(ExternalUIFolder) + if err != nil { + if !os.IsNotExist(err) { + return fmt.Errorf("cleanup exist file error: %w", err) + } + } + + unzipFolder, err := unzip(saved, C.Path.HomeDir()) + if err != nil { + return fmt.Errorf("can't extract zip file: %w", err) + } + + err = os.Rename(unzipFolder, ExternalUIFolder) + if err != nil { + return fmt.Errorf("can't rename folder: %w", err) + } + return nil +} + +func prepare() error { + if ExternalUIPath == "" || ExternalUIURL == "" { + return ErrIncompleteConf + } + + if ExternalUIName != "" { + ExternalUIFolder = filepath.Clean(path.Join(ExternalUIPath, ExternalUIName)) + if _, err := os.Stat(ExternalUIPath); os.IsNotExist(err) { + if err := os.MkdirAll(ExternalUIPath, os.ModePerm); err != nil { + return err + } + } + } else { + ExternalUIFolder = ExternalUIPath + } + + return nil +} + +func unzip(src, dest string) (string, error) { + r, err := zip.OpenReader(src) + if err != nil { + return "", err + } + defer r.Close() + var extractedFolder string + for _, f := range r.File { + fpath := filepath.Join(dest, f.Name) + if !strings.HasPrefix(fpath, filepath.Clean(dest)+string(os.PathSeparator)) { + return "", fmt.Errorf("invalid file path: %s", fpath) + } + if f.FileInfo().IsDir() { + os.MkdirAll(fpath, os.ModePerm) + continue + } + if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil { + return "", err + } + outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) + if err != nil { + return "", err + } + rc, err := f.Open() + if err != nil { + return "", err + } + _, err = io.Copy(outFile, rc) + outFile.Close() + rc.Close() + if err != nil { + return "", err + } + if extractedFolder == "" { + extractedFolder = filepath.Dir(fpath) + } + } + return extractedFolder, nil +} + +func cleanup(root string) error { + if _, err := os.Stat(root); os.IsNotExist(err) { + return nil + } + return filepath.Walk(root, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() { + if err := os.RemoveAll(path); err != nil { + return err + } + } else { + if err := os.Remove(path); err != nil { + return err + } + } + return nil + }) +} diff --git a/config/utils.go b/config/utils.go index f87fb341..66bf3441 100644 --- a/config/utils.go +++ b/config/utils.go @@ -1,15 +1,38 @@ package config import ( + "context" "fmt" + "io" "net" + "net/http" "net/netip" + "os" "strings" + "time" "github.com/metacubex/mihomo/adapter/outboundgroup" "github.com/metacubex/mihomo/common/structure" + mihomoHttp "github.com/metacubex/mihomo/component/http" + C "github.com/metacubex/mihomo/constant" ) +func downloadForBytes(url string) ([]byte, error) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) + defer cancel() + resp, err := mihomoHttp.HttpRequest(ctx, url, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + return io.ReadAll(resp.Body) +} + +func saveFile(bytes []byte, path string) error { + return os.WriteFile(path, bytes, 0o644) +} + func trimArr(arr []string) (r []string) { for _, e := range arr { r = append(r, strings.Trim(e, " ")) diff --git a/constant/metadata.go b/constant/metadata.go index 381e2dd4..f6cb269e 100644 --- a/constant/metadata.go +++ b/constant/metadata.go @@ -32,6 +32,7 @@ const ( TUIC HYSTERIA2 INNER + SSH ) type NetWork int @@ -83,6 +84,8 @@ func (t Type) String() string { return "Hysteria2" case INNER: return "Inner" + case SSH: + return "SSH" default: return "Unknown" } @@ -117,6 +120,7 @@ func ParseType(t string) (*Type, error) { res = HYSTERIA2 case "INNER": res = INNER + default: return nil, fmt.Errorf("unknown type: %s", t) } @@ -155,8 +159,13 @@ type Metadata struct { RawDstAddr net.Addr `json:"-"` // Only domain rule SniffHost string `json:"sniffHost"` + + HitRule string `-` } +func (m *Metadata) GeoedIp() bool { + return len(m.DstGeoIP) != 0 +} func (m *Metadata) RemoteAddress() string { return net.JoinHostPort(m.String(), strconv.FormatUint(uint64(m.DstPort), 10)) } diff --git a/constant/path.go b/constant/path.go index 77f7d0ef..8eaf0bb9 100644 --- a/constant/path.go +++ b/constant/path.go @@ -3,6 +3,7 @@ package constant import ( "crypto/md5" "encoding/hex" + "log" "os" P "path" "path/filepath" @@ -23,20 +24,16 @@ var ( // on Unix systems, `$HOME/.config/mihomo`. // on Windows, `%USERPROFILE%/.config/mihomo`. var Path = func() *path { - homeDir, err := os.UserHomeDir() - if err != nil { - homeDir, _ = os.Getwd() - } allowUnsafePath, _ := strconv.ParseBool(os.Getenv("SKIP_SAFE_PATH_CHECK")) - homeDir = P.Join(homeDir, ".config", Name) - - if _, err = os.Stat(homeDir); err != nil { - if configHome, ok := os.LookupEnv("XDG_CONFIG_HOME"); ok { - homeDir = P.Join(configHome, Name) - } + ex, err := os.Executable() + if err != nil { + log.Panic(err) } + homeDir := filepath.Dir(ex) - return &path{homeDir: homeDir, configFile: "config.yaml", allowUnsafePath: allowUnsafePath} + homeDir = P.Join(homeDir, "config") + + return &path{homeDir: homeDir, configFile: P.Join(homeDir, "config.yaml"), allowUnsafePath: allowUnsafePath} }() type path struct { @@ -112,7 +109,6 @@ func (p *path) MMDB() string { } return P.Join(p.homeDir, "geoip.metadb") } - func (p *path) ASN() string { files, err := os.ReadDir(p.homeDir) if err != nil { @@ -131,7 +127,6 @@ func (p *path) ASN() string { } return P.Join(p.homeDir, ASNName) } - func (p *path) OldCache() string { return P.Join(p.homeDir, ".cache") } @@ -190,3 +185,7 @@ func (p *path) GetExecutableFullPath() string { res, _ := filepath.EvalSymlinks(exePath) return res } + +func (p *path) StatisticPath() string { + return P.Join(p.homeDir, "statistic") +} diff --git a/constant/rule.go b/constant/rule.go index 161c200a..aa86fe00 100644 --- a/constant/rule.go +++ b/constant/rule.go @@ -34,6 +34,10 @@ const ( AND OR NOT + RECORD + FileDI + MustResolve + ISOEmpty ) type RuleType int @@ -104,6 +108,14 @@ func (rt RuleType) String() string { return "OR" case NOT: return "NOT" + case RECORD: + return "RECORD" + case FileDI: + return "FileDI" + case MustResolve: + return "MustResolve" + case ISOEmpty: + return "ISOEmpty" default: return "Unknown" } diff --git a/docs/config.yaml b/docs/config.yaml index fe850163..eee6122b 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -8,15 +8,15 @@ mixed-port: 10801 # HTTP(S) 和 SOCKS 代理混合端口 allow-lan: true # 允许局域网连接 bind-address: "*" # 绑定 IP 地址,仅作用于 allow-lan 为 true,'*'表示所有地址 -authentication: # http,socks 入口的验证用户名,密码 +authentication: # http,socks入口的验证用户名,密码 - "username:password" -skip-auth-prefixes: # 设置跳过验证的 IP 段 +skip-auth-prefixes: # 设置跳过验证的IP段 - 127.0.0.1/8 - ::1/128 -lan-allowed-ips: # 允许连接的 IP 地址段,仅作用于 allow-lan 为 true, 默认值为 0.0.0.0/0 和::/0 +lan-allowed-ips: # 允许连接的 IP 地址段,仅作用于 allow-lan 为 true, 默认值为0.0.0.0/0和::/0 - 0.0.0.0/0 - ::/0 -lan-disallowed-ips: # 禁止连接的 IP 地址段,黑名单优先级高于白名单,默认值为空 +lan-disallowed-ips: # 禁止连接的 IP 地址段, 黑名单优先级高于白名单, 默认值为空 - 192.168.0.3/32 # find-process-mode has 3 values:always, strict, off @@ -58,11 +58,6 @@ external-controller: 0.0.0.0:9093 # RESTful API 监听地址 external-controller-tls: 0.0.0.0:9443 # RESTful API HTTPS 监听地址,需要配置 tls 部分配置文件 # secret: "123456" # `Authorization:Bearer ${secret}` -# RESTful API Unix socket 监听地址( windows版本大于17063也可以使用,即大于等于1803/RS4版本即可使用 ) -# !!!注意: 从Unix socket访问api接口不会验证secret, 如果开启请自行保证安全问题 !!! -# 测试方法: curl -v --unix-socket "mihomo.sock" http://localhost/ -external-controller-unix: mihomo.sock - # tcp-concurrent: true # TCP 并发连接所有 IP, 将使用最快握手的 TCP # 配置 WEB UI 目录,使用 http://{{external-controller}}/ui 访问 @@ -114,9 +109,9 @@ tun: # auto-detect-interface: true # 自动识别出口网卡 # auto-route: true # 配置路由表 # mtu: 9000 # 最大传输单元 - # gso: false # 启用通用分段卸载,仅支持 Linux + # gso: false # 启用通用分段卸载, 仅支持 Linux # gso-max-size: 65536 # 通用分段卸载包的最大大小 - # strict-route: true # 将所有连接路由到 tun 来防止泄漏,但你的设备将无法其他设备被访问 + # strict-route: true # 将所有连接路由到tun来防止泄漏,但你的设备将无法其他设备被访问 inet4-route-address: # 启用 auto-route 时使用自定义路由而不是默认路由 - 0.0.0.0/1 - 128.0.0.0/1 @@ -124,9 +119,9 @@ tun: - "::/1" - "8000::/1" # endpoint-independent-nat: false # 启用独立于端点的 NAT - # include-interface: # 限制被路由的接口。默认不限制,与 `exclude-interface` 冲突 + # include-interface: # 限制被路由的接口。默认不限制, 与 `exclude-interface` 冲突 # - "lan0" - # exclude-interface: # 排除路由的接口,与 `include-interface` 冲突 + # exclude-interface: # 排除路由的接口, 与 `include-interface` 冲突 # - "lan1" # include-uid: # UID 规则仅在 Linux 下被支持,并且需要 auto-route # - 0 @@ -148,7 +143,7 @@ tun: # exclude-package: # 排除被路由的 Android 应用包名 # - com.android.captiveportallogin -#ebpf 配置 +#ebpf配置 ebpf: auto-redir: # redirect 模式,仅支持 TCP - eth0 @@ -205,7 +200,7 @@ tunnels: # one line config target: target.com proxy: proxy -# DNS 配置 +# DNS配置 dns: cache-algorithm: arc enable: false # 关闭将使用系统 DNS @@ -213,7 +208,7 @@ dns: listen: 0.0.0.0:53 # 开启 DNS 服务器监听 # ipv6: false # false 将返回 AAAA 的空结果 # ipv6-timeout: 300 # 单位:ms,内部双栈并发时,向上游查询 AAAA 时,等待 AAAA 的时间,默认 100ms - # 用于解析 nameserver,fallback 以及其他 DNS 服务器配置的,DNS 服务域名 + # 用于解析 nameserver,fallback 以及其他DNS服务器配置的,DNS 服务域名 # 只能使用纯 IP 地址,可使用加密 DNS default-nameserver: - 114.114.114.114 @@ -227,12 +222,12 @@ dns: # use-hosts: true # 查询 hosts - # 配置不使用 fake-ip 的域名 + # 配置不使用fake-ip的域名 # fake-ip-filter: # - '*.lan' # - localhost.ptlogin2.qq.com - # DNS 主要域名配置 + # DNS主要域名配置 # 支持 UDP,TCP,DoT,DoH,DoQ # 这部分为主要 DNS 配置,影响所有直连,确保使用对大陆解析精准的 DNS nameserver: @@ -244,7 +239,7 @@ dns: - https://mozilla.cloudflare-dns.com/dns-query#DNS&h3=true # 指定策略组和使用 HTTP/3 - dhcp://en0 # dns from dhcp - quic://dns.adguard.com:784 # DNS over QUIC - # - '8.8.8.8#en0' # 兼容指定 DNS 出口网卡 + # - '8.8.8.8#en0' # 兼容指定DNS出口网卡 # 当配置 fallback 时,会查询 nameserver 中返回的 IP 是否为 CN,非必要配置 # 当不是 CN,则使用 fallback 中的 DNS 查询结果 @@ -254,6 +249,7 @@ dns: # - 'tcp://1.1.1.1#ProxyGroupName' # 指定 DNS 过代理查询,ProxyGroupName 为策略组名或节点名,过代理配置优先于配置出口网卡,当找不到策略组或节点名则设置为出口网卡 # 专用于节点域名解析的 DNS 服务器,非必要配置项 + # 配置服务器若查询失败将使用 nameserver,非并发查询 # proxy-server-nameserver: # - https://dns.google/dns-query # - tls://one.one.one.one @@ -342,7 +338,7 @@ proxies: # socks5 # udp-over-tcp: false # ip-version: ipv4 # 设置节点使用 IP 版本,可选:dual,ipv4,ipv6,ipv4-prefer,ipv6-prefer。默认使用 dual # ipv4:仅使用 IPv4 ipv6:仅使用 IPv6 - # ipv4-prefer:优先使用 IPv4 对于 TCP 会进行双栈解析,并发链接但是优先使用 IPv4 链接, + # ipv4-prefer:优先使用 IPv4 对于 TCP 会进行双栈解析,并发链接但是优先使用 IPv4 链接, # UDP 则为双栈解析,获取结果中的第一个 IPv4 # ipv6-prefer 同 ipv4-prefer # 现有协议都支持此参数,TCP 效果仅在开启 tcp-concurrent 生效 @@ -354,7 +350,7 @@ proxies: # socks5 # max-streams: 0 # Maximum multiplexed streams in a connection before opening a new connection. Conflict with max-connections and min-streams. # padding: false # Enable padding. Requires sing-box server version 1.3-beta9 or later. # statistic: false # 控制是否将底层连接显示在面板中,方便打断底层连接 - # only-tcp: false # 如果设置为 true, smux 的设置将不会对 udp 生效,udp 连接会直接走底层协议 + # only-tcp: false # 如果设置为true, smux的设置将不会对udp生效,udp连接会直接走底层协议 - name: "ss2" type: ss @@ -387,7 +383,6 @@ proxies: # socks5 # headers: # custom: value # v2ray-http-upgrade: false - # v2ray-http-upgrade-fast-open: false - name: "ss4-shadow-tls" type: ss @@ -410,18 +405,18 @@ proxies: # socks5 password: [YOUR_SS_PASSWORD] client-fingerprint: chrome # One of: chrome, ios, firefox or safari - # 可以是 chrome, ios, firefox, safari 中的一个 + # 可以是chrome, ios, firefox, safari中的一个 plugin: restls plugin-opts: host: "www.microsoft.com" # Must be a TLS 1.3 server - # 应当是一个 TLS 1.3 服务器 + # 应当是一个TLS 1.3 服务器 password: [YOUR_RESTLS_PASSWORD] version-hint: "tls13" # Control your post-handshake traffic through restls-script # Hide proxy behaviors like "tls in tls". # see https://github.com/3andne/restls/blob/main/Restls-Script:%20Hide%20Your%20Proxy%20Traffic%20Behavior.md - # 用 restls 剧本来控制握手后的行为,隐藏"tls in tls"等特征 + # 用restls剧本来控制握手后的行为,隐藏"tls in tls"等特征 # 详情:https://github.com/3andne/restls/blob/main/Restls-Script:%20%E9%9A%90%E8%97%8F%E4%BD%A0%E7%9A%84%E4%BB%A3%E7%90%86%E8%A1%8C%E4%B8%BA.md restls-script: "300?100<1,400~100,350~100,600~100,300~200,300~100" @@ -433,18 +428,18 @@ proxies: # socks5 password: [YOUR_SS_PASSWORD] client-fingerprint: chrome # One of: chrome, ios, firefox or safari - # 可以是 chrome, ios, firefox, safari 中的一个 + # 可以是chrome, ios, firefox, safari中的一个 plugin: restls plugin-opts: host: "vscode.dev" # Must be a TLS 1.2 server - # 应当是一个 TLS 1.2 服务器 + # 应当是一个TLS 1.2 服务器 password: [YOUR_RESTLS_PASSWORD] version-hint: "tls12" restls-script: "1000?100<1,500~100,350~100,600~100,400~200" # vmess - # cipher 支持 auto/aes-128-gcm/chacha20-poly1305/none + # cipher支持 auto/aes-128-gcm/chacha20-poly1305/none - name: "vmess" type: vmess server: server @@ -466,7 +461,6 @@ proxies: # socks5 # max-early-data: 2048 # early-data-header-name: Sec-WebSocket-Protocol # v2ray-http-upgrade: false - # v2ray-http-upgrade-fast-open: false - name: "vmess-h2" type: vmess @@ -595,7 +589,6 @@ proxies: # socks5 headers: Host: example.com # v2ray-http-upgrade: false - # v2ray-http-upgrade-fast-open: false # Trojan - name: "trojan" @@ -611,10 +604,6 @@ proxies: # socks5 # - h2 # - http/1.1 # skip-cert-verify: true - # ss-opts: # like trojan-go's `shadowsocks` config - # enabled: false - # method: aes-128-gcm # aes-128-gcm/aes-256-gcm/chacha20-ietf-poly1305 - # password: "example" - name: trojan-grpc server: server @@ -644,7 +633,6 @@ proxies: # socks5 # headers: # Host: example.com # v2ray-http-upgrade: false - # v2ray-http-upgrade-fast-open: false - name: "trojan-xtls" type: trojan @@ -686,13 +674,11 @@ proxies: # socks5 type: hysteria2 server: server.com port: 443 - # ports: 1000,2000-3000,5000 # port 不可省略 - # hop-interval: 15 - # up 和 down 均不写或为 0 则使用 BBR 流控 + # up和down均不写或为0则使用BBR流控 # up: "30 Mbps" # 若不写单位,默认为 Mbps # down: "200 Mbps" # 若不写单位,默认为 Mbps password: yourpassword - # obfs: salamander # 默认为空,如果填写则开启 obfs,目前仅支持 salamander + # obfs: salamander # 默认为空,如果填写则开启obfs,目前仅支持salamander # obfs-password: yourpassword # sni: server.com # skip-cert-verify: false @@ -718,12 +704,14 @@ proxies: # socks5 # reserved: [209,98,59] # 一个出站代理的标识。当值不为空时,将使用指定的 proxy 发出连接 # dialer-proxy: "ss1" - # remote-dns-resolve: true # 强制 dns 远程解析,默认值为 false - # dns: [ 1.1.1.1, 8.8.8.8 ] # 仅在 remote-dns-resolve 为 true 时生效 - # 如果 peers 不为空,该段落中的 allowed-ips 不可为空;前面段落的 server,port,public-key,pre-shared-key 均会被忽略,但 private-key 会被保留且只能在顶层指定 + # remote-dns-resolve: true # 强制dns远程解析,默认值为false + # dns: [ 1.1.1.1, 8.8.8.8 ] # 仅在remote-dns-resolve为true时生效 + # 如果peers不为空,该段落中的allowed-ips不可为空;前面段落的server,port,ip,ipv6,public-key,pre-shared-key均会被忽略,但private-key会被保留且只能在顶层指定 # peers: # - server: 162.159.192.1 # port: 2480 + # ip: 172.16.0.2 + # ipv6: fd01:5ca1:ab1e:80fa:ab85:6eea:213f:f4a5 # public-key: Cr8hWlKvtDt7nrvf+f0brNQQzabAqrjfBvas9pmowjo= # # pre-shared-key: 31aIhAPwktDGpH4JDhA8GNvjFXEf/a6+UaQRyOAiyfM= # allowed-ips: ['0.0.0.0/0'] @@ -734,9 +722,9 @@ proxies: # socks5 server: www.example.com port: 10443 type: tuic - # tuicV4 必须填写 token(不可同时填写 uuid 和 password) + # tuicV4必须填写token (不可同时填写uuid和password) token: TOKEN - # tuicV5 必须填写 uuid 和 password(不可同时填写 token) + # tuicV5必须填写uuid和password(不可同时填写token) uuid: 00000000-0000-0000-0000-000000000001 password: PASSWORD_1 # ip: 127.0.0.1 # for overwriting the DNS lookup result of the server address set in option 'server' @@ -754,8 +742,8 @@ proxies: # socks5 # max-open-streams: 20 # default 100, too many open streams may hurt performance # sni: example.com # - # meta 和 sing-box 私有扩展,将 ss-uot 用于 udp 中继,开启此选项后 udp-relay-mode 将失效 - # 警告,与原版 tuic 不兼容!!! + # meta和sing-box私有扩展,将ss-uot用于udp中继,开启此选项后udp-relay-mode将失效 + # 警告,与原版tuic不兼容!!! # udp-over-stream: false # udp-over-stream-version: 1 @@ -779,21 +767,9 @@ proxies: # socks5 # protocol-param: "#" # udp: true - - name: "ssh-out" - type: ssh - - server: 127.0.0.1 - port: 22 - username: root - password: password - privateKey: path - -# dns 出站会将请求劫持到内部 dns 模块,所有请求均在内部处理 - - name: "dns-out" - type: dns proxy-groups: - # 代理链,目前 relay 可以支持 udp 的只有 vmess/vless/trojan/ss/ssr/tuic - # wireguard 目前不支持在 relay 中使用,请使用 proxy 中的 dialer-proxy 配置项 + # 代理链,目前relay可以支持udp的只有vmess/vless/trojan/ss/ssr/tuic + # wireguard目前不支持在relay中使用,请使用proxy中的dialer-proxy配置项 # Traffic: mihomo <-> http <-> vmess <-> ss1 <-> ss2 <-> Internet - name: "relay" type: relay @@ -867,19 +843,10 @@ proxy-groups: # Mihomo 格式的节点或支持 *ray 的分享格式 proxy-providers: provider1: - type: http # http 的 path 可空置,默认储存路径为 homedir 的 proxies 文件夹,文件名为 url 的 md5 + type: http # http 的 path 可空置,默认储存路径为 homedir的proxies文件夹,文件名为url的md5 url: "url" interval: 3600 path: ./provider1.yaml # 默认只允许存储在 mihomo 的 Home Dir,如果想存储到任意位置,添加环境变量 SKIP_SAFE_PATH_CHECK=1 - proxy: DIRECT - header: - User-Agent: - - "Clash/v1.18.0" - - "mihomo/1.18.3" - # Accept: - # - 'application/vnd.github.v3.raw' - # Authorization: - # - 'token 1231231' health-check: enable: true interval: 600 @@ -895,8 +862,6 @@ proxy-providers: # interface-name: tailscale0 # routing-mark: 233 # ip-version: ipv4-prefer - # additional-prefix: "[provider1]" - # additional-suffix: "test" test: type: file path: /test.yaml @@ -909,9 +874,8 @@ rule-providers: behavior: classical # domain ipcidr interval: 259200 path: /path/to/save/file.yaml # 默认只允许存储在 mihomo 的 Home Dir,如果想存储到任意位置,添加环境变量 SKIP_SAFE_PATH_CHECK=1 - type: http # http 的 path 可空置,默认储存路径为 homedir 的 rules 文件夹,文件名为 url 的 md5 + type: http # http 的 path 可空置,默认储存路径为 homedir的rules文件夹,文件名为url的md5 url: "url" - proxy: DIRECT rule2: behavior: classical interval: 259200 @@ -919,8 +883,6 @@ rule-providers: type: file rules: - RULE-SET,rule1,REJECT - - IP-ASN,1,PROXY - - DOMAIN-REGEX,^abc,DIRECT - DOMAIN-SUFFIX,baidu.com,DIRECT - DOMAIN-KEYWORD,google,ss1 - IP-CIDR,1.1.1.1/32,ss1 @@ -960,7 +922,7 @@ listeners: port: 10808 #listen: 0.0.0.0 # 默认监听 0.0.0.0 # rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules - # proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 + # proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理 # udp: false # 默认 true - name: http-in-1 @@ -968,14 +930,14 @@ listeners: port: 10809 listen: 0.0.0.0 # rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules - # proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时,这里的 proxy 名称必须合法,否则会出错) + # proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时,这里的proxy名称必须合法,否则会出错) - name: mixed-in-1 type: mixed # HTTP(S) 和 SOCKS 代理混合 port: 10810 listen: 0.0.0.0 # rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules - # proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时,这里的 proxy 名称必须合法,否则会出错) + # proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时,这里的proxy名称必须合法,否则会出错) # udp: false # 默认 true - name: reidr-in-1 @@ -983,14 +945,14 @@ listeners: port: 10811 listen: 0.0.0.0 # rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules - # proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时,这里的 proxy 名称必须合法,否则会出错) + # proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时,这里的proxy名称必须合法,否则会出错) - name: tproxy-in-1 type: tproxy port: 10812 listen: 0.0.0.0 # rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules - # proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时,这里的 proxy 名称必须合法,否则会出错) + # proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时,这里的proxy名称必须合法,否则会出错) # udp: false # 默认 true - name: shadowsocks-in-1 @@ -998,7 +960,7 @@ listeners: port: 10813 listen: 0.0.0.0 # rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules - # proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时,这里的 proxy 名称必须合法,否则会出错) + # proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时,这里的proxy名称必须合法,否则会出错) password: vlmpIPSyHH6f4S8WVPdRIHIlzmB+GIRfoH3aNJ/t9Gg= cipher: 2022-blake3-aes-256-gcm @@ -1007,13 +969,13 @@ listeners: port: 10814 listen: 0.0.0.0 # rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules - # proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时,这里的 proxy 名称必须合法,否则会出错) + # proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时,这里的proxy名称必须合法,否则会出错) users: - username: 1 uuid: 9d0cb9d0-964f-4ef6-897d-6c6b3ccf9e68 alterId: 1 - # ws-path: "/" # 如果不为空则开启 websocket 传输层 - # 下面两项如果填写则开启 tls(需要同时填写) + # ws-path: "/" # 如果不为空则开启websocket传输层 + # 下面两项如果填写则开启tls(需要同时填写) # certificate: ./server.crt # private-key: ./server.key @@ -1022,10 +984,10 @@ listeners: port: 10815 listen: 0.0.0.0 # rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules - # proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时,这里的 proxy 名称必须合法,否则会出错) - # token: # tuicV4 填写(可以同时填写 users) + # proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时,这里的proxy名称必须合法,否则会出错) + # token: # tuicV4填写(可以同时填写users) # - TOKEN - # users: # tuicV5 填写(可以同时填写 token) + # users: # tuicV5填写(可以同时填写token) # 00000000-0000-0000-0000-000000000000: PASSWORD_0 # 00000000-0000-0000-0000-000000000001: PASSWORD_1 # certificate: ./server.crt @@ -1042,25 +1004,25 @@ listeners: port: 10816 listen: 0.0.0.0 # rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules - # proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时,这里的 proxy 名称必须合法,否则会出错) + # proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时,这里的proxy名称必须合法,否则会出错) network: [tcp, udp] target: target.com - name: tun-in-1 type: tun # rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules - # proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时,这里的 proxy 名称必须合法,否则会出错) + # proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时,这里的proxy名称必须合法,否则会出错) stack: system # gvisor / mixed dns-hijack: - 0.0.0.0:53 # 需要劫持的 DNS # auto-detect-interface: false # 自动识别出口网卡 # auto-route: false # 配置路由表 # mtu: 9000 # 最大传输单元 - inet4-address: # 必须手动设置 ipv4 地址段 + inet4-address: # 必须手动设置ipv4地址段 - 198.19.0.1/30 - inet6-address: # 必须手动设置 ipv6 地址段 + inet6-address: # 必须手动设置ipv6地址段 - "fdfe:dcba:9877::1/126" - # strict-route: true # 将所有连接路由到 tun 来防止泄漏,但你的设备将无法其他设备被访问 + # strict-route: true # 将所有连接路由到tun来防止泄漏,但你的设备将无法其他设备被访问 # inet4-route-address: # 启用 auto-route 时使用自定义路由而不是默认路由 # - 0.0.0.0/1 # - 128.0.0.0/1 @@ -1088,17 +1050,17 @@ listeners: # exclude-package: # 排除被路由的 Android 应用包名 # - com.android.captiveportallogin # 入口配置与 Listener 等价,传入流量将和 socks,mixed 等入口一样按照 mode 所指定的方式进行匹配处理 -# shadowsocks,vmess 入口配置(传入流量将和 socks,mixed 等入口一样按照 mode 所指定的方式进行匹配处理) +# shadowsocks,vmess 入口配置(传入流量将和socks,mixed等入口一样按照mode所指定的方式进行匹配处理) # ss-config: ss://2022-blake3-aes-256-gcm:vlmpIPSyHH6f4S8WVPdRIHIlzmB+GIRfoH3aNJ/t9Gg=@:23456 # vmess-config: vmess://1:9d0cb9d0-964f-4ef6-897d-6c6b3ccf9e68@:12345 -# tuic 服务器入口(传入流量将和 socks,mixed 等入口一样按照 mode 所指定的方式进行匹配处理) +# tuic服务器入口(传入流量将和socks,mixed等入口一样按照mode所指定的方式进行匹配处理) # tuic-server: # enable: true # listen: 127.0.0.1:10443 -# token: # tuicV4 填写(可以同时填写 users) +# token: # tuicV4填写(可以同时填写users) # - TOKEN -# users: # tuicV5 填写(可以同时填写 token) +# users: # tuicV5填写(可以同时填写token) # 00000000-0000-0000-0000-000000000000: PASSWORD_0 # 00000000-0000-0000-0000-000000000001: PASSWORD_1 # certificate: ./server.crt diff --git a/go.mod b/go.mod index 08e5b5aa..e71979f6 100644 --- a/go.mod +++ b/go.mod @@ -9,12 +9,14 @@ require ( github.com/cilium/ebpf v0.12.3 github.com/coreos/go-iptables v0.7.0 github.com/dlclark/regexp2 v1.11.0 + github.com/gliderlabs/ssh v0.3.7 github.com/go-chi/chi/v5 v5.0.12 github.com/go-chi/cors v1.2.1 github.com/go-chi/render v1.0.3 github.com/gobwas/ws v1.4.0 github.com/gofrs/uuid/v5 v5.2.0 github.com/insomniacslk/dhcp v0.0.0-20240419123447-f1cffa2c0c49 + github.com/kardianos/service v1.2.2 github.com/klauspost/cpuid/v2 v2.2.7 github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 github.com/mdlayher/netlink v1.7.2 @@ -32,6 +34,7 @@ require ( github.com/mroth/weightedrand/v2 v2.1.0 github.com/openacid/low v0.1.21 github.com/oschwald/maxminddb-golang v1.12.0 + github.com/pires/go-proxyproto v0.7.0 github.com/puzpuzpuz/xsync/v3 v3.1.0 github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 @@ -53,6 +56,7 @@ require ( golang.org/x/sync v0.7.0 golang.org/x/sys v0.20.0 google.golang.org/protobuf v1.34.1 + gopkg.in/natefinch/lumberjack.v2 v2.2.1 gopkg.in/yaml.v3 v3.0.1 lukechampine.com/blake3 v1.3.0 ) @@ -62,6 +66,7 @@ require ( github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 // indirect github.com/ajg/form v1.5.1 // indirect github.com/andybalholm/brotli v1.0.6 // indirect + github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect github.com/buger/jsonparser v1.1.1 // indirect github.com/cloudflare/circl v1.3.7 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -71,6 +76,7 @@ require ( github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/gaukas/godicttls v0.0.4 // indirect + github.com/go-co-op/gocron v1.37.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/gobwas/httphead v0.1.0 // indirect @@ -78,6 +84,7 @@ require ( github.com/google/btree v1.1.2 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect + github.com/google/uuid v1.4.0 // indirect github.com/hashicorp/yamux v0.1.1 // indirect github.com/josharian/native v1.1.0 // indirect github.com/klauspost/compress v1.17.4 // indirect @@ -92,7 +99,9 @@ require ( github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/quic-go/qpack v0.4.0 // indirect github.com/quic-go/qtls-go1-20 v0.4.1 // indirect + github.com/robfig/cron/v3 v3.0.1 // indirect github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 // indirect + github.com/sagernet/utls v1.5.4 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b // indirect github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c // indirect @@ -103,6 +112,7 @@ require ( github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect + go.uber.org/atomic v1.9.0 // indirect go.uber.org/mock v0.4.0 // indirect golang.org/x/mod v0.17.0 // indirect golang.org/x/text v0.15.0 // indirect diff --git a/go.sum b/go.sum index b9f1af14..3198b364 100644 --- a/go.sum +++ b/go.sum @@ -11,6 +11,8 @@ github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI= github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= @@ -25,6 +27,7 @@ github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vc github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= github.com/coreos/go-iptables v0.7.0 h1:XWM3V+MPRr5/q51NuWSgU0fqMad64Zyxs8ZUoMsamr8= github.com/coreos/go-iptables v0.7.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -44,12 +47,16 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/gaukas/godicttls v0.0.4 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXbk= github.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI= +github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE= +github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8= github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s= github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4= github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0= +github.com/go-co-op/gocron v1.37.0 h1:ZYDJGtQ4OMhTLKOKMIch+/CY70Brbb1dGdooLEhh7b0= +github.com/go-co-op/gocron v1.37.0/go.mod h1:3L/n6BkO7ABj+TrfSVXLRzsP26zmikL4ISkLQ0O8iNY= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= @@ -75,6 +82,8 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/tink/go v1.6.1 h1:t7JHqO8Ath2w2ig5vjwQYJzhGEZymedQc90lQXUBa4I= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -84,12 +93,20 @@ github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFF github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= +github.com/kardianos/service v1.2.2 h1:ZvePhAHfvo0A7Mftk/tEzqEZ7Q4lgnR8sGz4xu1YX60= +github.com/kardianos/service v1.2.2/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM= github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc= @@ -142,6 +159,9 @@ github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq5 github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY= github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE= github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs= +github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -154,6 +174,10 @@ github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs= github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= +github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= +github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a h1:+NkI2670SQpQWvkkD2QgdTuzQG263YZ+2emfpeyGqW0= github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a/go.mod h1:63s7jpZqcDAIpj8oI/1v4Izok+npJOHACFCU6+huCkM= @@ -165,6 +189,8 @@ github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnV github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4= github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ= github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo= +github.com/sagernet/utls v1.5.4 h1:KmsEGbB2dKUtCNC+44NwAdNAqnqQ6GA4pTO0Yik56co= +github.com/sagernet/utls v1.5.4/go.mod h1:CTGxPWExIloRipK3XFpYv0OVyhO8kk3XCGW/ieyTh1s= github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e h1:iGH0RMv2FzELOFNFQtvsxH7NPmlo7X5JizEK51UCojo= github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e/go.mod h1:YbL4TKHRR6APYQv3U2RGfwLDpPYSyWz6oUlpISBEzBE= github.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA= @@ -192,6 +218,7 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= @@ -214,6 +241,8 @@ github.com/zhangyunhao116/fastrand v0.4.0 h1:86QB6Y+GGgLZRFRDCjMmAS28QULwspK9sgL github.com/zhangyunhao116/fastrand v0.4.0/go.mod h1:vIyo6EyBhjGKpZv6qVlkPl4JVAklpMM4DSKzbAkMguA= gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec h1:FpfFs4EhNehiVfzQttTuxanPIT43FtkkCFypIod8LHo= gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec/go.mod h1:BZ1RAoRPbCxum9Grlv5aeksu2H8BiKehBYooU2LFiOQ= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= @@ -244,6 +273,7 @@ golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -271,6 +301,11 @@ google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFW google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 56e71632..fc5ecd97 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -91,6 +91,7 @@ func ApplyConfig(cfg *config.Config, force bool) { } updateUsers(cfg.Users) + updateUsersTls(cfg.TlsUser) updateProxies(cfg.Proxies, cfg.Providers) updateRules(cfg.Rules, cfg.SubRules, cfg.RuleProviders) updateSniffer(cfg.Sniffer) @@ -98,7 +99,7 @@ func ApplyConfig(cfg *config.Config, force bool) { updateGeneral(cfg.General) updateNTP(cfg.NTP) updateDNS(cfg.DNS, cfg.RuleProviders, cfg.General.IPv6) - updateListeners(cfg.General, cfg.Listeners, force) + updateListeners(cfg.General, cfg.Listeners, force, cfg.WanInput) updateIPTables(cfg) updateTun(cfg.General) updateExperimental(cfg) @@ -114,7 +115,7 @@ func ApplyConfig(cfg *config.Config, force bool) { tunnel.OnRunning() hcCompatibleProvider(cfg.Providers) - log.SetLevel(cfg.General.LogLevel) + log.SetLevel(cfg.General.Log.Level) } func initInnerTcp() { @@ -163,7 +164,8 @@ func GetGeneral() *config.General { return general } -func updateListeners(general *config.General, listeners map[string]C.InboundListener, force bool) { +func updateListeners(general *config.General, listeners map[string]C.InboundListener, force bool, + wanInput *inbound.WanInput) { listener.PatchInboundListeners(listeners, tunnel.Tunnel, true) if !force { return @@ -188,6 +190,10 @@ func updateListeners(general *config.General, listeners map[string]C.InboundList listener.ReCreateShadowSocks(general.ShadowSocksConfig, tunnel.Tunnel) listener.ReCreateVmess(general.VmessConfig, tunnel.Tunnel) listener.ReCreateTuic(general.TuicServer, tunnel.Tunnel) + + if wanInput.Port != 0 { + listener.ReCreateMixedTls(wanInput, tunnel.Tunnel) + } } func updateExperimental(c *config.Config) { @@ -419,6 +425,14 @@ func updateUsers(users []auth.AuthUser) { } } +func updateUsersTls(users []auth.AuthUser) { + authenticator := auth.NewAuthenticator(users) + authStore.SetAuthenticatorTls(authenticator) + if authenticator != nil { + log.Infoln("Authentication tls of local server updated") + } +} + func updateProfile(cfg *config.Config) { profileCfg := cfg.Profile diff --git a/hub/hub.go b/hub/hub.go index 38779e13..1bfd018f 100644 --- a/hub/hub.go +++ b/hub/hub.go @@ -50,7 +50,7 @@ func Parse(options ...Option) error { if cfg.General.ExternalController != "" { go route.Start(cfg.General.ExternalController, cfg.General.ExternalControllerTLS, - cfg.General.Secret, cfg.TLS.Certificate, cfg.TLS.PrivateKey, cfg.General.LogLevel == log.DEBUG) + cfg.General.Secret, cfg.TLS.Certificate, cfg.TLS.PrivateKey, true) } if cfg.General.ExternalControllerUnix != "" { diff --git a/hub/route/configs.go b/hub/route/configs.go index f2cf298a..653e4351 100644 --- a/hub/route/configs.go +++ b/hub/route/configs.go @@ -4,11 +4,11 @@ import ( "net/http" "net/netip" "path/filepath" + "sync" "github.com/metacubex/mihomo/adapter/inbound" "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/resolver" - "github.com/metacubex/mihomo/component/updater" "github.com/metacubex/mihomo/config" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/hub/executor" @@ -21,6 +21,11 @@ import ( "github.com/go-chi/render" ) +var ( + updateGeoMux sync.Mutex + updatingGeo = false +) + func configRouter() http.Handler { r := chi.NewRouter() r.Get("/", getConfigs) @@ -364,25 +369,40 @@ func updateConfigs(w http.ResponseWriter, r *http.Request) { } func updateGeoDatabases(w http.ResponseWriter, r *http.Request) { - err := updater.UpdateGeoDatabases() - if err != nil { - log.Errorln("[REST-API] update GEO databases failed: %v", err) - render.Status(r, http.StatusInternalServerError) - render.JSON(w, r, newError(err.Error())) + updateGeoMux.Lock() + + if updatingGeo { + updateGeoMux.Unlock() + render.Status(r, http.StatusBadRequest) + render.JSON(w, r, newError("updating...")) return } - cfg, err := executor.ParseWithPath(C.Path.Config()) - if err != nil { - log.Errorln("[REST-API] update GEO databases failed: %v", err) - render.Status(r, http.StatusInternalServerError) - render.JSON(w, r, newError("Error parsing configuration")) - return - } + updatingGeo = true + updateGeoMux.Unlock() - log.Warnln("[GEO] update GEO databases success, applying config") + go func() { + defer func() { + updatingGeo = false + }() - executor.ApplyConfig(cfg, false) + log.Warnln("[REST-API] updating GEO databases...") + + if err := config.UpdateGeoDatabases(); err != nil { + log.Errorln("[REST-API] update GEO databases failed: %v", err) + return + } + + cfg, err := executor.ParseWithPath(C.Path.Config()) + if err != nil { + log.Errorln("[REST-API] update GEO databases failed: %v", err) + return + } + + log.Warnln("[REST-API] update GEO databases successful, apply config...") + + executor.ApplyConfig(cfg, false) + }() render.NoContent(w, r) } diff --git a/hub/route/connections.go b/hub/route/connections.go index e0ff2426..c55d98e4 100644 --- a/hub/route/connections.go +++ b/hub/route/connections.go @@ -25,8 +25,8 @@ func connectionRouter() http.Handler { func getConnections(w http.ResponseWriter, r *http.Request) { if !(r.Header.Get("Upgrade") == "websocket") { - snapshot := statistic.DefaultManager.Snapshot() - render.JSON(w, r, snapshot) + snapshots := statistic.Snapshots() + render.JSON(w, r, snapshots) return } @@ -51,8 +51,8 @@ func getConnections(w http.ResponseWriter, r *http.Request) { buf := &bytes.Buffer{} sendSnapshot := func() error { buf.Reset() - snapshot := statistic.DefaultManager.Snapshot() - if err := json.NewEncoder(buf).Encode(snapshot); err != nil { + snapshots := statistic.Snapshots() + if err := json.NewEncoder(buf).Encode(snapshots); err != nil { return err } @@ -74,16 +74,23 @@ func getConnections(w http.ResponseWriter, r *http.Request) { func closeConnection(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "id") - if c := statistic.DefaultManager.Get(id); c != nil { - _ = c.Close() +here: + for _, manager := range statistic.ChannelManager { + if c := manager.Get(id); c != nil { + _ = c.Close() + break here + } + } render.NoContent(w, r) } func closeAllConnections(w http.ResponseWriter, r *http.Request) { - statistic.DefaultManager.Range(func(c statistic.Tracker) bool { - _ = c.Close() - return true - }) + for _, v := range statistic.ChannelManager { + v.Range(func(c statistic.Tracker) bool { + _ = c.Close() + return true + }) + } render.NoContent(w, r) } diff --git a/hub/route/server.go b/hub/route/server.go index c28782b9..813e4222 100644 --- a/hub/route/server.go +++ b/hub/route/server.go @@ -5,9 +5,16 @@ import ( "crypto/subtle" "crypto/tls" "encoding/json" + "fmt" + S "github.com/kardianos/service" + "github.com/metacubex/mihomo/adapter" + "github.com/metacubex/mihomo/adapter/outboundgroup" + cs "github.com/metacubex/mihomo/service" + "github.com/metacubex/mihomo/tunnel" "net" "net/http" "os" + "os/exec" "path/filepath" "runtime/debug" "strings" @@ -89,8 +96,9 @@ func router(isDebug bool, withAuth bool) *chi.Mux { r.Mount("/providers/rules", ruleProviderRouter()) r.Mount("/cache", cacheRouter()) r.Mount("/dns", dnsRouter()) - r.Mount("/restart", restartRouter()) - r.Mount("/upgrade", upgradeRouter()) + //r.Mount("/restart", restartRouter()) + //r.Mount("/upgrade", upgradeRouter()) + r.Get("/reboot", reboot) addExternalRouters(r) }) @@ -106,7 +114,6 @@ func router(isDebug bool, withAuth bool) *chi.Mux { } return r } - func Start(addr string, tlsAddr string, secret string, certificate, privateKey string, isDebug bool) { if serverAddr != "" { @@ -157,7 +164,6 @@ func Start(addr string, tlsAddr string, secret string, } } - func StartUnix(addr string, isDebug bool) { addr = C.Path.Resolve(addr) @@ -190,7 +196,6 @@ func StartUnix(addr string, isDebug bool) { log.Errorln("External controller unix serve error: %s", err) } } - func setPrivateNetworkAccess(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method == http.MethodOptions && r.Header.Get("Access-Control-Request-Method") != "" { @@ -261,16 +266,41 @@ func traffic(w http.ResponseWriter, r *http.Request) { tick := time.NewTicker(time.Second) defer tick.Stop() - t := statistic.DefaultManager + cs := statistic.ChannelManager buf := &bytes.Buffer{} + + proxies := tunnel.Proxies() + + selectors := make([]*outboundgroup.Selector, 0) + for _, proxy := range proxies { + if adapterP, ok := proxy.(*adapter.Proxy); ok { + adapter := adapterP.ProxyAdapter + if adapterP.Type() == C.Selector { + selector := adapter.(*outboundgroup.Selector) + selectors = append(selectors, selector) + } + } + } + var err error for range tick.C { buf.Reset() - up, down := t.Now() - if err := json.NewEncoder(buf).Encode(Traffic{ - Up: up, - Down: down, - }); err != nil { + var traffics map[string]Traffic = make(map[string]Traffic) + for _, s := range selectors { + id := s.Now() + if _, ok := traffics[id]; ok { + continue + } + if v, ok := cs[id]; ok { + up, down := v.Now() + traffic := Traffic{ + Up: up, + Down: down, + } + traffics[id] = traffic + } + } + if err := json.NewEncoder(buf).Encode(traffics); err != nil { break } @@ -304,26 +334,23 @@ func memory(w http.ResponseWriter, r *http.Request) { tick := time.NewTicker(time.Second) defer tick.Stop() - t := statistic.DefaultManager + buf := &bytes.Buffer{} - var err error - first := true + for range tick.C { buf.Reset() - inuse := t.Memory() - // make chat.js begin with zero - // this is shit var,but we need output 0 for first time - if first { - inuse = 0 - first = false + stat, err := statistic.Processor.MemoryInfo() + if err != nil { + return } + inuse := stat.RSS if err := json.NewEncoder(buf).Encode(Memory{ - Inuse: inuse, - OSLimit: 0, + Inuse: inuse, }); err != nil { break } + if wsConn == nil { _, err = w.Write(buf.Bytes()) w.(http.Flusher).Flush() @@ -445,3 +472,39 @@ func getLogs(w http.ResponseWriter, r *http.Request) { func version(w http.ResponseWriter, r *http.Request) { render.JSON(w, r, render.M{"meta": C.Meta, "version": C.Version}) } + +func reboot(w http.ResponseWriter, r *http.Request) { + + if cs.IsOpenWrt() { + confPath := "/etc/init.d/" + cs.ServiceName + cmd := exec.Command(confPath, "restart") + err := cmd.Start() + if err != nil { + w.WriteHeader(http.StatusBadRequest) + fmt.Fprintf(w, "%v", err) + return + } + w.WriteHeader(http.StatusOK) + return + } + + status, _ := cs.Srv.Status() + if status == S.StatusUnknown { + w.WriteHeader(http.StatusServiceUnavailable) + } else { + execPath, err := os.Executable() + if err != nil { + w.WriteHeader(http.StatusBadRequest) + fmt.Fprintf(w, "%v", err) + return + } + cmd := exec.Command(execPath, "-s", "restart") + err = cmd.Start() + if err != nil { + w.WriteHeader(http.StatusBadRequest) + fmt.Fprintf(w, "%v", err) + return + } + w.WriteHeader(http.StatusOK) + } +} diff --git a/hub/route/upgrade.go b/hub/route/upgrade.go index db00af5c..ea371798 100644 --- a/hub/route/upgrade.go +++ b/hub/route/upgrade.go @@ -6,7 +6,8 @@ import ( "net/http" "os" - "github.com/metacubex/mihomo/component/updater" + "github.com/metacubex/mihomo/config" + "github.com/metacubex/mihomo/hub/updater" "github.com/metacubex/mihomo/log" "github.com/go-chi/chi/v5" @@ -17,7 +18,6 @@ func upgradeRouter() http.Handler { r := chi.NewRouter() r.Post("/", upgradeCore) r.Post("/ui", updateUI) - r.Post("/geo", updateGeoDatabases) return r } @@ -31,7 +31,7 @@ func upgradeCore(w http.ResponseWriter, r *http.Request) { return } - err = updater.UpdateCore(execPath) + err = updater.Update(execPath) if err != nil { log.Warnln("%s", err) render.Status(r, http.StatusInternalServerError) @@ -48,9 +48,9 @@ func upgradeCore(w http.ResponseWriter, r *http.Request) { } func updateUI(w http.ResponseWriter, r *http.Request) { - err := updater.UpdateUI() + err := config.UpdateUI() if err != nil { - if errors.Is(err, updater.ErrIncompleteConf) { + if errors.Is(err, config.ErrIncompleteConf) { log.Warnln("%s", err) render.Status(r, http.StatusNotImplemented) render.JSON(w, r, newError(fmt.Sprintf("%s", err))) diff --git a/hub/updater/limitedreader.go b/hub/updater/limitedreader.go new file mode 100644 index 00000000..c31db601 --- /dev/null +++ b/hub/updater/limitedreader.go @@ -0,0 +1,67 @@ +package updater + +import ( + "fmt" + "io" + + "golang.org/x/exp/constraints" +) + +// LimitReachedError records the limit and the operation that caused it. +type LimitReachedError struct { + Limit int64 +} + +// Error implements the [error] interface for *LimitReachedError. +// +// TODO(a.garipov): Think about error string format. +func (lre *LimitReachedError) Error() string { + return fmt.Sprintf("attempted to read more than %d bytes", lre.Limit) +} + +// limitedReader is a wrapper for [io.Reader] limiting the input and dealing +// with errors package. +type limitedReader struct { + r io.Reader + limit int64 + n int64 +} + +// Read implements the [io.Reader] interface. +func (lr *limitedReader) Read(p []byte) (n int, err error) { + if lr.n == 0 { + return 0, &LimitReachedError{ + Limit: lr.limit, + } + } + + p = p[:Min(lr.n, int64(len(p)))] + + n, err = lr.r.Read(p) + lr.n -= int64(n) + + return n, err +} + +// LimitReader wraps Reader to make it's Reader stop with ErrLimitReached after +// n bytes read. +func LimitReader(r io.Reader, n int64) (limited io.Reader, err error) { + if n < 0 { + return nil, &updateError{Message: "limit must be non-negative"} + } + + return &limitedReader{ + r: r, + limit: n, + n: n, + }, nil +} + +// Min returns the smaller of x or y. +func Min[T constraints.Integer | ~string](x, y T) (res T) { + if x < y { + return x + } + + return y +} diff --git a/hub/updater/updater.go b/hub/updater/updater.go new file mode 100644 index 00000000..02ff07ba --- /dev/null +++ b/hub/updater/updater.go @@ -0,0 +1,496 @@ +package updater + +import ( + "archive/zip" + "compress/gzip" + "context" + "fmt" + "io" + "net/http" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + "sync" + "time" + + mihomoHttp "github.com/metacubex/mihomo/component/http" + "github.com/metacubex/mihomo/constant" + C "github.com/metacubex/mihomo/constant" + "github.com/metacubex/mihomo/log" + + "github.com/klauspost/cpuid/v2" +) + +// modify from https://github.com/AdguardTeam/AdGuardHome/blob/595484e0b3fb4c457f9bb727a6b94faa78a66c5f/internal/updater/updater.go +// Updater is the mihomo updater. +var ( + goarm string + gomips string + amd64Compatible string + + workDir string + + // mu protects all fields below. + mu sync.Mutex + + currentExeName string // 当前可执行文件 + updateDir string // 更新目录 + packageName string // 更新压缩文件 + backupDir string // 备份目录 + backupExeName string // 备份文件名 + updateExeName string // 更新后的可执行文件 + + baseURL string = "https://github.com/MetaCubeX/mihomo/releases/download/Prerelease-Alpha/mihomo" + versionURL string = "https://github.com/MetaCubeX/mihomo/releases/download/Prerelease-Alpha/version.txt" + packageURL string + latestVersion string +) + +func init() { + if runtime.GOARCH == "amd64" && cpuid.CPU.X64Level() < 3 { + amd64Compatible = "-compatible" + } +} + +type updateError struct { + Message string +} + +func (e *updateError) Error() string { + return fmt.Sprintf("update error: %s", e.Message) +} + +// Update performs the auto-updater. It returns an error if the updater failed. +// If firstRun is true, it assumes the configuration file doesn't exist. +func Update(execPath string) (err error) { + mu.Lock() + defer mu.Unlock() + + latestVersion, err = getLatestVersion() + if err != nil { + return err + } + + log.Infoln("current version %s, latest version %s", constant.Version, latestVersion) + + if latestVersion == constant.Version { + err := &updateError{Message: "already using latest version"} + return err + } + + updateDownloadURL() + + defer func() { + if err != nil { + log.Errorln("updater: failed: %v", err) + } else { + log.Infoln("updater: finished") + } + }() + + workDir = filepath.Dir(execPath) + + err = prepare(execPath) + if err != nil { + return fmt.Errorf("preparing: %w", err) + } + + defer clean() + + err = downloadPackageFile() + if err != nil { + return fmt.Errorf("downloading package file: %w", err) + } + + err = unpack() + if err != nil { + return fmt.Errorf("unpacking: %w", err) + } + + err = backup() + if err != nil { + return fmt.Errorf("backuping: %w", err) + } + + err = replace() + if err != nil { + return fmt.Errorf("replacing: %w", err) + } + + return nil +} + +// prepare fills all necessary fields in Updater object. +func prepare(exePath string) (err error) { + updateDir = filepath.Join(workDir, "meta-update") + currentExeName = exePath + _, pkgNameOnly := filepath.Split(packageURL) + if pkgNameOnly == "" { + return fmt.Errorf("invalid PackageURL: %q", packageURL) + } + + packageName = filepath.Join(updateDir, pkgNameOnly) + //log.Infoln(packageName) + backupDir = filepath.Join(workDir, "meta-backup") + + if runtime.GOOS == "windows" { + updateExeName = "mihomo" + "-" + runtime.GOOS + "-" + runtime.GOARCH + amd64Compatible + ".exe" + } else if runtime.GOOS == "android" && runtime.GOARCH == "arm64" { + updateExeName = "mihomo-android-arm64-v8" + } else { + updateExeName = "mihomo" + "-" + runtime.GOOS + "-" + runtime.GOARCH + amd64Compatible + } + + log.Infoln("updateExeName: %s ", updateExeName) + + backupExeName = filepath.Join(backupDir, filepath.Base(exePath)) + updateExeName = filepath.Join(updateDir, updateExeName) + + log.Infoln( + "updater: updating using url: %s", + packageURL, + ) + + currentExeName = exePath + _, err = os.Stat(currentExeName) + if err != nil { + return fmt.Errorf("checking %q: %w", currentExeName, err) + } + + return nil +} + +// unpack extracts the files from the downloaded archive. +func unpack() error { + var err error + _, pkgNameOnly := filepath.Split(packageURL) + + log.Infoln("updater: unpacking package") + if strings.HasSuffix(pkgNameOnly, ".zip") { + _, err = zipFileUnpack(packageName, updateDir) + if err != nil { + return fmt.Errorf(".zip unpack failed: %w", err) + } + + } else if strings.HasSuffix(pkgNameOnly, ".gz") { + _, err = gzFileUnpack(packageName, updateDir) + if err != nil { + return fmt.Errorf(".gz unpack failed: %w", err) + } + + } else { + return fmt.Errorf("unknown package extension") + } + + return nil +} + +// backup makes a backup of the current executable file +func backup() (err error) { + log.Infoln("updater: backing up current ExecFile:%s to %s", currentExeName, backupExeName) + _ = os.Mkdir(backupDir, 0o755) + + err = os.Rename(currentExeName, backupExeName) + if err != nil { + return err + } + + return nil +} + +// replace moves the current executable with the updated one +func replace() error { + var err error + + log.Infoln("replacing: %s to %s", updateExeName, currentExeName) + if runtime.GOOS == "windows" { + // rename fails with "File in use" error + err = copyFile(updateExeName, currentExeName) + } else { + err = os.Rename(updateExeName, currentExeName) + } + if err != nil { + return err + } + + log.Infoln("updater: renamed: %s to %s", updateExeName, currentExeName) + + return nil +} + +// clean removes the temporary directory itself and all it's contents. +func clean() { + _ = os.RemoveAll(updateDir) +} + +// MaxPackageFileSize is a maximum package file length in bytes. The largest +// package whose size is limited by this constant currently has the size of +// approximately 9 MiB. +const MaxPackageFileSize = 32 * 1024 * 1024 + +// Download package file and save it to disk +func downloadPackageFile() (err error) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) + defer cancel() + resp, err := mihomoHttp.HttpRequest(ctx, packageURL, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil) + if err != nil { + return fmt.Errorf("http request failed: %w", err) + } + + defer func() { + closeErr := resp.Body.Close() + if closeErr != nil && err == nil { + err = closeErr + } + }() + + var r io.Reader + r, err = LimitReader(resp.Body, MaxPackageFileSize) + if err != nil { + return fmt.Errorf("http request failed: %w", err) + } + + log.Debugln("updater: reading http body") + // This use of ReadAll is now safe, because we limited body's Reader. + body, err := io.ReadAll(r) + if err != nil { + return fmt.Errorf("io.ReadAll() failed: %w", err) + } + + log.Debugln("updateDir %s", updateDir) + err = os.Mkdir(updateDir, 0o755) + if err != nil { + return fmt.Errorf("mkdir error: %w", err) + } + + log.Debugln("updater: saving package to file %s", packageName) + err = os.WriteFile(packageName, body, 0o644) + if err != nil { + return fmt.Errorf("os.WriteFile() failed: %w", err) + } + return nil +} + +// Unpack a single .gz file to the specified directory +// Existing files are overwritten +// All files are created inside outDir, subdirectories are not created +// Return the output file name +func gzFileUnpack(gzfile, outDir string) (string, error) { + f, err := os.Open(gzfile) + if err != nil { + return "", fmt.Errorf("os.Open(): %w", err) + } + + defer func() { + closeErr := f.Close() + if closeErr != nil && err == nil { + err = closeErr + } + }() + + gzReader, err := gzip.NewReader(f) + if err != nil { + return "", fmt.Errorf("gzip.NewReader(): %w", err) + } + + defer func() { + closeErr := gzReader.Close() + if closeErr != nil && err == nil { + err = closeErr + } + }() + // Get the original file name from the .gz file header + originalName := gzReader.Header.Name + if originalName == "" { + // Fallback: remove the .gz extension from the input file name if the header doesn't provide the original name + originalName = filepath.Base(gzfile) + originalName = strings.TrimSuffix(originalName, ".gz") + } + + outputName := filepath.Join(outDir, originalName) + + // Create the output file + wc, err := os.OpenFile( + outputName, + os.O_WRONLY|os.O_CREATE|os.O_TRUNC, + 0o755, + ) + if err != nil { + return "", fmt.Errorf("os.OpenFile(%s): %w", outputName, err) + } + + defer func() { + closeErr := wc.Close() + if closeErr != nil && err == nil { + err = closeErr + } + }() + + // Copy the contents of the gzReader to the output file + _, err = io.Copy(wc, gzReader) + if err != nil { + return "", fmt.Errorf("io.Copy(): %w", err) + } + + return outputName, nil +} + +// Unpack a single file from .zip file to the specified directory +// Existing files are overwritten +// All files are created inside 'outDir', subdirectories are not created +// Return the output file name +func zipFileUnpack(zipfile, outDir string) (string, error) { + zrc, err := zip.OpenReader(zipfile) + if err != nil { + return "", fmt.Errorf("zip.OpenReader(): %w", err) + } + + defer func() { + closeErr := zrc.Close() + if closeErr != nil && err == nil { + err = closeErr + } + }() + if len(zrc.File) == 0 { + return "", fmt.Errorf("no files in the zip archive") + } + + // Assuming the first file in the zip archive is the target file + zf := zrc.File[0] + var rc io.ReadCloser + rc, err = zf.Open() + if err != nil { + return "", fmt.Errorf("zip file Open(): %w", err) + } + + defer func() { + closeErr := rc.Close() + if closeErr != nil && err == nil { + err = closeErr + } + }() + fi := zf.FileInfo() + name := fi.Name() + outputName := filepath.Join(outDir, name) + + if fi.IsDir() { + return "", fmt.Errorf("the target file is a directory") + } + + var wc io.WriteCloser + wc, err = os.OpenFile(outputName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, fi.Mode()) + if err != nil { + return "", fmt.Errorf("os.OpenFile(): %w", err) + } + + defer func() { + closeErr := wc.Close() + if closeErr != nil && err == nil { + err = closeErr + } + }() + _, err = io.Copy(wc, rc) + if err != nil { + return "", fmt.Errorf("io.Copy(): %w", err) + } + + return outputName, nil +} + +// Copy file on disk +func copyFile(src, dst string) error { + d, e := os.ReadFile(src) + if e != nil { + return e + } + e = os.WriteFile(dst, d, 0o644) + if e != nil { + return e + } + return nil +} + +func getLatestVersion() (version string, err error) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) + defer cancel() + resp, err := mihomoHttp.HttpRequest(ctx, versionURL, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil) + if err != nil { + return "", fmt.Errorf("get Latest Version fail: %w", err) + } + defer func() { + closeErr := resp.Body.Close() + if closeErr != nil && err == nil { + err = closeErr + } + }() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return "", fmt.Errorf("get Latest Version fail: %w", err) + } + content := strings.TrimRight(string(body), "\n") + return content, nil +} + +func updateDownloadURL() { + var middle string + + if runtime.GOARCH == "arm" && probeGoARM() { + //-linux-armv7-alpha-e552b54.gz + middle = fmt.Sprintf("-%s-%s%s-%s", runtime.GOOS, runtime.GOARCH, goarm, latestVersion) + } else if runtime.GOARCH == "arm64" { + //-linux-arm64-alpha-e552b54.gz + if runtime.GOOS == "android" { + middle = fmt.Sprintf("-%s-%s-v8-%s", runtime.GOOS, runtime.GOARCH, latestVersion) + } else { + middle = fmt.Sprintf("-%s-%s-%s", runtime.GOOS, runtime.GOARCH, latestVersion) + } + } else if isMIPS(runtime.GOARCH) && gomips != "" { + middle = fmt.Sprintf("-%s-%s-%s-%s", runtime.GOOS, runtime.GOARCH, gomips, latestVersion) + } else { + middle = fmt.Sprintf("-%s-%s%s-%s", runtime.GOOS, runtime.GOARCH, amd64Compatible, latestVersion) + } + + if runtime.GOOS == "windows" { + middle += ".zip" + } else { + middle += ".gz" + } + packageURL = baseURL + middle + //log.Infoln(packageURL) +} + +// isMIPS returns true if arch is any MIPS architecture. +func isMIPS(arch string) (ok bool) { + switch arch { + case + "mips", + "mips64", + "mips64le", + "mipsle": + return true + default: + return false + } +} + +// linux only +func probeGoARM() (ok bool) { + cmd := exec.Command("cat", "/proc/cpuinfo") + output, err := cmd.Output() + if err != nil { + log.Errorln("probe goarm error:%s", err) + return false + } + cpuInfo := string(output) + if strings.Contains(cpuInfo, "vfpv3") || strings.Contains(cpuInfo, "vfpv4") { + goarm = "v7" + } else if strings.Contains(cpuInfo, "vfp") { + goarm = "v6" + } else { + goarm = "v5" + } + return true +} diff --git a/listener/auth/auth.go b/listener/auth/auth.go index 46f552b8..cea8ee55 100644 --- a/listener/auth/auth.go +++ b/listener/auth/auth.go @@ -5,11 +5,17 @@ import ( ) var authenticator auth.Authenticator +var authenticatorTls auth.Authenticator func Authenticator() auth.Authenticator { return authenticator } - +func AuthenticatorTls() auth.Authenticator { + return authenticatorTls +} func SetAuthenticator(au auth.Authenticator) { authenticator = au } +func SetAuthenticatorTls(au auth.Authenticator) { + authenticatorTls = au +} diff --git a/listener/mixed/conn_wrap.go b/listener/mixed/conn_wrap.go new file mode 100644 index 00000000..d0d0f960 --- /dev/null +++ b/listener/mixed/conn_wrap.go @@ -0,0 +1,96 @@ +package mixed + +import ( + "io" + "net" + "time" +) + +type MyConn struct { + net.Conn + buf []byte + bufLen int + localAddr net.Addr + remoteAddr net.Addr + peeked bool +} + +func NewMyConn(conn net.Conn) *MyConn { + MyConn := MyConn{ + Conn: conn, + localAddr: conn.LocalAddr(), + remoteAddr: conn.RemoteAddr(), + } + return &MyConn +} + +func (c *MyConn) Peek(num int) ([]byte, error) { + c.peeked = true + c.bufLen = num + c.buf = make([]byte, c.bufLen) + _, err := io.ReadFull(c.Conn, c.buf) + return c.buf, err +} + +func (c *MyConn) Read(b []byte) (n int, err error) { + if c.peeked { + n := copy(b, c.buf) + if n < c.bufLen { + c.bufLen -= n + c.buf = c.buf[n:] + } else { + c.peeked = false + c.buf = nil + c.bufLen = 0 + } + return n, nil + } else { + if c.Conn == nil { + return 0, io.EOF + } + return c.Conn.Read(b) + } +} + +func (c *MyConn) Write(b []byte) (n int, err error) { + if c.Conn != nil { + return c.Conn.Write(b) + } + return 0, io.EOF +} + +func (c *MyConn) Close() error { + c.buf = nil + conn := c.Conn + c.Conn = nil + if conn != nil { + return conn.Close() + } + return nil +} + +func (c *MyConn) RemoteAddr() net.Addr { + return c.remoteAddr +} + +func (c *MyConn) LocalAddr() net.Addr { + return c.localAddr +} + +func (c *MyConn) SetDeadline(t time.Time) error { + if c.Conn != nil { + c.Conn.SetDeadline(t) + return nil + } else { + return io.EOF + } +} + +func (c *MyConn) SetReadDeadline(t time.Time) error { + if c.Conn != nil { + c.Conn.SetReadDeadline(t) + return nil + } else { + return io.EOF + } +} diff --git a/listener/mixed/handle_tls.go b/listener/mixed/handle_tls.go new file mode 100644 index 00000000..28eca2eb --- /dev/null +++ b/listener/mixed/handle_tls.go @@ -0,0 +1,202 @@ +package mixed + +import ( + "bytes" + "crypto/tls" + "github.com/metacubex/mihomo/adapter/inbound" + "github.com/metacubex/mihomo/listener/socks" + "net" + "strconv" + "time" + + N "github.com/metacubex/mihomo/common/net" + C "github.com/metacubex/mihomo/constant" + "github.com/metacubex/mihomo/log" + "github.com/metacubex/mihomo/transport/socks5" + proxyproto "github.com/pires/go-proxyproto" +) + +const recordTypeHandshake = 0x16 + +var sshPrefix = []byte("SSH-") +var SynDrive = []byte{0x25, 0x52, 0x18, 0x14} + +var tlsConfig *tls.Config + +func NewTls(addr string, wanInput *inbound.WanInput, tunnel C.Tunnel) (*Listener, error) { + + if wanInput.TlsProxy != nil { + if wanInput.TlsProxy.Cert != "" && wanInput.TlsProxy.Key != "" { + certFile := wanInput.TlsProxy.Cert + keyFile := wanInput.TlsProxy.Key + cert, err := tls.LoadX509KeyPair(certFile, keyFile) + if err != nil { + return nil, err + } + + tlsConfig = &tls.Config{ + Certificates: []tls.Certificate{cert}, + } + + } + } + + l, err := net.Listen("tcp", addr) + if err != nil { + return nil, err + } + proxyListener := &proxyproto.Listener{Listener: l} + + ml := &Listener{ + listener: proxyListener, + addr: addr, + // cache: cache.New(cache.WithAge(30)), + } + go func() { + for { + c, err := ml.listener.Accept() + + // log.Warnln("local %s, remote %s", c.LocalAddr().String(), c.RemoteAddr().String()) + if err != nil { + if ml.closed { + break + } + continue + } + tcpcon, ok := c.(*proxyproto.Conn).TCPConn() + if ok { + N.TCPKeepAlive(tcpcon) + } + + myConn := NewMyConn(c) + head, err := myConn.Peek(4) + if err != nil { + c.Close() + continue + } + + if head[0] == recordTypeHandshake && tlsConfig != nil { + go handleConnTls(myConn, wanInput.TlsProxy, tunnel) + } else if bytes.Equal(head, sshPrefix) { + go handleSSh(myConn, wanInput.SshProxy) + } else if bytes.Equal(head, SynDrive) { + go handleSynDrive(myConn, wanInput.Syndrive) + } else { + myConn.Close() + } + + } + }() + + return ml, nil +} + +func handleSynDrive(conn net.Conn, syndrive *inbound.SynDrive) { + defer conn.Close() + if syndrive != nil { + conn2target(conn, syndrive.Target, false, "syn") + } + +} + +func handleSSh(conn net.Conn, sshProxy *inbound.SSHProxy) { + defer conn.Close() + if sshProxy != nil { + conn2target(conn, sshProxy.Target, sshProxy.ProxyProto, "ssh") + } else { + sshServer.HandleConn(conn) + } + +} + +func handleConnTls(conn net.Conn, tlsProxy *inbound.TLSProxy, tunnel C.Tunnel) { + tlsConn := tls.Server(conn, tlsConfig) + err := tlsConn.Handshake() + if err != nil { + tlsConn.Close() + return + } + + myConn := NewMyConn(tlsConn) + head, err := myConn.Peek(1) + if err != nil { + myConn.Close() + return + } + if head[0] == socks5.Version { + socks.HandleSocks5Tls(myConn, tunnel) + } else { + defer myConn.Close() + + connectionState := tlsConn.ConnectionState() + domain := connectionState.ServerName + + target, ok := tlsProxy.TlsTargets[domain] + if ok { + conn2target(myConn, target.Target, target.ProxyProto, "tls") + } else { + target, ok := tlsProxy.TlsTargets["all"] + if ok { + conn2target(myConn, target.Target, target.ProxyProto, "tls") + } + } + } +} + +func conn2target(conn net.Conn, target string, useProxyProto bool, protoType string) { + cc, err := net.DialTimeout("tcp", target, 5*time.Second) + if err != nil { + log.Errorln("%s -> %v", conn.RemoteAddr().String(), err) + return + } + defer cc.Close() + if useProxyProto { + shost, sport, err := net.SplitHostPort(conn.RemoteAddr().String()) + if err != nil { + log.Errorln("%s -> %v", conn.RemoteAddr().String(), err) + return + } + siport, err := strconv.Atoi(sport) + if err != nil { + log.Errorln("%s -> %v", conn.RemoteAddr().String(), err) + return + } + lhost, lport, err := net.SplitHostPort(conn.LocalAddr().String()) + if err != nil { + log.Errorln("%s -> %v", conn.RemoteAddr().String(), err) + return + } + liport, err := strconv.Atoi(lport) + if err != nil { + log.Errorln("%s -> %v", conn.RemoteAddr().String(), err) + return + } + + remoteIp := net.ParseIP(shost) + tranPro := proxyproto.TCPv6 + if remoteIp.To4() != nil { + tranPro = proxyproto.TCPv4 + } + header := &proxyproto.Header{ + Version: 1, + Command: proxyproto.PROXY, + TransportProtocol: tranPro, + SourceAddr: &net.TCPAddr{ + IP: remoteIp, + Port: siport, + }, + DestinationAddr: &net.TCPAddr{ + IP: net.ParseIP(lhost), + Port: liport, + }, + } + + _, err = header.WriteTo(cc) + if err != nil { + log.Errorln("%s -> %v", conn.RemoteAddr().String(), err) + return + } + } + log.Infoln("%s %s -> %v", protoType, conn.RemoteAddr().String(), target) + N.Relay(conn, cc) +} diff --git a/listener/mixed/sshd.go b/listener/mixed/sshd.go new file mode 100644 index 00000000..487fc848 --- /dev/null +++ b/listener/mixed/sshd.go @@ -0,0 +1,210 @@ +package mixed + +import ( + "crypto/rand" + "crypto/rsa" + "github.com/gliderlabs/ssh" + "github.com/metacubex/mihomo/adapter/inbound" + C "github.com/metacubex/mihomo/constant" + authStore "github.com/metacubex/mihomo/listener/auth" + log "github.com/sirupsen/logrus" + gossh "golang.org/x/crypto/ssh" + "net" + "time" +) + +var sshServer ssh.Server + +type sshConn struct { + net.Conn + closeCallback func() + ctx ssh.Context +} + +var ( + DeadlineTimeout = 30 * time.Second + IdleTimeout = 10 * time.Second + tunnel C.Tunnel +) + +func InitSShServer(tunnel_ C.Tunnel) { + tunnel = tunnel_ + sshServer = ssh.Server{ + PasswordHandler: passwordHandler, + + ConnectionFailedCallback: sshConnectionFailed, + LocalPortForwardingCallback: ssh.LocalPortForwardingCallback(func(ctx ssh.Context, dhost string, dport uint32) bool { + //log.Println("Accepted forward", dhost, dport) + return true + }), + + ChannelHandlers: map[string]ssh.ChannelHandler{ + "direct-tcpip": DirectTCPIPHandler, + "session": SessionHandler, + }, + PtyCallback: func(ctx ssh.Context, pty ssh.Pty) bool { + return false + }, + //MaxTimeout: DeadlineTimeout, + //IdleTimeout: IdleTimeout, + } + + //if len(sshServer.HostSigners) == 0 { + // signer, err := generateSigner() + // if err != nil { + // + // log.Panicf("%v", err) + // } + // sshServer.HostSigners = append(sshServer.HostSigners, signer) + //} + sshServer.SetOption(HostKeyFile()) + +} + +func passwordHandler(ctx ssh.Context, password string) bool { + author := authStore.AuthenticatorTls() + if author == nil { + return true + } + + if author.Verify(ctx.User(), password) { + return true + } + return false +} + +func sshConnectionFailed(conn net.Conn, err error) { + // Log the underlying error with a specific message + log.Warnf("ssh: Failed connection from %s with error: %v", conn.RemoteAddr(), err) + +} + +func DirectTCPIPHandler(srv *ssh.Server, conn *gossh.ServerConn, newChan gossh.NewChannel, ctx ssh.Context) { + d := inbound.LocalForwardChannelData{} + if err := gossh.Unmarshal(newChan.ExtraData(), &d); err != nil { + newChan.Reject(gossh.ConnectionFailed, "error parsing forward data: "+err.Error()) + return + } + + if srv.LocalPortForwardingCallback == nil || !srv.LocalPortForwardingCallback(ctx, d.DestAddr, d.DestPort) { + newChan.Reject(gossh.Prohibited, "port forwarding is disabled") + return + } + + //dest := net.JoinHostPort(d.DestAddr, strconv.FormatInt(int64(d.DestPort), 10)) + + //var dialer net.Dialer + //dconn, err := dialer.DialContext(ctx, "tcp", dest) + //if err != nil { + // newChan.Reject(gossh.ConnectionFailed, err.Error()) + // return + //} + + ch, reqs, err := newChan.Accept() + if err != nil { + //dconn.Close() + return + } + //go gossh.DiscardRequests(reqs) + breakout := make(chan struct{}) + go func() { + for { + select { + case req := <-reqs: + if req != nil && req.WantReply { + req.Reply(false, nil) + } + case <-breakout: + return + + } + } + }() + + //go func() { + // defer ch.Close() + // defer dconn.Close() + // io.Copy(ch, dconn) + //}() + //go func() { + // defer ch.Close() + // defer dconn.Close() + // io.Copy(dconn, ch) + //}() + + sshConn := inbound.MySSHConn{ + Channel: ch, + LocalAddr_: conn.LocalAddr(), + RemoteAddr_: conn.RemoteAddr(), + } + metadata := inbound.NewSSH(&d, &sshConn, C.SSH) + if !metadata.Valid() { + log.Warnln("ssh not valid: %#v", newChan.ExtraData()) + } + go func(breakout chan<- struct{}) { + defer sshConn.Close() + defer func() { + breakout <- struct{}{} + }() + tunnel.HandleTCPConn(&sshConn, metadata) + }(breakout) +} + +func SessionHandler(srv *ssh.Server, conn *gossh.ServerConn, newChan gossh.NewChannel, ctx ssh.Context) { + defer func() { + conn.Close() + }() + ch, _, err := newChan.Accept() + if err != nil { + + return + } + defer func() { + ch.Close() + }() + var chars [1]byte + for { + _, err := ch.Read(chars[:]) + if err != nil { + break + } + _, err = ch.Write(chars[:]) + if err != nil { + break + } + //log.Infof("%d", chars[0]) + if chars[0] == 3 || chars[0] == 26 { //ctr-c == 3 ctr-z == 26 + break + } + } +} + +var ed25519_key = ` +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW +QyNTUxOQAAACCBH8jSvR0eyMdieVjyup2TKrtaCbB2WZzzYGKxdGLISQAAAKAVISnTFSEp +0wAAAAtzc2gtZWQyNTUxOQAAACCBH8jSvR0eyMdieVjyup2TKrtaCbB2WZzzYGKxdGLISQ +AAAEDl+FO3qPfVkYDbrC94EapwmxOYOzAHpRSz0bueb7dWI4EfyNK9HR7Ix2J5WPK6nZMq +u1oJsHZZnPNgYrF0YshJAAAAHHJvb3RAaVpicDE4dDI5bTNvNjE4OTg4b3RibloB +-----END OPENSSH PRIVATE KEY----- +` + +func HostKeyFile() ssh.Option { + return func(srv *ssh.Server) error { + + signer, err := gossh.ParsePrivateKey([]byte(ed25519_key)) + if err != nil { + return err + } + srv.AddHostKey(signer) + return nil + } +} + +func generateSigner() (ssh.Signer, error) { + key, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return nil, err + } + return gossh.NewSignerFromKey(key) +} diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index 09bf308c..2dc6524e 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -59,23 +59,15 @@ func CalculateInterfaceName(name string) (tunName string) { if err != nil { return } - tunIndex := 0 - indexSet := make(map[int]struct{}) + var tunIndex int for _, netInterface := range interfaces { if strings.HasPrefix(netInterface.Name, tunName) { index, parseErr := strconv.ParseInt(netInterface.Name[len(tunName):], 10, 16) if parseErr == nil { - indexSet[int(index)] = struct{}{} + tunIndex = int(index) + 1 } } } - for index := range indexSet { - if index == tunIndex { - tunIndex += 1 - } else { // indexSet already sorted and distinct, so this tunIndex nobody used - break - } - } tunName = F.ToString(tunName, tunIndex) return } diff --git a/listener/socks/tcp.go b/listener/socks/tcp.go index f2696e3f..51290050 100644 --- a/listener/socks/tcp.go +++ b/listener/socks/tcp.go @@ -98,12 +98,11 @@ func HandleSocks4(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) if inbound.SkipAuthRemoteAddr(conn.RemoteAddr()) { authenticator = nil } - addr, _, user, err := socks4.ServerHandshake(conn, authenticator) + addr, _, _, err := socks4.ServerHandshake(conn, authenticator) if err != nil { conn.Close() return } - additions = append(additions, inbound.WithInUser(user)) tunnel.HandleTCPConn(inbound.NewSocket(socks5.ParseAddr(addr), conn, C.SOCKS4, additions...)) } @@ -112,7 +111,7 @@ func HandleSocks5(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) if inbound.SkipAuthRemoteAddr(conn.RemoteAddr()) { authenticator = nil } - target, command, user, err := socks5.ServerHandshake(conn, authenticator) + target, command, _, err := socks5.ServerHandshake(conn, authenticator) if err != nil { conn.Close() return @@ -122,6 +121,19 @@ func HandleSocks5(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) io.Copy(io.Discard, conn) return } - additions = append(additions, inbound.WithInUser(user)) tunnel.HandleTCPConn(inbound.NewSocket(target, conn, C.SOCKS5, additions...)) } + +func HandleSocks5Tls(conn net.Conn, tunnel C.Tunnel) { + target, command, _, err := socks5.ServerHandshake(conn, authStore.AuthenticatorTls()) + if err != nil { + conn.Close() + return + } + if command == socks5.CmdUDPAssociate { + defer conn.Close() + io.Copy(io.Discard, conn) + return + } + tunnel.HandleTCPConn(inbound.NewSocket(target, conn, C.SOCKS5)) +} diff --git a/listener/tls_listener.go b/listener/tls_listener.go new file mode 100644 index 00000000..13b80e75 --- /dev/null +++ b/listener/tls_listener.go @@ -0,0 +1,40 @@ +package listener + +import ( + "github.com/metacubex/mihomo/adapter/inbound" + C "github.com/metacubex/mihomo/constant" + "strconv" + + "github.com/metacubex/mihomo/listener/mixed" + "github.com/metacubex/mihomo/log" +) + +func ReCreateMixedTls(wanInput *inbound.WanInput, tunnel C.Tunnel) { + + var err error + defer func() { + if err != nil { + log.Errorln("Start tls server error: %s", err.Error()) + } + }() + + addr := ":" + strconv.Itoa(wanInput.Port) + if portIsZero(addr) { + return + } + + //mixedListener, err = mixed.New(addr, tcpIn) + mixed.InitSShServer(tunnel) + mixedTlsListener, err := mixed.NewTls(addr, wanInput, tunnel) + if err != nil { + return + } + + // mixedTlsUDPLister, err = socks.NewUDP(addr, udpIn) + // if err != nil { + // mixedTlsListener.Close() + // return + // } + + log.Infoln("wan proxy listening at: %s", mixedTlsListener.Address()) +} diff --git a/listener/tproxy/packet.go b/listener/tproxy/packet.go index b038d954..e4852665 100644 --- a/listener/tproxy/packet.go +++ b/listener/tproxy/packet.go @@ -105,9 +105,9 @@ func listenLocalConn(rAddr, lAddr net.Addr, tunnel C.Tunnel) (*net.UDPConn, erro buf := pool.Get(pool.UDPBufferSize) br, err := lc.Read(buf) if err != nil { + pool.Put(buf) if errors.Is(err, net.ErrClosed) { log.Debugln("TProxy local conn listener exit.. rAddr=%s lAddr=%s", rAddr.String(), lAddr.String()) - pool.Put(buf) return } } diff --git a/log/log.go b/log/log.go index 6f565e7c..618bb9a7 100644 --- a/log/log.go +++ b/log/log.go @@ -1,7 +1,9 @@ package log import ( + "bytes" "fmt" + "gopkg.in/natefinch/lumberjack.v2" "os" "github.com/metacubex/mihomo/common/observable" @@ -15,14 +17,35 @@ var ( level = INFO ) +type MylogFormatter struct { +} + +func (f *MylogFormatter) Format(entry *log.Entry) ([]byte, error) { + + var b *bytes.Buffer + if entry.Buffer != nil { + b = entry.Buffer + } else { + b = &bytes.Buffer{} + } + + b.WriteString(entry.Time.Format("2006/01/02 15:04:05")) + b.WriteString(fmt.Sprintf(" |%.4s| ", entry.Level)) + + b.WriteString(entry.Message) + + b.WriteByte('\n') + return b.Bytes(), nil +} func init() { log.SetOutput(os.Stdout) log.SetLevel(log.DebugLevel) - log.SetFormatter(&log.TextFormatter{ - FullTimestamp: true, - TimestampFormat: "2006-01-02T15:04:05.999999999Z07:00", - EnvironmentOverrideColors: true, - }) + //log.SetFormatter(&log.TextFormatter{ + // FullTimestamp: true, + // TimestampFormat: "2006/01/02 15:04:05", + // EnvironmentOverrideColors: true, + //}) + log.SetFormatter(&MylogFormatter{}) } type Event struct { @@ -79,6 +102,18 @@ func SetLevel(newLevel LogLevel) { level = newLevel } +func SetOutput(file string, maxSize, maxBackups, maxAge int, compress bool) { + if file != "" { + log.SetOutput(&lumberjack.Logger{ + Filename: file, + MaxSize: maxSize, // megabytes + MaxBackups: maxBackups, + MaxAge: maxAge, //days + Compress: compress, // disabled by default + }) + } +} + func print(data Event) { if data.LogLevel < level { return diff --git a/main.go b/main.go index 61f1d683..c3a7f49c 100644 --- a/main.go +++ b/main.go @@ -3,39 +3,45 @@ package main import ( "flag" "fmt" + "github.com/metacubex/mihomo/component/updater" + "github.com/metacubex/mihomo/hub" "os" "os/signal" - "path/filepath" "runtime" + "runtime/debug" "strings" "syscall" + "time" + "unsafe" - "github.com/metacubex/mihomo/component/updater" "github.com/metacubex/mihomo/config" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/constant/features" - "github.com/metacubex/mihomo/hub" "github.com/metacubex/mihomo/hub/executor" "github.com/metacubex/mihomo/log" + cs "github.com/metacubex/mihomo/service" "go.uber.org/automaxprocs/maxprocs" ) var ( - version bool - testConfig bool - geodataMode bool - homeDir string - configFile string + version bool + testConfig bool + geodataMode bool + //homeDir string + //configFile string externalUI string externalController string externalControllerUnix string secret string + + service string + flagset map[string]bool ) func init() { - flag.StringVar(&homeDir, "d", os.Getenv("CLASH_HOME_DIR"), "set configuration directory") - flag.StringVar(&configFile, "f", os.Getenv("CLASH_CONFIG_FILE"), "specify configuration file") + //flag.StringVar(&homeDir, "d", os.Getenv("CLASH_HOME_DIR"), "set configuration directory") + //flag.StringVar(&configFile, "f", os.Getenv("CLASH_CONFIG_FILE"), "specify configuration file") flag.StringVar(&externalUI, "ext-ui", os.Getenv("CLASH_OVERRIDE_EXTERNAL_UI_DIR"), "override external ui directory") flag.StringVar(&externalController, "ext-ctl", os.Getenv("CLASH_OVERRIDE_EXTERNAL_CONTROLLER"), "override external controller address") flag.StringVar(&externalControllerUnix, "ext-ctl-unix", os.Getenv("CLASH_OVERRIDE_EXTERNAL_CONTROLLER_UNIX"), "override external controller unix address") @@ -44,9 +50,16 @@ func init() { flag.BoolVar(&version, "v", false, "show current version of mihomo") flag.BoolVar(&testConfig, "t", false, "test configuration and exit") flag.Parse() + + flagset = map[string]bool{} + flag.Visit(func(f *flag.Flag) { + flagset[f.Name] = true + }) } func main() { + a := 1 + log.Infoln("ptr size %d", unsafe.Sizeof(&a)) _, _ = maxprocs.Set(maxprocs.Logger(func(string, ...any) {})) if version { fmt.Printf("Mihomo Meta %s %s %s with %s %s\n", @@ -58,24 +71,6 @@ func main() { return } - if homeDir != "" { - if !filepath.IsAbs(homeDir) { - currentDir, _ := os.Getwd() - homeDir = filepath.Join(currentDir, homeDir) - } - C.SetHomeDir(homeDir) - } - - if configFile != "" { - if !filepath.IsAbs(configFile) { - currentDir, _ := os.Getwd() - configFile = filepath.Join(currentDir, configFile) - } - } else { - configFile = filepath.Join(C.Path.HomeDir(), C.Path.Config()) - } - C.SetConfig(configFile) - if geodataMode { C.GeodataMode = true } @@ -94,6 +89,15 @@ func main() { return } + prg := cs.NewService(run) + if flagset["s"] { + prg.Action(service) + return + } + prg.RunIt() +} + +func run() { var options []hub.Option if externalUI != "" { options = append(options, hub.WithExternalUI(externalUI)) @@ -127,7 +131,7 @@ func main() { } defer executor.Shutdown() - + memoryUsage() termSign := make(chan os.Signal, 1) hupSign := make(chan os.Signal, 1) signal.Notify(termSign, syscall.SIGINT, syscall.SIGTERM) @@ -145,3 +149,28 @@ func main() { } } } + +// memoryUsage implements a couple of not really beautiful hacks which purpose is to +// make OS reclaim the memory freed by AdGuard Home as soon as possible. +func memoryUsage() { + debug.SetGCPercent(10) + + // madvdontneed: setting madvdontneed=1 will use MADV_DONTNEED + // instead of MADV_FREE on Linux when returning memory to the + // kernel. This is less efficient, but causes RSS numbers to drop + // more quickly. + _ = os.Setenv("GODEBUG", "madvdontneed=1") + + // periodically call "debug.FreeOSMemory" so + // that the OS could reclaim the free memory + go func() { + ticker := time.NewTicker(15 * time.Second) + for { + select { + case t := <-ticker.C: + t.Second() + debug.FreeOSMemory() + } + } + }() +} diff --git a/rules/common/domain_regex.go b/rules/common/domain_regex.go index d214a772..baa8ce0e 100644 --- a/rules/common/domain_regex.go +++ b/rules/common/domain_regex.go @@ -1,9 +1,8 @@ package common import ( - C "github.com/metacubex/mihomo/constant" - "github.com/dlclark/regexp2" + C "github.com/metacubex/mihomo/constant" ) type DomainRegex struct { diff --git a/rules/common/fileDi.go b/rules/common/fileDi.go new file mode 100644 index 00000000..0267fb22 --- /dev/null +++ b/rules/common/fileDi.go @@ -0,0 +1,63 @@ +package common + +import ( + C "github.com/metacubex/mihomo/constant" + "github.com/metacubex/mihomo/rules/domain" + P "path" +) + +var sets map[string]*domain.DomainSet = make(map[string]*domain.DomainSet) + +type FileDI struct { + adapter string + payload string +} + +func (f *FileDI) RuleType() C.RuleType { + return C.FileDI +} + +func (f *FileDI) Match(metadata *C.Metadata) (bool, string) { + zoneset, ok := sets[f.payload] + if !ok { + return false, f.adapter + } + host := metadata.Host + if host == "" { + return false, f.adapter + } + if zoneset.HasDomain(host) { + return true, f.adapter + } + return false, f.adapter +} + +func (f *FileDI) Adapter() string { + return f.adapter +} + +func (f *FileDI) Payload() string { + return f.payload +} + +func (f *FileDI) ShouldResolveIP() bool { + return false +} + +func (f *FileDI) ShouldFindProcess() bool { + return false +} + +// FILEDI,alias,DIRECT,file path +func NewFileDi(payload, adapter, file string) *FileDI { + domainSet := &domain.DomainSet{ + File: P.Join(C.Path.HomeDir(), file), + Payload: payload, + } + domainSet.Init() + sets[payload] = domainSet + return &FileDI{ + adapter: adapter, + payload: payload, + } +} diff --git a/rules/common/fileDiRecord.go b/rules/common/fileDiRecord.go new file mode 100644 index 00000000..f3e80bdf --- /dev/null +++ b/rules/common/fileDiRecord.go @@ -0,0 +1,227 @@ +package common + +import ( + "bufio" + "context" + "github.com/metacubex/mihomo/component/mmdb" + "net" + "os" + P "path" + "slices" + "strings" + "sync" + "unicode" + + "github.com/metacubex/mihomo/component/resolver" + C "github.com/metacubex/mihomo/constant" + "github.com/metacubex/mihomo/log" + "github.com/metacubex/mihomo/rules/domain" +) + +var ( + setsRecord map[string]*domain.DomainSet = make(map[string]*domain.DomainSet) + fdrdoonce sync.Once + domainchannel chan *recordedDomain +) + +type recordedDomain struct { + domain string + zoneset *domain.DomainSet +} + +type FileDIRecord struct { + adapter string + payload string + conditions []string + operator string +} + +func (f *FileDIRecord) RuleType() C.RuleType { + return C.RECORD +} + +func (f *FileDIRecord) Match(metadata *C.Metadata) (bool, string) { + host := metadata.Host + if host == "" { + return false, "" + } + for _, zoneset := range setsRecord { + if zoneset.HasDomain(host) { + metadata.HitRule = zoneset.Payload + return true, zoneset.Adapter + } + } + if !metadata.DstIP.IsValid() { + ip, err := resolver.ResolveIP(context.TODO(), host) + if err != nil { + //log.Warnln("[DNS] resolve %s error: %s", metadata.Host, err.Error()) + return false, f.adapter + } else { + metadata.DstIP = ip + } + } + for _, zoneset := range setsRecord { + if matchISO(metadata.DstIP.AsSlice(), host, zoneset, metadata) { + return true, zoneset.Adapter + } + } + return false, f.adapter +} + +func (f *FileDIRecord) Adapter() string { + return f.adapter +} + +func (f *FileDIRecord) Payload() string { + return f.payload +} + +func (f *FileDIRecord) ShouldResolveIP() bool { + return false +} + +func (f *FileDIRecord) ShouldFindProcess() bool { + return false +} + +func NewFileDIRecord(payload, adapter string, params []string) *FileDIRecord { + fdrdoonce.Do(func() { + domainchannel = make(chan *recordedDomain, 100) + go func() { + for rd := range domainchannel { + readAndWrite(rd.zoneset.File, rd.domain) + rd.zoneset.Init() + } + }() + }) + groups := make([][]string, 0) + var count = 0 + for { + group := make([]string, 0) + if count == 0 { + group = append(group, payload) + group = append(group, adapter) + } + count++ + var idx = slices.Index(params, "@") + if idx > -1 { + group = append(group, params[0:idx]...) + groups = append(groups, group) + params = params[idx+1:] + } else { + group = append(group, params[:]...) + groups = append(groups, group) + break + } + } + for _, group := range groups { + log.Infoln("%v", group) + domainSet := &domain.DomainSet{ + File: P.Join(C.Path.HomeDir(), group[2]), + Conditions: group[4:], + Operator: group[3], + Adapter: group[1], + Payload: group[0], + } + domainSet.Init() + setsRecord[domainSet.Payload] = domainSet + } + + return &FileDIRecord{ + adapter: "unknown", + payload: "unknown", + } +} + +func matchISO(ipAddress net.IP, host string, domainSet *domain.DomainSet, metadata *C.Metadata) bool { + if !metadata.GeoedIp() { + metadata.DstGeoIP = mmdb.IPInstance().LookupCode(ipAddress) + if !metadata.GeoedIp() { + return false + } + } + iso := metadata.DstGeoIP[0] + var match bool + if domainSet.Operator == "and" { + match = true + for _, cond := range domainSet.Conditions { + if cond[0] == '!' { + cond = cond[1:] + match = strings.EqualFold(iso, cond) + match = !match + } else { + match = strings.EqualFold(iso, cond) + } + if !match { + return false + } + } + } else if domainSet.Operator == "or" { + match = false + for _, cond := range domainSet.Conditions { + if cond[0] == '!' { + cond = cond[1:] + match = strings.EqualFold(iso, cond) + match = !match + } else { + match = strings.EqualFold(iso, cond) + } + if match { + break + } + } + } else { + return false + } + + if match { + log.Warnln("add %s into %s, ip %s ios %s", host, domainSet.File, ipAddress, iso) + domainchannel <- &recordedDomain{ + domain: host, + zoneset: domainSet, + } + return true + } + return false +} + +func readAndWrite(file, domain string) { + var strs = make([]string, 0, 1000) + var seen = make(map[string]bool, 1000) + if _, err := os.Stat(file); err == nil { + f, err := os.OpenFile(file, os.O_RDONLY, os.ModePerm) + if err != nil { + log.Errorln("%v", err) + return + } + scanner := bufio.NewScanner(f) + for scanner.Scan() { + line := scanner.Text() + line = strings.TrimSpace(line) + line = strings.TrimFunc(line, func(r rune) bool { + return !unicode.IsGraphic(r) + }) + if line == "" { + continue + } + if seen[line] { + continue + } + strs = append(strs, line) + } + if err := scanner.Err(); err != nil { + f.Close() + return + } + f.Close() + } + strs = append(strs, domain) + f, err := os.OpenFile(file, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.ModePerm) + if err != nil { + return + } + for _, v := range strs { + f.WriteString(v + "\n") + } + f.Close() +} diff --git a/rules/common/geoip.go b/rules/common/geoip.go index b50680a4..254fd7d1 100644 --- a/rules/common/geoip.go +++ b/rules/common/geoip.go @@ -65,7 +65,6 @@ func (g *GEOIP) Match(metadata *C.Metadata) (bool, string) { } return false, g.adapter } - if metadata.DstGeoIP != nil { return false, g.adapter } diff --git a/rules/common/geoip_iso_empty.go b/rules/common/geoip_iso_empty.go new file mode 100644 index 00000000..af808fbe --- /dev/null +++ b/rules/common/geoip_iso_empty.go @@ -0,0 +1,53 @@ +package common + +import ( + "github.com/metacubex/mihomo/component/mmdb" + C "github.com/metacubex/mihomo/constant" +) + +type ISOEmpty struct { + adapter string +} + +func (g *ISOEmpty) RuleType() C.RuleType { + return C.ISOEmpty +} + +func (g *ISOEmpty) Match(metadata *C.Metadata) (bool, string) { + ip := metadata.DstIP + if !ip.IsValid() { + return false, g.adapter + } + if metadata.GeoedIp() { + return false, g.adapter + } + metadata.DstGeoIP = mmdb.IPInstance().LookupCode(ip.AsSlice()) + if metadata.GeoedIp() { + return false, g.adapter + } + return true, g.adapter +} + +func (g *ISOEmpty) Adapter() string { + return g.adapter +} + +func (g *ISOEmpty) Payload() string { + return "ISO_Empty" +} + +func (g *ISOEmpty) ShouldResolveIP() bool { + return true +} + +func (g *ISOEmpty) ShouldFindProcess() bool { + return false +} + +func NewISOEmpty(adapter string) *ISOEmpty { + isoEmpty := &ISOEmpty{ + adapter: adapter, + } + + return isoEmpty +} diff --git a/rules/common/must_resolve.go b/rules/common/must_resolve.go new file mode 100644 index 00000000..9fb3b894 --- /dev/null +++ b/rules/common/must_resolve.go @@ -0,0 +1,42 @@ +package common + +import ( + C "github.com/metacubex/mihomo/constant" +) + +type MustResolve struct { + adapter string +} + +func (f *MustResolve) RuleType() C.RuleType { + return C.MustResolve +} + +func (f *MustResolve) Match(metadata *C.Metadata) (bool, string) { + if metadata.DstIP.IsUnspecified() { + return true, f.adapter + } + return false, f.adapter +} + +func (f *MustResolve) Adapter() string { + return f.adapter +} + +func (f *MustResolve) Payload() string { + return "MustResolve" +} + +func (f *MustResolve) ShouldResolveIP() bool { + return true +} + +func (f *MustResolve) ShouldFindProcess() bool { + return false +} + +func NewMustResolve(adapter string) *MustResolve { + return &MustResolve{ + adapter: adapter, + } +} diff --git a/rules/domain/domain.go b/rules/domain/domain.go new file mode 100644 index 00000000..92c85cb8 --- /dev/null +++ b/rules/domain/domain.go @@ -0,0 +1,64 @@ +package domain + +import ( + "bufio" + "os" + "strings" + "sync" + "unicode" + + "github.com/metacubex/mihomo/log" +) + +type DomainSet struct { + File string + mu sync.RWMutex + Set *Set + Conditions []string + Operator string + Adapter string + Payload string +} + +func (d *DomainSet) Init() { + d.mu.Lock() + defer d.mu.Unlock() + var strs []string + f, err := os.OpenFile(d.File, os.O_RDONLY, os.ModePerm) + if err != nil { + log.Fatalln("%v", err) + } + defer f.Close() + scanner := bufio.NewScanner(f) + var count = 0 + for scanner.Scan() { + line := scanner.Text() + line = strings.TrimSpace(line) + line = strings.TrimFunc(line, func(r rune) bool { + return !unicode.IsGraphic(r) + }) + if line == "" { + continue + } + count++ + strs = append(strs, line) + } + if err := scanner.Err(); err != nil { + log.Fatalln("%v", err) + } + if len(strs) == 0 { + return + } + d.Set = NewSet(strs) + log.Infoln("%s loaded %d line from %s, mem size %f MB", d.Payload, count, d.File, float32(d.Set.Size())/1024/1024) +} + +func (d *DomainSet) HasDomain(domain string) bool { + d.mu.RLock() + defer d.mu.RUnlock() + + if d.Set == nil { + return false + } + return d.Set.Has(domain) +} diff --git a/rules/domain/domain_test.go b/rules/domain/domain_test.go new file mode 100644 index 00000000..660bad6a --- /dev/null +++ b/rules/domain/domain_test.go @@ -0,0 +1,63 @@ +package domain + +import ( + "bufio" + "log" + "os" + "strings" + "testing" + "unicode" +) + +var set *Set + +func init() { + var strs []string + f, err := os.OpenFile("direct.txt", os.O_RDONLY, os.ModePerm) + if err != nil { + log.Fatal(err) + } + defer f.Close() + scanner := bufio.NewScanner(f) + var count = 0 + for scanner.Scan() { + line := scanner.Text() + line = strings.TrimSpace(line) + line = strings.TrimFunc(line, func(r rune) bool { + return !unicode.IsGraphic(r) + }) + if line == "" { + continue + } + count++ + strs = append(strs, line) + } + if err := scanner.Err(); err != nil { + log.Fatalln(err) + } + if len(strs) == 0 { + return + } + set = NewSet(strs) + log.Printf(" %d line, mem size %f MB", count, float32(set.Size())/1024/1024) +} + +func TestSet_Has(t *testing.T) { + var domains []string + domains = append(domains, "u.jd.com") + domains = append(domains, "oogle.com") + domains = append(domains, "google.com") + domains = append(domains, "1google.com") + domains = append(domains, "1.google.com") + domains = append(domains, "1.google.com.1") + domains = append(domains, "com") + domains = append(domains, "rch.google") + domains = append(domains, ".rch.google") + domains = append(domains, "www.youtube.com") + domains = append(domains, "play.google.com") + domains = append(domains, "checkip.synology.com") + + for _, v := range domains { + t.Log(v, set.Has(v)) + } +} diff --git a/rules/domain/sskv.go b/rules/domain/sskv.go new file mode 100644 index 00000000..898a171a --- /dev/null +++ b/rules/domain/sskv.go @@ -0,0 +1,186 @@ +package domain + +import ( + "reflect" + "slices" + "unsafe" + + "github.com/openacid/low/bitmap" +) + +// mod from https://github.com/openacid/succinct + +const prefixLabel = '\r' + +type Set struct { + leaves, labelBitmap []uint64 + labels []byte + ranks, selects []int32 +} + +// NewSet creates a new *Set struct, from a slice of sorted strings. +func NewSet(strs []string) *Set { + + keys := make([]string, 0, len(strs)) + seen := make(map[string]bool, len(strs)) + for _, v := range strs { + if seen[v] { + continue + } + seen[v] = true + keys = append(keys, reverseDomainSuffix(v)) + } + + slices.Sort(keys) + ss := &Set{} + lIdx := 0 + + type qElt struct{ s, e, col int } + + queue := []qElt{{0, len(keys), 0}} + + for i := 0; i < len(queue); i++ { + elt := queue[i] + + if elt.col == len(keys[elt.s]) { + // a leaf node + elt.s++ + setBit(&ss.leaves, i, 1) + } + + for j := elt.s; j < elt.e; { + + frm := j + + for ; j < elt.e && keys[j][elt.col] == keys[frm][elt.col]; j++ { + } + + queue = append(queue, qElt{frm, j, elt.col + 1}) + ss.labels = append(ss.labels, keys[frm][elt.col]) + setBit(&ss.labelBitmap, lIdx, 0) + lIdx++ + } + + setBit(&ss.labelBitmap, lIdx, 1) + lIdx++ + } + + ss.init() + return ss +} + +// Has query for a key and return whether it presents in the Set. +func (ss *Set) Has(key string) bool { + kbs := s2b(key) + klen := len(kbs) + nodeId, bmIdx := 0, 0 + + for i := klen - 1; i >= 0; i-- { + c := kbs[i] + for ; ; bmIdx++ { + //log.Printf("%c", c) + if getBit(ss.labelBitmap, bmIdx) != 0 { + // no more labels in this node + //if c == '.' { + // return true + //} else { + // return false + //} + return false + } + + la := ss.labels[bmIdx-nodeId] + // isPrefix := la == '\r' + // if isPrefix { + // log.Printf("%c '/r' \n", c) + // } else { + // log.Printf("%c '%c'\n", c, la) + // } + + if c == '.' && la == prefixLabel { + return true + } + if la == c { + break + } + } + // log.Printf("%c\n", c) + // go to next level + + nodeId = countZeros(ss.labelBitmap, ss.ranks, bmIdx+1) + bmIdx = selectIthOne(ss.labelBitmap, ss.ranks, ss.selects, nodeId-1) + 1 + } + + return ss.labels[bmIdx-nodeId] == prefixLabel +} + +func setBit(bm *[]uint64, i int, v int) { + for i>>6 >= len(*bm) { + *bm = append(*bm, 0) + } + (*bm)[i>>6] |= uint64(v) << uint(i&63) +} + +func getBit(bm []uint64, i int) uint64 { + return bm[i>>6] & (1 << uint(i&63)) +} + +// init builds pre-calculated cache to speed up rank() and select() +func (ss *Set) init() { + ss.selects, ss.ranks = bitmap.IndexSelect32R64(ss.labelBitmap) +} + +// countZeros counts the number of "0" in a bitmap before the i-th bit(excluding +// the i-th bit) on behalf of rank index. +// E.g.: +// +// countZeros("010010", 4) == 3 +// // 012345 +func countZeros(bm []uint64, ranks []int32, i int) int { + a, _ := bitmap.Rank64(bm, ranks, int32(i)) + return i - int(a) +} + +// selectIthOne returns the index of the i-th "1" in a bitmap, on behalf of rank +// and select indexes. +// E.g.: +// +// selectIthOne("010010", 1) == 4 +// // 012345 +func selectIthOne(bm []uint64, ranks, selects []int32, i int) int { + a, _ := bitmap.Select32R64(bm, selects, ranks, int32(i)) + return int(a) +} + +func reverseDomainSuffix(domain string) string { + l := len(domain) + b := []byte(domain) + for i := 0; i < l/2; i++ { + b[i] = domain[l-i-1] + b[l-i-1] = domain[i] + } + b = append(b, prefixLabel) + return string(b) +} + +func s2b(s string) (b []byte) { + bh := (*reflect.SliceHeader)(unsafe.Pointer(&b)) + sh := (*reflect.StringHeader)(unsafe.Pointer(&s)) + bh.Data = sh.Data + bh.Cap = sh.Len + bh.Len = sh.Len + return b +} + +func (ss *Set) Size() int { + // leaves, labelBitmap []uint64 + // labels []byte + // ranks, selects []int32 + leavesSize := cap(ss.leaves) * 8 + labelBitmapSize := cap(ss.labelBitmap) * 8 + labelsSize := cap(ss.labels) + ranksSize := cap(ss.ranks) * 4 + selectsSize := cap(ss.selects) * 4 + + return leavesSize + labelBitmapSize + labelsSize + ranksSize + selectsSize +} diff --git a/rules/parser.go b/rules/parser.go index 9b1f5520..76cd2ec9 100644 --- a/rules/parser.go +++ b/rules/parser.go @@ -81,6 +81,14 @@ func ParseRule(tp, payload, target string, params []string, subRules map[string] case "MATCH": parsed = RC.NewMatch(target) parseErr = nil + case "FILEDI": + parsed = RC.NewFileDi(payload, target, params[0]) + case "RECORD": + parsed = RC.NewFileDIRecord(payload, target, params) + case "MUSTRESOLVE": + parsed = RC.NewMustResolve(target) + case "ISOEMPTY": + parsed = RC.NewISOEmpty(target) default: parseErr = fmt.Errorf("unsupported rule type %s", tp) } diff --git a/service/service.go b/service/service.go new file mode 100644 index 00000000..ec425e41 --- /dev/null +++ b/service/service.go @@ -0,0 +1,246 @@ +package service + +import ( + "flag" + "fmt" + S "github.com/kardianos/service" + "io/ioutil" + "os" + "os/exec" + "runtime" + "strings" +) + +var Srv S.Service +var Prg *Program +var ServiceName = "clash" + +type Program struct { + Run func() +} + +func (p *Program) Start(s S.Service) error { + // Start should not block. Do the actual work async. + go p.run() + return nil +} + +func (p *Program) run() { + // Do work here + p.Run() +} +func (p *Program) Stop(s S.Service) error { + // Stop should not block. Return with a few seconds. + return nil +} + +func NewService(Run func()) *Program { + svcConfig := &S.Config{ + Name: ServiceName, + DisplayName: ServiceName, + Description: ServiceName, + } + Prg = &Program{Run: Run} + var err error + Srv, err = S.New(Prg, svcConfig) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + if IsOpenWrt() { + svcConfig.Option = S.KeyValue{} + svcConfig.Option["SysvScript"] = openWrtScript + } + fmt.Println("platform", Srv.Platform()) + return Prg +} + +func (p *Program) Action(action string) { + var err error + switch action { + case "install": + err := Srv.Install() + if err != nil { + fmt.Println(err) + return + } + if IsOpenWrt() { + // On OpenWrt it is important to run enable after the service installation + // Otherwise, the service won't start on the system startup + _, err := runInitdCommand("enable") + if err != nil { + fmt.Println(err) + } + return + } + case "uninstall": + if IsOpenWrt() { + // On OpenWrt it is important to run disable command first + // as it will remove the symlink + _, err := runInitdCommand("disable") + if err != nil { + fmt.Println(err) + return + } + } + err = Srv.Uninstall() + if err != nil { + fmt.Println(err) + } + case "status": + status, err := Srv.Status() + if err != nil { + if IsOpenWrt() { + status = 0 + } else { + fmt.Println(err) + return + } + } + switch status { + case 0: + fmt.Printf("unknown\n") + case 1: + fmt.Printf("running\n") + case 2: + fmt.Printf("stopped\n") + } + case "start": + fallthrough + case "stop": + fallthrough + case "restart": + err := S.Control(Srv, action) + if err != nil { + if IsOpenWrt() { + _, err := runInitdCommand(action) + if err != nil { + fmt.Println(1, err) + } + } else { + fmt.Println(2, err) + } + } + default: + flag.PrintDefaults() + } +} + +func (p *Program) RunIt() { + status, _ := Srv.Status() + if status != S.StatusUnknown { + // if installed as service, it always goes here. + err := Srv.Run() + if err != nil { + fmt.Println(err) + } + } else { + // this makes use of running directly otherwise. + p.Run() + } +} + +// IsOpenWrt checks if OS is OpenWRT +func IsOpenWrt() bool { + if runtime.GOOS != "linux" { + return false + } + body, err := ioutil.ReadFile("/etc/os-release") + if err != nil { + return false + } + return strings.Contains(string(body), "OpenWrt") +} + +// runInitdCommand runs init.d service command +// returns command code or error if any +func runInitdCommand(action string) (int, error) { + confPath := "/etc/init.d/" + ServiceName + code, _, err := RunCommand("sh", "-c", confPath+" "+action) + return code, err +} + +// runCommand runs shell command +func RunCommand(command string, arguments ...string) (int, string, error) { + cmd := exec.Command(command, arguments...) + out, err := cmd.Output() + if err != nil { + return 1, "", fmt.Errorf("exec.Command(%s) failed: %v: %s", command, err, string(out)) + } + + return cmd.ProcessState.ExitCode(), string(out), nil +} + +// OpenWrt procd init script +// https://github.com/AdguardTeam/AdGuardHome/issues/1386 +const openWrtScript = `#!/bin/sh /etc/rc.common +DESCRIPTION="{{.Name}}" +cmd="{{.Path}}{{range .Arguments}} {{.|cmd}}{{end}}" +name="{{.Name}}" +pid_file="/var/run/$name.pid" +stdout_log="/var/log/$name.log" +stderr_log="/var/log/$name.err" +START=99 +get_pid() { + cat "$pid_file" +} +is_running() { + [ -f "$pid_file" ] && cat /proc/$(get_pid)/stat > /dev/null 2>&1 +} +start() { + if is_running; then + echo "Already started" + else + echo "Starting $name" + + $cmd >> "$stdout_log" 2>> "$stderr_log" & + echo $! > "$pid_file" + if ! is_running; then + echo "Unable to start, see $stdout_log and $stderr_log" + exit 1 + fi + fi +} +stop() { + if is_running; then + echo -n "Stopping $name.." + kill $(get_pid) + for i in $(seq 1 10) + do + if ! is_running; then + break + fi + echo -n "." + sleep 1 + done + echo + if is_running; then + echo "Not stopped; may still be shutting down or shutdown may have failed" + exit 1 + else + echo "Stopped" + if [ -f "$pid_file" ]; then + rm "$pid_file" + fi + fi + else + echo "Not running" + fi +} +restart() { + stop + if is_running; then + echo "Unable to stop, will not attempt to start" + exit 1 + fi + start +} +status() { + if is_running; then + echo "Running" + else + echo "Stopped" + exit 1 + fi +} +` diff --git a/test/go.mod b/test/go.mod index 92374886..a8338c0b 100644 --- a/test/go.mod +++ b/test/go.mod @@ -1,14 +1,16 @@ module mihomo-test -go 1.20 +go 1.21.0 + +toolchain go1.22.1 require ( github.com/docker/docker v20.10.21+incompatible github.com/docker/go-connections v0.4.0 github.com/metacubex/mihomo v0.0.0 - github.com/miekg/dns v1.1.57 - github.com/stretchr/testify v1.8.4 - golang.org/x/net v0.18.0 + github.com/miekg/dns v1.1.59 + github.com/stretchr/testify v1.9.0 + golang.org/x/net v0.25.0 ) replace github.com/metacubex/mihomo => ../ @@ -16,21 +18,21 @@ replace github.com/metacubex/mihomo => ../ require ( github.com/3andne/restls-client-go v0.1.6 // indirect github.com/Microsoft/go-winio v0.6.0 // indirect - github.com/RyuaNerin/go-krypto v1.0.2 // indirect + github.com/RyuaNerin/go-krypto v1.2.4 // indirect github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 // indirect github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect - github.com/andybalholm/brotli v1.0.5 // indirect + github.com/andybalholm/brotli v1.1.0 // indirect github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/buger/jsonparser v1.1.1 // indirect - github.com/cilium/ebpf v0.12.3 // indirect + github.com/cilium/ebpf v0.15.0 // indirect github.com/coreos/go-iptables v0.7.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/distribution/reference v0.5.0 // indirect - github.com/dlclark/regexp2 v1.10.0 // indirect + github.com/dlclark/regexp2 v1.11.0 // indirect github.com/docker/distribution v2.8.3+incompatible // indirect github.com/docker/go-units v0.4.0 // indirect github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9 // indirect - github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 // indirect + github.com/ericlagergren/polyval v0.0.0-20230805202542-18692a1b76f9 // indirect github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1 // indirect github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect @@ -39,85 +41,85 @@ require ( github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/gobwas/httphead v0.1.0 // indirect github.com/gobwas/pool v0.2.1 // indirect - github.com/gobwas/ws v1.3.1 // indirect - github.com/gofrs/uuid/v5 v5.0.0 // indirect + github.com/gobwas/ws v1.4.0 // indirect + github.com/gofrs/uuid/v5 v5.2.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/google/btree v1.1.2 // indirect github.com/google/go-cmp v0.6.0 // indirect - github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect + github.com/google/pprof v0.0.0-20240509144519-723abb6459b7 // indirect github.com/hashicorp/yamux v0.1.1 // indirect - github.com/insomniacslk/dhcp v0.0.0-20231016090811-6a2c8fbdcc1c // indirect + github.com/insomniacslk/dhcp v0.0.0-20240419123447-f1cffa2c0c49 // indirect github.com/josharian/native v1.1.0 // indirect github.com/jpillora/backoff v1.0.0 // indirect - github.com/klauspost/compress v1.16.7 // indirect - github.com/klauspost/cpuid/v2 v2.2.6 // indirect - github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect + github.com/klauspost/compress v1.17.8 // indirect + github.com/klauspost/cpuid/v2 v2.2.7 // indirect + github.com/lufia/plan9stats v0.0.0-20240513124658-fba389f38bae // indirect github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mdlayher/netlink v1.7.2 // indirect - github.com/mdlayher/socket v0.4.1 // indirect + github.com/mdlayher/socket v0.5.1 // indirect github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 // indirect - github.com/metacubex/gvisor v0.0.0-20231001104248-0f672c3fb8d8 // indirect - github.com/metacubex/quic-go v0.40.1-0.20231130135418-0c1b47cf9394 // indirect - github.com/metacubex/sing-quic v0.0.0-20231130141855-0022295e524b // indirect - github.com/metacubex/sing-shadowsocks v0.2.5 // indirect - github.com/metacubex/sing-shadowsocks2 v0.1.4 // indirect - github.com/metacubex/sing-tun v0.1.15-0.20231103033938-170591e8d5bd // indirect - github.com/metacubex/sing-vmess v0.1.9-0.20230921005247-a0488d7dac74 // indirect - github.com/metacubex/sing-wireguard v0.0.0-20231001110902-321836559170 // indirect + github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec // indirect + github.com/metacubex/quic-go v0.44.1-0.20240521004242-fcd70d587e22 // indirect + github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 // indirect + github.com/metacubex/sing-shadowsocks v0.2.6 // indirect + github.com/metacubex/sing-shadowsocks2 v0.2.0 // indirect + github.com/metacubex/sing-tun v0.2.7-0.20240521155100-e8316a45a414 // indirect + github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f // indirect + github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63 // indirect github.com/moby/term v0.5.0 // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/mroth/weightedrand/v2 v2.1.0 // indirect github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // indirect - github.com/onsi/ginkgo/v2 v2.9.5 // indirect + github.com/onsi/ginkgo/v2 v2.17.3 // indirect github.com/openacid/low v0.1.21 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.2 // indirect github.com/oschwald/maxminddb-golang v1.12.0 // indirect - github.com/pierrec/lz4/v4 v4.1.14 // indirect + github.com/pierrec/lz4/v4 v4.1.21 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect - github.com/puzpuzpuz/xsync/v3 v3.0.2 // indirect + github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect + github.com/puzpuzpuz/xsync/v3 v3.1.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect github.com/quic-go/qtls-go1-20 v0.4.1 // indirect github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a // indirect github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect - github.com/sagernet/sing v0.2.18-0.20231108041402-4fbbd193203c // indirect - github.com/sagernet/sing-mux v0.1.5-0.20231109075101-6b086ed6bb07 // indirect + github.com/sagernet/sing v0.3.8 // indirect + github.com/sagernet/sing-mux v0.2.1-0.20240124034317-9bfb33698bb6 // indirect github.com/sagernet/sing-shadowtls v0.1.4 // indirect - github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 // indirect - github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6 // indirect - github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 // indirect - github.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f // indirect - github.com/samber/lo v1.38.1 // indirect - github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 // indirect - github.com/shirou/gopsutil/v3 v3.23.10 // indirect + github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 // indirect + github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6 // indirect + github.com/sagernet/utls v1.5.4 // indirect + github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e // indirect + github.com/samber/lo v1.39.0 // indirect + github.com/scjalliance/comshim v0.0.0-20231116235529-bbacf79a4691 // indirect + github.com/shirou/gopsutil/v3 v3.24.4 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b // indirect github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c // indirect github.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e // indirect github.com/sirupsen/logrus v1.9.3 // indirect - github.com/tklauser/go-sysconf v0.3.12 // indirect - github.com/tklauser/numcpus v0.6.1 // indirect - github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect - github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect + github.com/tklauser/go-sysconf v0.3.14 // indirect + github.com/tklauser/numcpus v0.8.0 // indirect + github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect + github.com/vishvananda/netns v0.0.4 // indirect github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect - github.com/yusufpapurcu/wmi v1.2.3 // indirect - github.com/zhangyunhao116/fastrand v0.3.0 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect + github.com/zhangyunhao116/fastrand v0.4.0 // indirect gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect - go.uber.org/mock v0.3.0 // indirect - go4.org/netipx v0.0.0-20230824141953-6213f710f925 // indirect - golang.org/x/crypto v0.16.0 // indirect - golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect - golang.org/x/mod v0.14.0 // indirect - golang.org/x/sync v0.5.0 // indirect - golang.org/x/sys v0.15.0 // indirect - golang.org/x/text v0.14.0 // indirect - golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.15.0 // indirect - google.golang.org/protobuf v1.31.0 // indirect + go.uber.org/mock v0.4.0 // indirect + go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect + golang.org/x/crypto v0.23.0 // indirect + golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect + golang.org/x/mod v0.17.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect + golang.org/x/time v0.5.0 // indirect + golang.org/x/tools v0.21.0 // indirect + google.golang.org/protobuf v1.34.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - lukechampine.com/blake3 v1.2.1 // indirect + lukechampine.com/blake3 v1.3.0 // indirect ) diff --git a/test/go.sum b/test/go.sum index af34b356..d8c6b935 100644 --- a/test/go.sum +++ b/test/go.sum @@ -3,14 +3,18 @@ github.com/3andne/restls-client-go v0.1.6/go.mod h1:iEdTZNt9kzPIxjIGSMScUFSBrUH6 github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= +github.com/RyuaNerin/elliptic2 v1.0.0/go.mod h1:wWB8fWrJI/6EPJkyV/r1Rj0hxUgrusmqSj8JN6yNf/A= github.com/RyuaNerin/go-krypto v1.0.2 h1:9KiZrrBs+tDrQ66dNy4nrX6SzntKtSKdm0wKHhdB4WM= github.com/RyuaNerin/go-krypto v1.0.2/go.mod h1:17LzMeJCgzGTkPH3TmfzRnEJ/yA7ErhTPp9sxIqONtA= +github.com/RyuaNerin/go-krypto v1.2.4/go.mod h1:QqCYkoutU3yInyD9INt2PGolVRsc3W4oraQadVGXJ/8= github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 h1:cDVUiFo+npB0ZASqnw4q90ylaVAbnYyx0JYqK4YcGok= github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344/go.mod h1:9pIqrY6SXNL8vjRQE5Hd/OL5GyK/9MrGUWs87z/eFfk= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= @@ -21,6 +25,9 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/cilium/ebpf v0.12.3 h1:8ht6F9MquybnY97at+VDZb3eQQr8ev79RueWeVaEcG4= github.com/cilium/ebpf v0.12.3/go.mod h1:TctK1ivibvI3znr66ljgi4hqOT8EYQjz1KWBfb1UVgM= +github.com/cilium/ebpf v0.13.2/go.mod h1:DHp1WyrLeiBh19Cf/tfiSMhqheEiK8fXFZ4No0P1Hso= +github.com/cilium/ebpf v0.14.0/go.mod h1:DHp1WyrLeiBh19Cf/tfiSMhqheEiK8fXFZ4No0P1Hso= +github.com/cilium/ebpf v0.15.0/go.mod h1:DHp1WyrLeiBh19Cf/tfiSMhqheEiK8fXFZ4No0P1Hso= github.com/coreos/go-iptables v0.7.0 h1:XWM3V+MPRr5/q51NuWSgU0fqMad64Zyxs8ZUoMsamr8= github.com/coreos/go-iptables v0.7.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -30,6 +37,7 @@ github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0= github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v20.10.21+incompatible h1:UTLdBmHk3bEY+w8qeO5KttOhy6OmXWsl/FEet9Uswog= @@ -42,6 +50,7 @@ github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9 h1:/5RkVc9Rc81 github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9/go.mod h1:hkIFzoiIPZYxdFOOLyDho59b7SrDfo+w3h+yWdlg45I= github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 h1:8j2RH289RJplhA6WfdaPqzg1MjH2K8wX5e0uhAxrw2g= github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391/go.mod h1:K2R7GhgxrlJzHw2qiPWsCZXf/kXEJN9PLnQK73Ll0po= +github.com/ericlagergren/polyval v0.0.0-20230805202542-18692a1b76f9/go.mod h1:aXxf//HFNaacVV7/YZ8qevpNZAEoxSCpoBjscNhjrCI= github.com/ericlagergren/saferand v0.0.0-20220206064634-960a4dd2bc5c h1:RUzBDdZ+e/HEe2Nh8lYsduiPAZygUfVXJn0Ncj5sHMg= github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1 h1:tlDMEdcPRQKBEz5nGDMvswiajqh7k8ogWRlhRwKy5mY= github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1/go.mod h1:4RfsapbGx2j/vU5xC/5/9qB3kn9Awp1YDiEnN43QrJ4= @@ -64,8 +73,12 @@ github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.3.1 h1:Qi34dfLMWJbiKaNbDVzM9x27nZBjmkaW6i4+Ku+pGVU= github.com/gobwas/ws v1.3.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= +github.com/gobwas/ws v1.3.2/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= +github.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakrc= github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M= github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= +github.com/gofrs/uuid/v5 v5.1.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= +github.com/gofrs/uuid/v5 v5.2.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -80,12 +93,19 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= +github.com/google/pprof v0.0.0-20240320155624-b11c3daa6f07/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= +github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= +github.com/google/pprof v0.0.0-20240509144519-723abb6459b7/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= github.com/google/tink/go v1.6.1 h1:t7JHqO8Ath2w2ig5vjwQYJzhGEZymedQc90lQXUBa4I= github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/insomniacslk/dhcp v0.0.0-20231016090811-6a2c8fbdcc1c h1:PgxFEySCI41sH0mB7/2XswdXbUykQsRUGod8Rn+NubM= github.com/insomniacslk/dhcp v0.0.0-20231016090811-6a2c8fbdcc1c/go.mod h1:3A9PQ1cunSDF/1rbTq99Ts4pVnycWg+vlPkfeD2NLFI= +github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2/go.mod h1:3A9PQ1cunSDF/1rbTq99Ts4pVnycWg+vlPkfeD2NLFI= +github.com/insomniacslk/dhcp v0.0.0-20240227161007-c728f5dd21c8/go.mod h1:izxuNQZeFrbx2nK2fAyN5iNUB34Fe9j0nK4PwLzAkKw= +github.com/insomniacslk/dhcp v0.0.0-20240419123447-f1cffa2c0c49/go.mod h1:KclMyHxX06VrVr0DJmeFSUb1ankt7xTfoOA35pCkoic= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= @@ -96,12 +116,19 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/lufia/plan9stats v0.0.0-20240226150601-1dcf7310316a/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k= +github.com/lufia/plan9stats v0.0.0-20240408141607-282e7b5d6b74/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k= +github.com/lufia/plan9stats v0.0.0-20240513124658-fba389f38bae/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k= github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc= github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= @@ -110,26 +137,52 @@ github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/ github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= +github.com/mdlayher/socket v0.5.0/go.mod h1:WkcBFfvyG8QENs5+hfQPl1X6Jpd2yeLIYgrGFmJiJxI= +github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ= github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 h1:cjd4biTvOzK9ubNCCkQ+ldc4YSH/rILn53l/xGBFHHI= github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759/go.mod h1:UHOv2xu+RIgLwpXca7TLrXleEd4oR3sPatW6IF8wU88= github.com/metacubex/gvisor v0.0.0-20231001104248-0f672c3fb8d8 h1:npBvaPAT145UY8682AzpUMWpdIxJti/WPLjy7gCiYYs= github.com/metacubex/gvisor v0.0.0-20231001104248-0f672c3fb8d8/go.mod h1:ZR6Gas7P1GcADCVBc1uOrA0bLQqDDyp70+63fD/BE2c= +github.com/metacubex/gvisor v0.0.0-20231209122014-3e43224c7bbc/go.mod h1:rhBU9tD5ktoGPBtXUquhWuGJ4u+8ZZzBMi2cAdv9q8Y= +github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec/go.mod h1:8BVmQ+3cxjqzWElafm24rb2Ae4jRI6vAXNXWqWjfrXw= github.com/metacubex/quic-go v0.40.1-0.20231130135418-0c1b47cf9394 h1:dIT+KB2hknBCrwVAXPeY9tpzzkOZP5m40yqUteRT6/Y= github.com/metacubex/quic-go v0.40.1-0.20231130135418-0c1b47cf9394/go.mod h1:F/t8VnA47xoia8ABlNA4InkZjssvFJ5p6E6jKdbkgAs= +github.com/metacubex/quic-go v0.41.1-0.20240120014142-a02f4a533d4a/go.mod h1:F/t8VnA47xoia8ABlNA4InkZjssvFJ5p6E6jKdbkgAs= +github.com/metacubex/quic-go v0.41.1-0.20240307164142-46c6f7cdf2d1/go.mod h1:F/t8VnA47xoia8ABlNA4InkZjssvFJ5p6E6jKdbkgAs= +github.com/metacubex/quic-go v0.42.1-0.20240319071510-a251e5c66a5c/go.mod h1:iGx3Y1zynls/FjFgykLSqDcM81U0IKePRTXEz5g3iiQ= +github.com/metacubex/quic-go v0.43.1-0.20240428051621-a109abfb4cf6/go.mod h1:uXHODgJFUfUnkkCMWLd5Er6L5QY/LFRZb9LD5jyyhsk= +github.com/metacubex/quic-go v0.43.2-0.20240518033621-2c3d14c6b38e/go.mod h1:uXHODgJFUfUnkkCMWLd5Er6L5QY/LFRZb9LD5jyyhsk= +github.com/metacubex/quic-go v0.44.1-0.20240521004242-fcd70d587e22/go.mod h1:88wAATpevav4xdy5N8oejQ2cbbI6EcLYEklFeo+qywA= github.com/metacubex/sing-quic v0.0.0-20231130141855-0022295e524b h1:7XXoEePvxfkQN9b2wB8UXU3uzb9uL8syEFF7A9VAKKQ= github.com/metacubex/sing-quic v0.0.0-20231130141855-0022295e524b/go.mod h1:Gu5/zqZDd5G1AUtoV2yjAPWOEy7zwbU2DBUjdxJh0Kw= +github.com/metacubex/sing-quic v0.0.0-20240130040922-cbe613c88f20/go.mod h1:bdHqEysJclB9BzIa5jcKKSZ1qua+YEPjR8fOzzE3vZU= +github.com/metacubex/sing-quic v0.0.0-20240310154810-47bca850fc01/go.mod h1:WyY0zYxv+o+18R/Ece+QFontlgXoobKbNqbtYn2zjz8= +github.com/metacubex/sing-quic v0.0.0-20240418004036-814c531c378d/go.mod h1:WyY0zYxv+o+18R/Ece+QFontlgXoobKbNqbtYn2zjz8= +github.com/metacubex/sing-quic v0.0.0-20240429040940-fa3a4ff2533e/go.mod h1:nfqibK+vkBtE6Ch8vYqrFTcaX4BC6TwKqNNl5rORll4= +github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72/go.mod h1:g7Mxj7b7zm7YVqD975mk/hSmrb0A0G4bVvIMr2MMzn8= github.com/metacubex/sing-shadowsocks v0.2.5 h1:O2RRSHlKGEpAVG/OHJQxyHqDy8uvvdCW/oW2TDBOIhc= github.com/metacubex/sing-shadowsocks v0.2.5/go.mod h1:Xz2uW9BEYGEoA8B4XEpoxt7ERHClFCwsMAvWaruoyMo= +github.com/metacubex/sing-shadowsocks v0.2.6/go.mod h1:zIkMeSnb8Mbf4hdqhw0pjzkn1d99YJ3JQm/VBg5WMTg= github.com/metacubex/sing-shadowsocks2 v0.1.4 h1:OOCf8lgsVcpTOJUeaFAMzyKVebaQOBnKirDdUdBoKIE= github.com/metacubex/sing-shadowsocks2 v0.1.4/go.mod h1:Qz028sLfdY3qxGRm9FDI+IM2Ae3ty2wR7HIzD/56h/k= +github.com/metacubex/sing-shadowsocks2 v0.2.0/go.mod h1:LCKF6j1P94zN8ZS+LXRK1gmYTVGB3squivBSXAFnOg8= github.com/metacubex/sing-tun v0.1.15-0.20231103033938-170591e8d5bd h1:k0+92eARqyTAovGhg2AxdsMWHjUsdiGCnR5NuXF3CQY= github.com/metacubex/sing-tun v0.1.15-0.20231103033938-170591e8d5bd/go.mod h1:Q7zmpJ+qOvMMXyUoYlxGQuWkqALUpXzFSSqO+KLPyzA= +github.com/metacubex/sing-tun v0.2.1-0.20240130042529-1f983547e9d4/go.mod h1:P+TjrGTG5AdQRaskP6NiI9gZmgnwR3o5ze9CkIQE+/s= +github.com/metacubex/sing-tun v0.2.6/go.mod h1:4VsMwZH1IlgPGFK1ZbBomZ/B2MYkTgs2+gnBAr5GOIo= +github.com/metacubex/sing-tun v0.2.7-0.20240512075008-89e7c6208eec/go.mod h1:4VsMwZH1IlgPGFK1ZbBomZ/B2MYkTgs2+gnBAr5GOIo= +github.com/metacubex/sing-tun v0.2.7-0.20240521155100-e8316a45a414/go.mod h1:4VsMwZH1IlgPGFK1ZbBomZ/B2MYkTgs2+gnBAr5GOIo= github.com/metacubex/sing-vmess v0.1.9-0.20230921005247-a0488d7dac74 h1:FtupiyFkaVjFvRa7B/uDtRWg5BNsoyPC9MTev3sDasY= github.com/metacubex/sing-vmess v0.1.9-0.20230921005247-a0488d7dac74/go.mod h1:8EWBZpc+qNvf5gmvjAtMHK1/DpcWqzfcBL842K00BsM= +github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY= github.com/metacubex/sing-wireguard v0.0.0-20231001110902-321836559170 h1:DBGA0hmrP4pVIwLiXUONdphjcppED+plmVaKf1oqkwk= github.com/metacubex/sing-wireguard v0.0.0-20231001110902-321836559170/go.mod h1:/VbJfbdLnANE+SKXyMk/96sTRrD4GdFLh5mkegqqFcY= +github.com/metacubex/sing-wireguard v0.0.0-20231209125515-0594297f7232/go.mod h1:NGCrBZ+fUmp81yaA1kVskcNWBnwl5z4UHxz47A01zm8= +github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63/go.mod h1:uY+BYb0UEknLrqvbGcwi9i++KgrKxsurysgI6G1Pveo= github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= +github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY= +github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= @@ -140,6 +193,11 @@ github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 h1:1102pQc2 github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7/go.mod h1:UqoUn6cHESlliMhOnKLWr+CBH+e3bazUPvFj1XZwAjs= github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= +github.com/onsi/ginkgo/v2 v2.16.0/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs= +github.com/onsi/ginkgo/v2 v2.17.0/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs= +github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs= +github.com/onsi/ginkgo/v2 v2.17.2/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc= +github.com/onsi/ginkgo/v2 v2.17.3/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc= github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= github.com/openacid/errors v0.8.1/go.mod h1:GUQEJJOJE3W9skHm8E8Y4phdl2LLEN8iD7c5gcGgdx0= github.com/openacid/low v0.1.21 h1:Tr2GNu4N/+rGRYdOsEHOE89cxUIaDViZbVmKz29uKGo= @@ -154,14 +212,17 @@ github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq5 github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY= github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE= github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/puzpuzpuz/xsync/v3 v3.0.2 h1:3yESHrRFYr6xzkz61LLkvNiPFXxJEAABanTQpKbAaew= github.com/puzpuzpuz/xsync/v3 v3.0.2/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= +github.com/puzpuzpuz/xsync/v3 v3.1.0/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs= @@ -177,24 +238,40 @@ github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2 github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk= github.com/sagernet/sing v0.2.18-0.20231108041402-4fbbd193203c h1:uask61Pxc3nGqsOSjqnBKrwfODWRoEa80lXm04LNk0E= github.com/sagernet/sing v0.2.18-0.20231108041402-4fbbd193203c/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo= +github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo= +github.com/sagernet/sing v0.3.0/go.mod h1:9pfuAH6mZfgnz/YjP6xu5sxx882rfyjpcrTdUpd6w3g= +github.com/sagernet/sing v0.3.5/go.mod h1:+60H3Cm91RnL9dpVGWDPHt0zTQImO9Vfqt9a4rSambI= +github.com/sagernet/sing v0.3.6/go.mod h1:+60H3Cm91RnL9dpVGWDPHt0zTQImO9Vfqt9a4rSambI= +github.com/sagernet/sing v0.3.8/go.mod h1:+60H3Cm91RnL9dpVGWDPHt0zTQImO9Vfqt9a4rSambI= github.com/sagernet/sing-mux v0.1.5-0.20231109075101-6b086ed6bb07 h1:ncKb5tVOsCQgCsv6UpsA0jinbNb5OQ5GMPJlyQP3EHM= github.com/sagernet/sing-mux v0.1.5-0.20231109075101-6b086ed6bb07/go.mod h1:u/MZf32xPG8jEKe3t+xUV67EBnKtDtCaPhsJQOQGUYU= +github.com/sagernet/sing-mux v0.2.1-0.20240124034317-9bfb33698bb6/go.mod h1:khzr9AOPocLa+g53dBplwNDz4gdsyx/YM3swtAhlkHQ= github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k= github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4= github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as= github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37/go.mod h1:3skNSftZDJWTGVtVaM2jfbce8qHnmH/AGDRe62iNOg0= +github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo= github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6 h1:Px+hN4Vzgx+iCGVnWH5A8eR7JhNnIV3rGQmBxA7cw6Q= github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6/go.mod h1:zovq6vTvEM6ECiqE3Eeb9rpIylPpamPcmrJ9tv0Bt0M= +github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6/go.mod h1:73xRZuxwkFk4aiLw28hG8W6o9cr2UPrGL9pdY2UTbvY= github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 h1:kDUqhc9Vsk5HJuhfIATJ8oQwBmpOZJuozQG7Vk88lL4= github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2/go.mod h1:JKQMZq/O2qnZjdrt+B57olmfgEmLtY9iiSIEYtWvoSM= +github.com/sagernet/utls v1.5.4/go.mod h1:CTGxPWExIloRipK3XFpYv0OVyhO8kk3XCGW/ieyTh1s= github.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f h1:Kvo8w8Y9lzFGB/7z09MJ3TR99TFtfI/IuY87Ygcycho= github.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f/go.mod h1:mySs0abhpc/gLlvhoq7HP1RzOaRmIXVeZGCh++zoApk= +github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e/go.mod h1:YbL4TKHRR6APYQv3U2RGfwLDpPYSyWz6oUlpISBEzBE= github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= +github.com/samber/lo v1.39.0/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 h1:rc/CcqLH3lh8n+csdOuDfP+NuykE0U6AeYSJJHKDgSg= github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9/go.mod h1:a/83NAfUXvEuLpmxDssAXxgUgrEy12MId3Wd7OTs76s= +github.com/scjalliance/comshim v0.0.0-20231116235529-bbacf79a4691/go.mod h1:frmTThEHn5H+hHqLPGBDKVlFLpE8f/4vY2M9od2tW9k= github.com/shirou/gopsutil/v3 v3.23.10 h1:/N42opWlYzegYaVkWejXWJpbzKv2JDy3mrgGzKsh9hM= github.com/shirou/gopsutil/v3 v3.23.10/go.mod h1:JIE26kpucQi+innVlAUnIEOSBhBUkirr5b44yr55+WE= +github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= +github.com/shirou/gopsutil/v3 v3.24.2/go.mod h1:tSg/594BcA+8UdQU2XcW803GWYgdtauFFPgJCJKZlVk= +github.com/shirou/gopsutil/v3 v3.24.3/go.mod h1:JpND7O217xa72ewWz9zN2eIIkPWsDN/3pl0H8Qt0uwg= +github.com/shirou/gopsutil/v3 v3.24.4/go.mod h1:lTd2mdiOspcqLgAnr9/nGi71NkeMpWKdmhuxm9GusH8= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= @@ -210,6 +287,7 @@ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVs github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -217,55 +295,84 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0= +github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/tklauser/numcpus v0.7.0/go.mod h1:bb6dMVcj8A42tSE7i32fsIUCbQNllK5iDguyOZRUzAY= +github.com/tklauser/numcpus v0.8.0/go.mod h1:ZJZlAY+dmR4eut8epnzf0u/VwodKmryxR8txiloSqBE= github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA= github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264= +github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701/go.mod h1:P3a5rG4X7tI17Nn3aOIAYr5HbIMukwXG0urG0WuL8OA= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg= github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= +github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= github.com/zhangyunhao116/fastrand v0.3.0 h1:7bwe124xcckPulX6fxtr2lFdO2KQqaefdtbk+mqO/Ig= github.com/zhangyunhao116/fastrand v0.3.0/go.mod h1:0v5KgHho0VE6HU192HnY15de/oDS8UrbBChIFjIhBtc= +github.com/zhangyunhao116/fastrand v0.4.0/go.mod h1:vIyo6EyBhjGKpZv6qVlkPl4JVAklpMM4DSKzbAkMguA= gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec h1:FpfFs4EhNehiVfzQttTuxanPIT43FtkkCFypIod8LHo= gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec/go.mod h1:BZ1RAoRPbCxum9Grlv5aeksu2H8BiKehBYooU2LFiOQ= go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo= go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= +go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= go4.org/netipx v0.0.0-20230824141953-6213f710f925 h1:eeQDDVKFkx0g4Hyy8pHgmZaK0EqB4SD6rvKbUdN3ziQ= go4.org/netipx v0.0.0-20230824141953-6213f710f925/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= +go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= +golang.org/x/exp v0.0.0-20240110193028-0dcbfd608b1e/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= +golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= +golang.org/x/exp v0.0.0-20240318143956-a85f2c67cd81/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ= +golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ= +golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -284,14 +391,22 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -299,6 +414,10 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8= golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= +golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= +golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= +golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= +golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -306,6 +425,9 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= @@ -314,3 +436,5 @@ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI= lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= +lukechampine.com/blake3 v1.2.2/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= +lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= diff --git a/tunnel/statistic/manager.go b/tunnel/statistic/manager.go index 08747118..ce90fbba 100644 --- a/tunnel/statistic/manager.go +++ b/tunnel/statistic/manager.go @@ -1,30 +1,36 @@ package statistic import ( + "encoding/json" + "github.com/shirou/gopsutil/v3/process" "os" + "sync" "time" "github.com/metacubex/mihomo/common/atomic" - + "github.com/metacubex/mihomo/log" "github.com/puzpuzpuz/xsync/v3" - "github.com/shirou/gopsutil/v3/process" ) -var DefaultManager *Manager +var Processor *process.Process +var ChannelManager map[string]*Manager +var ChannelMutex sync.Mutex func init() { - DefaultManager = &Manager{ - connections: xsync.NewMapOf[string, Tracker](), - uploadTemp: atomic.NewInt64(0), - downloadTemp: atomic.NewInt64(0), - uploadBlip: atomic.NewInt64(0), - downloadBlip: atomic.NewInt64(0), - uploadTotal: atomic.NewInt64(0), - downloadTotal: atomic.NewInt64(0), - process: &process.Process{Pid: int32(os.Getpid())}, - } + ChannelManager = make(map[string]*Manager) - go DefaultManager.handle() + Processor = &process.Process{Pid: int32(os.Getpid())} + + go func() { + ticker := time.NewTicker(time.Second) + for range ticker.C { + ChannelMutex.Lock() + for _, v := range ChannelManager { + v.handle() + } + ChannelMutex.Unlock() + } + }() } type Manager struct { @@ -35,8 +41,20 @@ type Manager struct { downloadBlip atomic.Int64 uploadTotal atomic.Int64 downloadTotal atomic.Int64 - process *process.Process - memory uint64 +} + +func NewManager(channelname string) *Manager { + manager := &Manager{ + connections: xsync.NewMapOf[string, Tracker](), + uploadTemp: atomic.NewInt64(0), + downloadTemp: atomic.NewInt64(0), + uploadBlip: atomic.NewInt64(0), + downloadBlip: atomic.NewInt64(0), + uploadTotal: atomic.NewInt64(0), + downloadTotal: atomic.NewInt64(0), + } + ChannelManager[channelname] = manager + return manager } func (m *Manager) Join(c Tracker) { @@ -74,13 +92,8 @@ func (m *Manager) Now() (up int64, down int64) { return m.uploadBlip.Load(), m.downloadBlip.Load() } -func (m *Manager) Memory() uint64 { - m.updateMemory() - return m.memory -} - func (m *Manager) Snapshot() *Snapshot { - var connections []*TrackerInfo + var connections []*TrackerInfo = make([]*TrackerInfo, 0) m.Range(func(c Tracker) bool { connections = append(connections, c.Info()) return true @@ -89,18 +102,9 @@ func (m *Manager) Snapshot() *Snapshot { UploadTotal: m.uploadTotal.Load(), DownloadTotal: m.downloadTotal.Load(), Connections: connections, - Memory: m.memory, } } -func (m *Manager) updateMemory() { - stat, err := m.process.MemoryInfo() - if err != nil { - return - } - m.memory = stat.RSS -} - func (m *Manager) ResetStatistic() { m.uploadTemp.Store(0) m.uploadBlip.Store(0) @@ -111,14 +115,10 @@ func (m *Manager) ResetStatistic() { } func (m *Manager) handle() { - ticker := time.NewTicker(time.Second) - - for range ticker.C { - m.uploadBlip.Store(m.uploadTemp.Load()) - m.uploadTemp.Store(0) - m.downloadBlip.Store(m.downloadTemp.Load()) - m.downloadTemp.Store(0) - } + m.uploadBlip.Store(m.uploadTemp.Load()) + m.uploadTemp.Store(0) + m.downloadBlip.Store(m.downloadTemp.Load()) + m.downloadTemp.Store(0) } type Snapshot struct { @@ -127,3 +127,45 @@ type Snapshot struct { Connections []*TrackerInfo `json:"connections"` Memory uint64 `json:"memory"` } + +func Snapshots() map[string]*Snapshot { + var snapshots map[string]*Snapshot = make(map[string]*Snapshot) + for k, v := range ChannelManager { + snapshots[k] = v.Snapshot() + } + return snapshots +} + +func SaveChannelsData(filename string) { + snapshots := Snapshots() + for _, v := range snapshots { + v.Connections = nil + } + + if bytes, err := json.Marshal(snapshots); err == nil { + err = os.WriteFile(filename, bytes, 0666) + if err != nil { + log.Errorln(err.Error()) + } + + } +} +func RestoreChannelsData(filename string) { + bytes, err := os.ReadFile(filename) + if err != nil { + return + } + var snapshots = map[string]Snapshot{} + err = json.Unmarshal(bytes, &snapshots) + if err != nil { + log.Errorln(err.Error()) + return + } + ChannelMutex.Lock() + defer ChannelMutex.Unlock() + for k, v := range snapshots { + manager := NewManager(k) + manager.downloadTotal.Add(v.DownloadTotal) + manager.uploadTotal.Add(v.UploadTotal) + } +} diff --git a/tunnel/statistic/tracker.go b/tunnel/statistic/tracker.go index 0bf7995d..1253cb03 100644 --- a/tunnel/statistic/tracker.go +++ b/tunnel/statistic/tracker.go @@ -131,7 +131,8 @@ func parseRemoteDestination(addr net.Addr, conn C.Connection) string { } } -func NewTCPTracker(conn C.Conn, manager *Manager, metadata *C.Metadata, rule C.Rule, uploadTotal int64, downloadTotal int64, pushToManager bool) *tcpTracker { +func NewTCPTracker(conn C.Conn, manager *Manager, metadata *C.Metadata, rule C.Rule, + uploadTotal int64, downloadTotal int64, pushToManager bool, ruleadapter string) *tcpTracker { if conn != nil { metadata.RemoteDst = parseRemoteDestination(conn.RemoteAddr(), conn) } @@ -162,7 +163,8 @@ func NewTCPTracker(conn C.Conn, manager *Manager, metadata *C.Metadata, rule C.R if rule != nil { t.TrackerInfo.Rule = rule.RuleType().String() - t.TrackerInfo.RulePayload = rule.Payload() + //t.TrackerInfo.RulePayload = rule.Payload() + t.TrackerInfo.RulePayload = ruleadapter } manager.Join(t) diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 608ab2c5..5a8a600a 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "github.com/metacubex/mihomo/component/loopback" "net" "net/netip" "path/filepath" @@ -12,7 +13,6 @@ import ( "time" N "github.com/metacubex/mihomo/common/net" - "github.com/metacubex/mihomo/component/loopback" "github.com/metacubex/mihomo/component/nat" P "github.com/metacubex/mihomo/component/process" "github.com/metacubex/mihomo/component/resolver" @@ -402,7 +402,18 @@ func handleUDPConn(packet C.PacketAdapter) { return } - pc := statistic.NewUDPTracker(rawPc, statistic.DefaultManager, metadata, rule, 0, 0, true) + channel := rawPc.Chains()[0] + manager, ok := statistic.ChannelManager[channel] + if !ok { + statistic.ChannelMutex.Lock() + manager, ok = statistic.ChannelManager[channel] + if !ok { + manager = statistic.NewManager(channel) + } + statistic.ChannelMutex.Unlock() + } + + pc := statistic.NewUDPTracker(rawPc, manager, metadata, rule, 0, 0, true) switch true { case metadata.SpecialProxy != "": @@ -555,7 +566,24 @@ func handleTCPConn(connCtx C.ConnContext) { return } - remoteConn = statistic.NewTCPTracker(remoteConn, statistic.DefaultManager, metadata, rule, 0, int64(peekLen), true) + channel := remoteConn.Chains()[0] + manager, ok := statistic.ChannelManager[channel] + if !ok { + statistic.ChannelMutex.Lock() + manager, ok = statistic.ChannelManager[channel] + if !ok { + manager = statistic.NewManager(channel) + } + statistic.ChannelMutex.Unlock() + } + + rulePayload := metadata.HitRule + if rule != nil && rulePayload == "" { + rulePayload = rule.Payload() + } + remoteConn = statistic.NewTCPTracker(remoteConn, manager, metadata, rule, + 0, int64(peekLen), true, rulePayload) + defer func(remoteConn C.Conn) { _ = remoteConn.Close() }(remoteConn) @@ -564,11 +592,23 @@ func handleTCPConn(connCtx C.ConnContext) { case metadata.SpecialProxy != "": log.Infoln("[TCP] %s --> %s using %s", metadata.SourceDetail(), metadata.RemoteAddress(), metadata.SpecialProxy) case rule != nil: - if rule.Payload() != "" { - log.Infoln("[TCP] %s --> %s match %s using %s", metadata.SourceDetail(), metadata.RemoteAddress(), fmt.Sprintf("%s(%s)", rule.RuleType().String(), rule.Payload()), remoteConn.Chains().String()) - } else { - log.Infoln("[TCP] %s --> %s match %s using %s", metadata.SourceDetail(), metadata.RemoteAddress(), rule.RuleType().String(), remoteConn.Chains().String()) - } + //if rule.Payload() != "" { + // log.Infoln("[TCP] %s --> %s match %s using %s", + // metadata.SourceDetail(), metadata.RemoteAddress(), fmt.Sprintf("%s(%s)", + // rule.RuleType().String(), rule.Payload()), remoteConn.Chains().String()) + //} else { + // log.Infoln("[TCP] %s --> %s match %s using %s", metadata.SourceDetail(), + // metadata.RemoteAddress(), rule.RuleType().String(), remoteConn.Chains().String()) + //} + log.Infoln( + "%6s(%7s) | %12s | %s -> %s %s", + rule.RuleType().String(), + rulePayload, + remoteConn.Chains().String(), + metadata.SourceDetail(), + metadata.Type, + metadata.RemoteAddress(), + ) case mode == Global: log.Infoln("[TCP] %s --> %s using GLOBAL", metadata.SourceDetail(), metadata.RemoteAddress()) case mode == Direct: