mirror of
https://github.com/MetaCubeX/Clash.Meta.git
synced 2025-04-18 08:20:53 +00:00
update
This commit is contained in:
parent
0b6ae6ffb8
commit
1cf4f04df9
56 changed files with 3343 additions and 397 deletions
114
adapter/inbound/myinbound.go
Normal file
114
adapter/inbound/myinbound.go
Normal file
|
@ -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())
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
8
compile/build_linux_amd64.bat
Normal file
8
compile/build_linux_amd64.bat
Normal file
|
@ -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" ..
|
8
compile/build_linux_arm.bat
Normal file
8
compile/build_linux_arm.bat
Normal file
|
@ -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" ..
|
8
compile/build_linux_arm64.bat
Normal file
8
compile/build_linux_arm64.bat
Normal file
|
@ -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" ..
|
9
compile/build_windows.bat
Normal file
9
compile/build_windows.bat
Normal file
|
@ -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" ..
|
|
@ -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":
|
||||
|
|
23
component/dialer/reject_conn.go
Normal file
23
component/dialer/reject_conn.go
Normal file
|
@ -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 }
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
90
config/update_geo.go
Normal file
90
config/update_geo.go
Normal file
|
@ -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
|
||||
}
|
145
config/update_ui.go
Normal file
145
config/update_ui.go
Normal file
|
@ -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
|
||||
})
|
||||
}
|
|
@ -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, " "))
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
156
docs/config.yaml
156
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
|
||||
|
|
10
go.mod
10
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
|
||||
|
|
35
go.sum
35
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=
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 != "" {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)))
|
||||
|
|
67
hub/updater/limitedreader.go
Normal file
67
hub/updater/limitedreader.go
Normal file
|
@ -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
|
||||
}
|
496
hub/updater/updater.go
Normal file
496
hub/updater/updater.go
Normal file
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
96
listener/mixed/conn_wrap.go
Normal file
96
listener/mixed/conn_wrap.go
Normal file
|
@ -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
|
||||
}
|
||||
}
|
202
listener/mixed/handle_tls.go
Normal file
202
listener/mixed/handle_tls.go
Normal file
|
@ -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)
|
||||
}
|
210
listener/mixed/sshd.go
Normal file
210
listener/mixed/sshd.go
Normal file
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
40
listener/tls_listener.go
Normal file
40
listener/tls_listener.go
Normal file
|
@ -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())
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
45
log/log.go
45
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
|
||||
|
|
87
main.go
87
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()
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
63
rules/common/fileDi.go
Normal file
63
rules/common/fileDi.go
Normal file
|
@ -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,
|
||||
}
|
||||
}
|
227
rules/common/fileDiRecord.go
Normal file
227
rules/common/fileDiRecord.go
Normal file
|
@ -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()
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
53
rules/common/geoip_iso_empty.go
Normal file
53
rules/common/geoip_iso_empty.go
Normal file
|
@ -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
|
||||
}
|
42
rules/common/must_resolve.go
Normal file
42
rules/common/must_resolve.go
Normal file
|
@ -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,
|
||||
}
|
||||
}
|
64
rules/domain/domain.go
Normal file
64
rules/domain/domain.go
Normal file
|
@ -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)
|
||||
}
|
63
rules/domain/domain_test.go
Normal file
63
rules/domain/domain_test.go
Normal file
|
@ -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))
|
||||
}
|
||||
}
|
186
rules/domain/sskv.go
Normal file
186
rules/domain/sskv.go
Normal file
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
246
service/service.go
Normal file
246
service/service.go
Normal file
|
@ -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
|
||||
}
|
||||
`
|
114
test/go.mod
114
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
|
||||
)
|
||||
|
|
124
test/go.sum
124
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=
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Add table
Reference in a new issue