diff --git a/.github/update.log b/.github/update.log index b1b05cf8ae..eb50e3e587 100644 --- a/.github/update.log +++ b/.github/update.log @@ -1201,3 +1201,4 @@ Update On Sun Nov 30 19:38:16 CET 2025 Update On Mon Dec 1 19:44:38 CET 2025 Update On Tue Dec 2 19:43:40 CET 2025 Update On Wed Dec 3 19:42:45 CET 2025 +Update On Thu Dec 4 19:44:01 CET 2025 diff --git a/clash-meta/adapter/adapter.go b/clash-meta/adapter/adapter.go index ef8d4ee347..815064568b 100644 --- a/clash-meta/adapter/adapter.go +++ b/clash-meta/adapter/adapter.go @@ -153,8 +153,9 @@ func (p *Proxy) MarshalJSON() ([]byte, error) { mapping["mptcp"] = proxyInfo.MPTCP mapping["smux"] = proxyInfo.SMUX mapping["interface"] = proxyInfo.Interface - mapping["dialer-proxy"] = proxyInfo.DialerProxy mapping["routing-mark"] = proxyInfo.RoutingMark + mapping["provider-name"] = proxyInfo.ProviderName + mapping["dialer-proxy"] = proxyInfo.DialerProxy return json.Marshal(mapping) } @@ -177,14 +178,12 @@ func (p *Proxy) URLTest(ctx context.Context, url string, expectedStatus utils.In p.history.Pop() } - state, ok := p.extra.Load(url) - if !ok { - state = &internalProxyState{ + state, _ := p.extra.LoadOrStoreFn(url, func() *internalProxyState { + return &internalProxyState{ history: queue.New[C.DelayHistory](defaultHistoriesNum), alive: atomic.NewBool(true), } - p.extra.Store(url, state) - } + }) if !satisfied { record.Delay = 0 diff --git a/clash-meta/adapter/outbound/anytls.go b/clash-meta/adapter/outbound/anytls.go index 5ba7c6d265..c17f27d55e 100644 --- a/clash-meta/adapter/outbound/anytls.go +++ b/clash-meta/adapter/outbound/anytls.go @@ -6,7 +6,7 @@ import ( "strconv" "time" - CN "github.com/metacubex/mihomo/common/net" + N "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/component/proxydialer" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/transport/anytls" @@ -63,7 +63,7 @@ func (t *AnyTLS) ListenPacketContext(ctx context.Context, metadata *C.Metadata) // create uot on tcp destination := M.SocksaddrFromNet(metadata.UDPAddr()) - return newPacketConn(CN.NewThreadSafePacketConn(uot.NewLazyConn(c, uot.Request{Destination: destination})), t), nil + return newPacketConn(N.NewThreadSafePacketConn(uot.NewLazyConn(c, uot.Request{Destination: destination})), t), nil } // SupportUOT implements C.ProxyAdapter @@ -90,12 +90,13 @@ func NewAnyTLS(option AnyTLSOption) (*AnyTLS, error) { name: option.Name, addr: addr, tp: C.AnyTLS, + pdName: option.ProviderName, udp: option.UDP, tfo: option.TFO, mpTcp: option.MPTCP, iface: option.Interface, rmark: option.RoutingMark, - prefer: C.NewDNSPrefer(option.IPVersion), + prefer: option.IPVersion, }, option: &option, } diff --git a/clash-meta/adapter/outbound/base.go b/clash-meta/adapter/outbound/base.go index a3408f70fb..4a4c412d05 100644 --- a/clash-meta/adapter/outbound/base.go +++ b/clash-meta/adapter/outbound/base.go @@ -27,16 +27,17 @@ type ProxyAdapter interface { type Base struct { name string addr string - iface string tp C.AdapterType + pdName string udp bool xudp bool tfo bool mpTcp bool + iface string rmark int - id string prefer C.DNSPrefer dialer C.Dialer + id string } // Name implements C.ProxyAdapter @@ -85,6 +86,7 @@ func (b *Base) ProxyInfo() (info C.ProxyInfo) { info.SMUX = false info.Interface = b.iface info.RoutingMark = b.rmark + info.ProviderName = b.pdName return } @@ -160,14 +162,18 @@ func (b *Base) Close() error { } type BasicOption struct { - TFO bool `proxy:"tfo,omitempty"` - MPTCP bool `proxy:"mptcp,omitempty"` - Interface string `proxy:"interface-name,omitempty"` - RoutingMark int `proxy:"routing-mark,omitempty"` - IPVersion string `proxy:"ip-version,omitempty"` - DialerProxy string `proxy:"dialer-proxy,omitempty"` // don't apply this option into groups, but can set a group name in a proxy + TFO bool `proxy:"tfo,omitempty"` + MPTCP bool `proxy:"mptcp,omitempty"` + Interface string `proxy:"interface-name,omitempty"` + RoutingMark int `proxy:"routing-mark,omitempty"` + IPVersion C.DNSPrefer `proxy:"ip-version,omitempty"` + DialerProxy string `proxy:"dialer-proxy,omitempty"` // don't apply this option into groups, but can set a group name in a proxy + // + // The following parameters are used internally, assign value by the structure decoder are disallowed + // DialerForAPI C.Dialer `proxy:"-"` // the dialer used for API usage has higher priority than all the above configurations. + ProviderName string `proxy:"-"` } func (b *BasicOption) NewDialer(opts []dialer.Option) C.Dialer { @@ -213,6 +219,7 @@ func NewBase(opt BaseOption) *Base { type conn struct { N.ExtendedConn chain C.Chain + pdChain C.Chain adapterAddr string } @@ -234,9 +241,15 @@ func (c *conn) Chains() C.Chain { return c.chain } +// ProviderChains implements C.Connection +func (c *conn) ProviderChains() C.Chain { + return c.pdChain +} + // AppendToChains implements C.Connection func (c *conn) AppendToChains(a C.ProxyAdapter) { c.chain = append(c.chain, a.Name()) + c.pdChain = append(c.pdChain, a.ProxyInfo().ProviderName) } func (c *conn) Upstream() any { @@ -259,7 +272,7 @@ func NewConn(c net.Conn, a C.ProxyAdapter) C.Conn { if _, ok := c.(syscall.Conn); !ok { // exclusion system conn like *net.TCPConn c = N.NewDeadlineConn(c) // most conn from outbound can't handle readDeadline correctly } - cc := &conn{N.NewExtendedConn(c), nil, a.Addr()} + cc := &conn{N.NewExtendedConn(c), nil, nil, a.Addr()} cc.AppendToChains(a) return cc } @@ -267,6 +280,7 @@ func NewConn(c net.Conn, a C.ProxyAdapter) C.Conn { type packetConn struct { N.EnhancePacketConn chain C.Chain + pdChain C.Chain adapterName string connID string adapterAddr string @@ -287,9 +301,15 @@ func (c *packetConn) Chains() C.Chain { return c.chain } +// ProviderChains implements C.Connection +func (c *packetConn) ProviderChains() C.Chain { + return c.pdChain +} + // AppendToChains implements C.Connection func (c *packetConn) AppendToChains(a C.ProxyAdapter) { c.chain = append(c.chain, a.Name()) + c.pdChain = append(c.pdChain, a.ProxyInfo().ProviderName) } func (c *packetConn) LocalAddr() net.Addr { @@ -318,7 +338,7 @@ func newPacketConn(pc net.PacketConn, a ProxyAdapter) C.PacketConn { if _, ok := pc.(syscall.Conn); !ok { // exclusion system conn like *net.UDPConn epc = N.NewDeadlineEnhancePacketConn(epc) // most conn from outbound can't handle readDeadline correctly } - cpc := &packetConn{epc, nil, a.Name(), utils.NewUUIDV4().String(), a.Addr(), a.ResolveUDP} + cpc := &packetConn{epc, nil, nil, a.Name(), utils.NewUUIDV4().String(), a.Addr(), a.ResolveUDP} cpc.AppendToChains(a) return cpc } diff --git a/clash-meta/adapter/outbound/direct.go b/clash-meta/adapter/outbound/direct.go index be8367ba8f..42cd8def13 100644 --- a/clash-meta/adapter/outbound/direct.go +++ b/clash-meta/adapter/outbound/direct.go @@ -69,12 +69,13 @@ func NewDirectWithOption(option DirectOption) *Direct { Base: &Base{ name: option.Name, tp: C.Direct, + pdName: option.ProviderName, udp: true, tfo: option.TFO, mpTcp: option.MPTCP, iface: option.Interface, rmark: option.RoutingMark, - prefer: C.NewDNSPrefer(option.IPVersion), + prefer: option.IPVersion, }, loopBack: loopback.NewDetector(), } diff --git a/clash-meta/adapter/outbound/dns.go b/clash-meta/adapter/outbound/dns.go index 2522850247..5e253d2a01 100644 --- a/clash-meta/adapter/outbound/dns.go +++ b/clash-meta/adapter/outbound/dns.go @@ -158,12 +158,13 @@ func NewDnsWithOption(option DnsOption) *Dns { Base: &Base{ name: option.Name, tp: C.Dns, + pdName: option.ProviderName, udp: true, tfo: option.TFO, mpTcp: option.MPTCP, iface: option.Interface, rmark: option.RoutingMark, - prefer: C.NewDNSPrefer(option.IPVersion), + prefer: option.IPVersion, }, } } diff --git a/clash-meta/adapter/outbound/http.go b/clash-meta/adapter/outbound/http.go index 61b8596ad5..9707bf13c2 100644 --- a/clash-meta/adapter/outbound/http.go +++ b/clash-meta/adapter/outbound/http.go @@ -170,11 +170,12 @@ func NewHttp(option HttpOption) (*Http, error) { name: option.Name, addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)), tp: C.Http, + pdName: option.ProviderName, tfo: option.TFO, mpTcp: option.MPTCP, iface: option.Interface, rmark: option.RoutingMark, - prefer: C.NewDNSPrefer(option.IPVersion), + prefer: option.IPVersion, }, user: option.UserName, pass: option.Password, diff --git a/clash-meta/adapter/outbound/hysteria.go b/clash-meta/adapter/outbound/hysteria.go index 4673779861..67c9af0b6e 100644 --- a/clash-meta/adapter/outbound/hysteria.go +++ b/clash-meta/adapter/outbound/hysteria.go @@ -243,11 +243,12 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) { name: option.Name, addr: addr, tp: C.Hysteria, + pdName: option.ProviderName, udp: true, tfo: option.FastOpen, iface: option.Interface, rmark: option.RoutingMark, - prefer: C.NewDNSPrefer(option.IPVersion), + prefer: option.IPVersion, }, option: &option, client: client, diff --git a/clash-meta/adapter/outbound/hysteria2.go b/clash-meta/adapter/outbound/hysteria2.go index 78d2b9ff95..2982276938 100644 --- a/clash-meta/adapter/outbound/hysteria2.go +++ b/clash-meta/adapter/outbound/hysteria2.go @@ -9,7 +9,7 @@ import ( "strconv" "time" - CN "github.com/metacubex/mihomo/common/net" + N "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/component/ca" "github.com/metacubex/mihomo/component/proxydialer" @@ -85,7 +85,7 @@ func (h *Hysteria2) ListenPacketContext(ctx context.Context, metadata *C.Metadat if pc == nil { return nil, errors.New("packetConn is nil") } - return newPacketConn(CN.NewThreadSafePacketConn(pc), h), nil + return newPacketConn(N.NewThreadSafePacketConn(pc), h), nil } // Close implements C.ProxyAdapter @@ -110,10 +110,11 @@ func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) { name: option.Name, addr: addr, tp: C.Hysteria2, + pdName: option.ProviderName, udp: true, iface: option.Interface, rmark: option.RoutingMark, - prefer: C.NewDNSPrefer(option.IPVersion), + prefer: option.IPVersion, }, option: &option, } @@ -189,7 +190,7 @@ func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) { CWND: option.CWND, UdpMTU: option.UdpMTU, ServerAddress: func(ctx context.Context) (*net.UDPAddr, error) { - udpAddr, err := resolveUDPAddr(ctx, "udp", addr, C.NewDNSPrefer(option.IPVersion)) + udpAddr, err := resolveUDPAddr(ctx, "udp", addr, option.IPVersion) if err != nil { return nil, err } diff --git a/clash-meta/adapter/outbound/mieru.go b/clash-meta/adapter/outbound/mieru.go index 5a2db6309b..af790b6e05 100644 --- a/clash-meta/adapter/outbound/mieru.go +++ b/clash-meta/adapter/outbound/mieru.go @@ -8,7 +8,7 @@ import ( "strconv" "sync" - CN "github.com/metacubex/mihomo/common/net" + N "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/component/resolver" C "github.com/metacubex/mihomo/constant" @@ -104,7 +104,7 @@ func (m *Mieru) ListenPacketContext(ctx context.Context, metadata *C.Metadata) ( if err != nil { return nil, fmt.Errorf("dial to %s failed: %w", metadata.UDPAddr(), err) } - return newPacketConn(CN.NewThreadSafePacketConn(mierucommon.NewUDPAssociateWrapper(mierucommon.NewPacketOverStreamTunnel(c))), m), nil + return newPacketConn(N.NewThreadSafePacketConn(mierucommon.NewUDPAssociateWrapper(mierucommon.NewPacketOverStreamTunnel(c))), m), nil } // SupportUOT implements C.ProxyAdapter @@ -167,12 +167,13 @@ func NewMieru(option MieruOption) (*Mieru, error) { Base: &Base{ name: option.Name, addr: addr, - iface: option.Interface, tp: C.Mieru, + pdName: option.ProviderName, udp: option.UDP, xudp: false, + iface: option.Interface, rmark: option.RoutingMark, - prefer: C.NewDNSPrefer(option.IPVersion), + prefer: option.IPVersion, }, option: &option, client: c, diff --git a/clash-meta/adapter/outbound/shadowsocks.go b/clash-meta/adapter/outbound/shadowsocks.go index 0ece815125..8e4d9354ad 100644 --- a/clash-meta/adapter/outbound/shadowsocks.go +++ b/clash-meta/adapter/outbound/shadowsocks.go @@ -458,12 +458,13 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) { name: option.Name, addr: addr, tp: C.Shadowsocks, + pdName: option.ProviderName, udp: option.UDP, tfo: option.TFO, mpTcp: option.MPTCP, iface: option.Interface, rmark: option.RoutingMark, - prefer: C.NewDNSPrefer(option.IPVersion), + prefer: option.IPVersion, }, method: method, diff --git a/clash-meta/adapter/outbound/shadowsocksr.go b/clash-meta/adapter/outbound/shadowsocksr.go index 8eecf677e3..09c3924e27 100644 --- a/clash-meta/adapter/outbound/shadowsocksr.go +++ b/clash-meta/adapter/outbound/shadowsocksr.go @@ -162,12 +162,13 @@ func NewShadowSocksR(option ShadowSocksROption) (*ShadowSocksR, error) { name: option.Name, addr: addr, tp: C.ShadowsocksR, + pdName: option.ProviderName, udp: option.UDP, tfo: option.TFO, mpTcp: option.MPTCP, iface: option.Interface, rmark: option.RoutingMark, - prefer: C.NewDNSPrefer(option.IPVersion), + prefer: option.IPVersion, }, option: &option, cipher: coreCiph, diff --git a/clash-meta/adapter/outbound/singmux.go b/clash-meta/adapter/outbound/singmux.go index 310e3b61e1..55bd6ac385 100644 --- a/clash-meta/adapter/outbound/singmux.go +++ b/clash-meta/adapter/outbound/singmux.go @@ -3,7 +3,7 @@ package outbound import ( "context" - CN "github.com/metacubex/mihomo/common/net" + N "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/component/proxydialer" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" @@ -59,7 +59,7 @@ func (s *SingMux) ListenPacketContext(ctx context.Context, metadata *C.Metadata) if pc == nil { return nil, E.New("packetConn is nil") } - return newPacketConn(CN.NewThreadSafePacketConn(pc), s), nil + return newPacketConn(N.NewThreadSafePacketConn(pc), s), nil } func (s *SingMux) SupportUDP() bool { diff --git a/clash-meta/adapter/outbound/snell.go b/clash-meta/adapter/outbound/snell.go index 0c7d8876ee..64295a7083 100644 --- a/clash-meta/adapter/outbound/snell.go +++ b/clash-meta/adapter/outbound/snell.go @@ -165,12 +165,13 @@ func NewSnell(option SnellOption) (*Snell, error) { name: option.Name, addr: addr, tp: C.Snell, + pdName: option.ProviderName, udp: option.UDP, tfo: option.TFO, mpTcp: option.MPTCP, iface: option.Interface, rmark: option.RoutingMark, - prefer: C.NewDNSPrefer(option.IPVersion), + prefer: option.IPVersion, }, option: &option, psk: psk, diff --git a/clash-meta/adapter/outbound/socks5.go b/clash-meta/adapter/outbound/socks5.go index 3bc48d8626..c00480844a 100644 --- a/clash-meta/adapter/outbound/socks5.go +++ b/clash-meta/adapter/outbound/socks5.go @@ -190,12 +190,13 @@ func NewSocks5(option Socks5Option) (*Socks5, error) { name: option.Name, addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)), tp: C.Socks5, + pdName: option.ProviderName, udp: option.UDP, tfo: option.TFO, mpTcp: option.MPTCP, iface: option.Interface, rmark: option.RoutingMark, - prefer: C.NewDNSPrefer(option.IPVersion), + prefer: option.IPVersion, }, option: &option, user: option.UserName, diff --git a/clash-meta/adapter/outbound/ssh.go b/clash-meta/adapter/outbound/ssh.go index ec9068dd9b..5dca2b1720 100644 --- a/clash-meta/adapter/outbound/ssh.go +++ b/clash-meta/adapter/outbound/ssh.go @@ -186,10 +186,11 @@ func NewSsh(option SshOption) (*Ssh, error) { name: option.Name, addr: addr, tp: C.Ssh, + pdName: option.ProviderName, udp: false, iface: option.Interface, rmark: option.RoutingMark, - prefer: C.NewDNSPrefer(option.IPVersion), + prefer: option.IPVersion, }, option: &option, config: &config, diff --git a/clash-meta/adapter/outbound/sudoku.go b/clash-meta/adapter/outbound/sudoku.go index 0e6c3bba68..44affacccf 100644 --- a/clash-meta/adapter/outbound/sudoku.go +++ b/clash-meta/adapter/outbound/sudoku.go @@ -238,12 +238,13 @@ func NewSudoku(option SudokuOption) (*Sudoku, error) { name: option.Name, addr: baseConf.ServerAddress, tp: C.Sudoku, + pdName: option.ProviderName, udp: true, tfo: option.TFO, mpTcp: option.MPTCP, iface: option.Interface, rmark: option.RoutingMark, - prefer: C.NewDNSPrefer(option.IPVersion), + prefer: option.IPVersion, }, option: &option, table: table, diff --git a/clash-meta/adapter/outbound/trojan.go b/clash-meta/adapter/outbound/trojan.go index d2f7f38afb..cf86f7b452 100644 --- a/clash-meta/adapter/outbound/trojan.go +++ b/clash-meta/adapter/outbound/trojan.go @@ -288,12 +288,13 @@ func NewTrojan(option TrojanOption) (*Trojan, error) { name: option.Name, addr: addr, tp: C.Trojan, + pdName: option.ProviderName, udp: option.UDP, tfo: option.TFO, mpTcp: option.MPTCP, iface: option.Interface, rmark: option.RoutingMark, - prefer: C.NewDNSPrefer(option.IPVersion), + prefer: option.IPVersion, }, option: &option, hexPassword: trojan.Key(option.Password), diff --git a/clash-meta/adapter/outbound/tuic.go b/clash-meta/adapter/outbound/tuic.go index 8070359ed8..e29ad3ccbc 100644 --- a/clash-meta/adapter/outbound/tuic.go +++ b/clash-meta/adapter/outbound/tuic.go @@ -252,11 +252,12 @@ func NewTuic(option TuicOption) (*Tuic, error) { name: option.Name, addr: addr, tp: C.Tuic, + pdName: option.ProviderName, udp: true, tfo: option.FastOpen, iface: option.Interface, rmark: option.RoutingMark, - prefer: C.NewDNSPrefer(option.IPVersion), + prefer: option.IPVersion, }, option: &option, tlsConfig: tlsClientConfig, diff --git a/clash-meta/adapter/outbound/vless.go b/clash-meta/adapter/outbound/vless.go index d630107693..c5c759ac90 100644 --- a/clash-meta/adapter/outbound/vless.go +++ b/clash-meta/adapter/outbound/vless.go @@ -417,13 +417,14 @@ func NewVless(option VlessOption) (*Vless, error) { name: option.Name, addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)), tp: C.Vless, + pdName: option.ProviderName, udp: option.UDP, xudp: option.XUDP, tfo: option.TFO, mpTcp: option.MPTCP, iface: option.Interface, rmark: option.RoutingMark, - prefer: C.NewDNSPrefer(option.IPVersion), + prefer: option.IPVersion, }, client: client, option: &option, diff --git a/clash-meta/adapter/outbound/vmess.go b/clash-meta/adapter/outbound/vmess.go index 7af32c6db2..ea874250ff 100644 --- a/clash-meta/adapter/outbound/vmess.go +++ b/clash-meta/adapter/outbound/vmess.go @@ -427,13 +427,14 @@ func NewVmess(option VmessOption) (*Vmess, error) { name: option.Name, addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)), tp: C.Vmess, + pdName: option.ProviderName, udp: option.UDP, xudp: option.XUDP, tfo: option.TFO, mpTcp: option.MPTCP, iface: option.Interface, rmark: option.RoutingMark, - prefer: C.NewDNSPrefer(option.IPVersion), + prefer: option.IPVersion, }, client: client, option: &option, diff --git a/clash-meta/adapter/outbound/wireguard.go b/clash-meta/adapter/outbound/wireguard.go index 66d46b0219..86f8a61bbf 100644 --- a/clash-meta/adapter/outbound/wireguard.go +++ b/clash-meta/adapter/outbound/wireguard.go @@ -170,10 +170,11 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { name: option.Name, addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)), tp: C.WireGuard, + pdName: option.ProviderName, udp: option.UDP, iface: option.Interface, rmark: option.RoutingMark, - prefer: C.NewDNSPrefer(option.IPVersion), + prefer: option.IPVersion, }, } outbound.dialer = option.NewDialer(outbound.DialOptions()) diff --git a/clash-meta/adapter/parser.go b/clash-meta/adapter/parser.go index 0ad45ca956..08f90afe34 100644 --- a/clash-meta/adapter/parser.go +++ b/clash-meta/adapter/parser.go @@ -18,6 +18,7 @@ func ParseProxy(mapping map[string]any, options ...ProxyOption) (C.Proxy, error) opt := applyProxyOptions(options...) basicOption := outbound.BasicOption{ DialerForAPI: opt.DialerForAPI, + ProviderName: opt.ProviderName, } var ( @@ -186,6 +187,7 @@ func ParseProxy(mapping map[string]any, options ...ProxyOption) (C.Proxy, error) type proxyOption struct { DialerForAPI C.Dialer + ProviderName string } func applyProxyOptions(options ...ProxyOption) proxyOption { @@ -203,3 +205,9 @@ func WithDialerForAPI(dialer C.Dialer) ProxyOption { opt.DialerForAPI = dialer } } + +func WithProviderName(name string) ProxyOption { + return func(opt *proxyOption) { + opt.ProviderName = name + } +} diff --git a/clash-meta/adapter/provider/parser.go b/clash-meta/adapter/provider/parser.go index d6297b5662..6e0da67837 100644 --- a/clash-meta/adapter/provider/parser.go +++ b/clash-meta/adapter/provider/parser.go @@ -99,7 +99,7 @@ func ParseProxyProvider(name string, mapping map[string]any) (P.ProxyProvider, e } hc := NewHealthCheck([]C.Proxy{}, schema.HealthCheck.URL, uint(schema.HealthCheck.TestTimeout), hcInterval, schema.HealthCheck.Lazy, expectedStatus) - parser, err := NewProxiesParser(schema.Filter, schema.ExcludeFilter, schema.ExcludeType, schema.DialerProxy, schema.Override) + parser, err := NewProxiesParser(name, schema.Filter, schema.ExcludeFilter, schema.ExcludeType, schema.DialerProxy, schema.Override) if err != nil { return nil, err } diff --git a/clash-meta/adapter/provider/provider.go b/clash-meta/adapter/provider/provider.go index e5d3b8acd5..71713d8fbf 100644 --- a/clash-meta/adapter/provider/provider.go +++ b/clash-meta/adapter/provider/provider.go @@ -156,7 +156,7 @@ func (pp *proxySetProvider) Initial() error { func (pp *proxySetProvider) closeAllConnections() { statistic.DefaultManager.Range(func(c statistic.Tracker) bool { - for _, chain := range c.Chains() { + for _, chain := range c.ProviderChains() { if chain == pp.Name() { _ = c.Close() break @@ -330,7 +330,7 @@ func (cp *CompatibleProvider) Close() error { return cp.compatibleProvider.Close() } -func NewProxiesParser(filter string, excludeFilter string, excludeType string, dialerProxy string, override OverrideSchema) (resource.Parser[[]C.Proxy], error) { +func NewProxiesParser(pdName string, filter string, excludeFilter string, excludeType string, dialerProxy string, override OverrideSchema) (resource.Parser[[]C.Proxy], error) { var excludeTypeArray []string if excludeType != "" { excludeTypeArray = strings.Split(excludeType, "|") @@ -448,7 +448,7 @@ func NewProxiesParser(filter string, excludeFilter string, excludeType string, d } } - proxy, err := adapter.ParseProxy(mapping) + proxy, err := adapter.ParseProxy(mapping, adapter.WithProviderName(pdName)) if err != nil { return nil, fmt.Errorf("proxy %d error: %w", idx, err) } diff --git a/clash-meta/common/structure/structure.go b/clash-meta/common/structure/structure.go index 840e4f6cad..d43dec033e 100644 --- a/clash-meta/common/structure/structure.go +++ b/clash-meta/common/structure/structure.go @@ -517,6 +517,10 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e fieldName = tagValue } + if tagValue == "-" { + continue + } + rawMapKey := reflect.ValueOf(fieldName) rawMapVal := dataVal.MapIndex(rawMapKey) if !rawMapVal.IsValid() { diff --git a/clash-meta/common/structure/structure_test.go b/clash-meta/common/structure/structure_test.go index 06dffd0f32..c79c2eb46f 100644 --- a/clash-meta/common/structure/structure_test.go +++ b/clash-meta/common/structure/structure_test.go @@ -308,3 +308,27 @@ func TestStructure_Ignore(t *testing.T) { assert.Nil(t, err) assert.Equal(t, s.MustIgnore, "oldData") } + +func TestStructure_IgnoreInNest(t *testing.T) { + rawMap := map[string]any{ + "-": "newData", + } + + type TP struct { + MustIgnore string `test:"-"` + } + + s := struct { + TP + }{TP{MustIgnore: "oldData"}} + + err := decoder.Decode(rawMap, &s) + assert.Nil(t, err) + assert.Equal(t, s.MustIgnore, "oldData") + + // test omitempty + delete(rawMap, "-") + err = decoder.Decode(rawMap, &s) + assert.Nil(t, err) + assert.Equal(t, s.MustIgnore, "oldData") +} diff --git a/clash-meta/constant/adapters.go b/clash-meta/constant/adapters.go index 0b9098fd69..07ae5de1d8 100644 --- a/clash-meta/constant/adapters.go +++ b/clash-meta/constant/adapters.go @@ -59,6 +59,7 @@ var ErrNotSupport = errors.New("no support") type Connection interface { Chains() Chain + ProviderChains() Chain AppendToChains(adapter ProxyAdapter) RemoteDestination() string } @@ -102,13 +103,14 @@ type Dialer interface { } type ProxyInfo struct { - XUDP bool - TFO bool - MPTCP bool - SMUX bool - Interface string - RoutingMark int - DialerProxy string + XUDP bool + TFO bool + MPTCP bool + SMUX bool + Interface string + RoutingMark int + ProviderName string + DialerProxy string } type ProxyAdapter interface { diff --git a/clash-meta/constant/dns.go b/clash-meta/constant/dns.go index 13a2ed362a..79dafd2bb9 100644 --- a/clash-meta/constant/dns.go +++ b/clash-meta/constant/dns.go @@ -86,12 +86,17 @@ func (d DNSPrefer) String() string { } } -func NewDNSPrefer(prefer string) DNSPrefer { - if p, ok := dnsPreferMap[prefer]; ok { - return p - } else { - return DualStack +func (d DNSPrefer) MarshalText() ([]byte, error) { + return []byte(d.String()), nil +} + +func (d *DNSPrefer) UnmarshalText(data []byte) error { + p, exist := dnsPreferMap[strings.ToLower(string(data))] + if !exist { + p = DualStack } + *d = p + return nil } // FilterModeMapping is a mapping for FilterMode enum diff --git a/clash-meta/tunnel/statistic/tracker.go b/clash-meta/tunnel/statistic/tracker.go index ffa2e71ede..2fbb1cf9e3 100644 --- a/clash-meta/tunnel/statistic/tracker.go +++ b/clash-meta/tunnel/statistic/tracker.go @@ -28,6 +28,7 @@ type TrackerInfo struct { DownloadTotal atomic.Int64 `json:"download"` Start time.Time `json:"start"` Chain C.Chain `json:"chains"` + ProviderChain C.Chain `json:"providerChains"` Rule string `json:"rule"` RulePayload string `json:"rulePayload"` } @@ -126,6 +127,7 @@ func NewTCPTracker(conn C.Conn, manager *Manager, metadata *C.Metadata, rule C.R Start: time.Now(), Metadata: metadata, Chain: conn.Chains(), + ProviderChain: conn.ProviderChains(), Rule: "", UploadTotal: atomic.NewInt64(uploadTotal), DownloadTotal: atomic.NewInt64(downloadTotal), @@ -217,6 +219,7 @@ func NewUDPTracker(conn C.PacketConn, manager *Manager, metadata *C.Metadata, ru Start: time.Now(), Metadata: metadata, Chain: conn.Chains(), + ProviderChain: conn.ProviderChains(), Rule: "", UploadTotal: atomic.NewInt64(uploadTotal), DownloadTotal: atomic.NewInt64(downloadTotal), diff --git a/clash-nyanpasu/manifest/version.json b/clash-nyanpasu/manifest/version.json index 6a5b776520..597c367441 100644 --- a/clash-nyanpasu/manifest/version.json +++ b/clash-nyanpasu/manifest/version.json @@ -2,7 +2,7 @@ "manifest_version": 1, "latest": { "mihomo": "v1.19.17", - "mihomo_alpha": "alpha-fdb7cb1", + "mihomo_alpha": "alpha-32ce513", "clash_rs": "v0.9.2", "clash_premium": "2023-09-05-gdcc8d87", "clash_rs_alpha": "0.9.2-alpha+sha.e1f8fbb" @@ -69,5 +69,5 @@ "linux-armv7hf": "clash-armv7-unknown-linux-gnueabihf" } }, - "updated_at": "2025-12-02T22:21:02.066Z" + "updated_at": "2025-12-03T22:21:43.313Z" } diff --git a/lede/include/target.mk b/lede/include/target.mk index 12e17098ec..de9f9a63e1 100644 --- a/lede/include/target.mk +++ b/lede/include/target.mk @@ -299,6 +299,9 @@ ifeq ($(DUMP),1) ifneq ($(CONFIG_PCIEPORTBUS),) FEATURES += pcie endif + ifneq ($(CONFIG_PWM),) + FEATURES += pwm + endif ifneq ($(CONFIG_USB)$(CONFIG_USB_SUPPORT),) ifneq ($(CONFIG_USB_ARCH_HAS_HCD)$(CONFIG_USB_EHCI_HCD),) FEATURES += usb diff --git a/mihomo/adapter/adapter.go b/mihomo/adapter/adapter.go index ef8d4ee347..815064568b 100644 --- a/mihomo/adapter/adapter.go +++ b/mihomo/adapter/adapter.go @@ -153,8 +153,9 @@ func (p *Proxy) MarshalJSON() ([]byte, error) { mapping["mptcp"] = proxyInfo.MPTCP mapping["smux"] = proxyInfo.SMUX mapping["interface"] = proxyInfo.Interface - mapping["dialer-proxy"] = proxyInfo.DialerProxy mapping["routing-mark"] = proxyInfo.RoutingMark + mapping["provider-name"] = proxyInfo.ProviderName + mapping["dialer-proxy"] = proxyInfo.DialerProxy return json.Marshal(mapping) } @@ -177,14 +178,12 @@ func (p *Proxy) URLTest(ctx context.Context, url string, expectedStatus utils.In p.history.Pop() } - state, ok := p.extra.Load(url) - if !ok { - state = &internalProxyState{ + state, _ := p.extra.LoadOrStoreFn(url, func() *internalProxyState { + return &internalProxyState{ history: queue.New[C.DelayHistory](defaultHistoriesNum), alive: atomic.NewBool(true), } - p.extra.Store(url, state) - } + }) if !satisfied { record.Delay = 0 diff --git a/mihomo/adapter/outbound/anytls.go b/mihomo/adapter/outbound/anytls.go index 5ba7c6d265..c17f27d55e 100644 --- a/mihomo/adapter/outbound/anytls.go +++ b/mihomo/adapter/outbound/anytls.go @@ -6,7 +6,7 @@ import ( "strconv" "time" - CN "github.com/metacubex/mihomo/common/net" + N "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/component/proxydialer" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/transport/anytls" @@ -63,7 +63,7 @@ func (t *AnyTLS) ListenPacketContext(ctx context.Context, metadata *C.Metadata) // create uot on tcp destination := M.SocksaddrFromNet(metadata.UDPAddr()) - return newPacketConn(CN.NewThreadSafePacketConn(uot.NewLazyConn(c, uot.Request{Destination: destination})), t), nil + return newPacketConn(N.NewThreadSafePacketConn(uot.NewLazyConn(c, uot.Request{Destination: destination})), t), nil } // SupportUOT implements C.ProxyAdapter @@ -90,12 +90,13 @@ func NewAnyTLS(option AnyTLSOption) (*AnyTLS, error) { name: option.Name, addr: addr, tp: C.AnyTLS, + pdName: option.ProviderName, udp: option.UDP, tfo: option.TFO, mpTcp: option.MPTCP, iface: option.Interface, rmark: option.RoutingMark, - prefer: C.NewDNSPrefer(option.IPVersion), + prefer: option.IPVersion, }, option: &option, } diff --git a/mihomo/adapter/outbound/base.go b/mihomo/adapter/outbound/base.go index a3408f70fb..4a4c412d05 100644 --- a/mihomo/adapter/outbound/base.go +++ b/mihomo/adapter/outbound/base.go @@ -27,16 +27,17 @@ type ProxyAdapter interface { type Base struct { name string addr string - iface string tp C.AdapterType + pdName string udp bool xudp bool tfo bool mpTcp bool + iface string rmark int - id string prefer C.DNSPrefer dialer C.Dialer + id string } // Name implements C.ProxyAdapter @@ -85,6 +86,7 @@ func (b *Base) ProxyInfo() (info C.ProxyInfo) { info.SMUX = false info.Interface = b.iface info.RoutingMark = b.rmark + info.ProviderName = b.pdName return } @@ -160,14 +162,18 @@ func (b *Base) Close() error { } type BasicOption struct { - TFO bool `proxy:"tfo,omitempty"` - MPTCP bool `proxy:"mptcp,omitempty"` - Interface string `proxy:"interface-name,omitempty"` - RoutingMark int `proxy:"routing-mark,omitempty"` - IPVersion string `proxy:"ip-version,omitempty"` - DialerProxy string `proxy:"dialer-proxy,omitempty"` // don't apply this option into groups, but can set a group name in a proxy + TFO bool `proxy:"tfo,omitempty"` + MPTCP bool `proxy:"mptcp,omitempty"` + Interface string `proxy:"interface-name,omitempty"` + RoutingMark int `proxy:"routing-mark,omitempty"` + IPVersion C.DNSPrefer `proxy:"ip-version,omitempty"` + DialerProxy string `proxy:"dialer-proxy,omitempty"` // don't apply this option into groups, but can set a group name in a proxy + // + // The following parameters are used internally, assign value by the structure decoder are disallowed + // DialerForAPI C.Dialer `proxy:"-"` // the dialer used for API usage has higher priority than all the above configurations. + ProviderName string `proxy:"-"` } func (b *BasicOption) NewDialer(opts []dialer.Option) C.Dialer { @@ -213,6 +219,7 @@ func NewBase(opt BaseOption) *Base { type conn struct { N.ExtendedConn chain C.Chain + pdChain C.Chain adapterAddr string } @@ -234,9 +241,15 @@ func (c *conn) Chains() C.Chain { return c.chain } +// ProviderChains implements C.Connection +func (c *conn) ProviderChains() C.Chain { + return c.pdChain +} + // AppendToChains implements C.Connection func (c *conn) AppendToChains(a C.ProxyAdapter) { c.chain = append(c.chain, a.Name()) + c.pdChain = append(c.pdChain, a.ProxyInfo().ProviderName) } func (c *conn) Upstream() any { @@ -259,7 +272,7 @@ func NewConn(c net.Conn, a C.ProxyAdapter) C.Conn { if _, ok := c.(syscall.Conn); !ok { // exclusion system conn like *net.TCPConn c = N.NewDeadlineConn(c) // most conn from outbound can't handle readDeadline correctly } - cc := &conn{N.NewExtendedConn(c), nil, a.Addr()} + cc := &conn{N.NewExtendedConn(c), nil, nil, a.Addr()} cc.AppendToChains(a) return cc } @@ -267,6 +280,7 @@ func NewConn(c net.Conn, a C.ProxyAdapter) C.Conn { type packetConn struct { N.EnhancePacketConn chain C.Chain + pdChain C.Chain adapterName string connID string adapterAddr string @@ -287,9 +301,15 @@ func (c *packetConn) Chains() C.Chain { return c.chain } +// ProviderChains implements C.Connection +func (c *packetConn) ProviderChains() C.Chain { + return c.pdChain +} + // AppendToChains implements C.Connection func (c *packetConn) AppendToChains(a C.ProxyAdapter) { c.chain = append(c.chain, a.Name()) + c.pdChain = append(c.pdChain, a.ProxyInfo().ProviderName) } func (c *packetConn) LocalAddr() net.Addr { @@ -318,7 +338,7 @@ func newPacketConn(pc net.PacketConn, a ProxyAdapter) C.PacketConn { if _, ok := pc.(syscall.Conn); !ok { // exclusion system conn like *net.UDPConn epc = N.NewDeadlineEnhancePacketConn(epc) // most conn from outbound can't handle readDeadline correctly } - cpc := &packetConn{epc, nil, a.Name(), utils.NewUUIDV4().String(), a.Addr(), a.ResolveUDP} + cpc := &packetConn{epc, nil, nil, a.Name(), utils.NewUUIDV4().String(), a.Addr(), a.ResolveUDP} cpc.AppendToChains(a) return cpc } diff --git a/mihomo/adapter/outbound/direct.go b/mihomo/adapter/outbound/direct.go index be8367ba8f..42cd8def13 100644 --- a/mihomo/adapter/outbound/direct.go +++ b/mihomo/adapter/outbound/direct.go @@ -69,12 +69,13 @@ func NewDirectWithOption(option DirectOption) *Direct { Base: &Base{ name: option.Name, tp: C.Direct, + pdName: option.ProviderName, udp: true, tfo: option.TFO, mpTcp: option.MPTCP, iface: option.Interface, rmark: option.RoutingMark, - prefer: C.NewDNSPrefer(option.IPVersion), + prefer: option.IPVersion, }, loopBack: loopback.NewDetector(), } diff --git a/mihomo/adapter/outbound/dns.go b/mihomo/adapter/outbound/dns.go index 2522850247..5e253d2a01 100644 --- a/mihomo/adapter/outbound/dns.go +++ b/mihomo/adapter/outbound/dns.go @@ -158,12 +158,13 @@ func NewDnsWithOption(option DnsOption) *Dns { Base: &Base{ name: option.Name, tp: C.Dns, + pdName: option.ProviderName, udp: true, tfo: option.TFO, mpTcp: option.MPTCP, iface: option.Interface, rmark: option.RoutingMark, - prefer: C.NewDNSPrefer(option.IPVersion), + prefer: option.IPVersion, }, } } diff --git a/mihomo/adapter/outbound/http.go b/mihomo/adapter/outbound/http.go index 61b8596ad5..9707bf13c2 100644 --- a/mihomo/adapter/outbound/http.go +++ b/mihomo/adapter/outbound/http.go @@ -170,11 +170,12 @@ func NewHttp(option HttpOption) (*Http, error) { name: option.Name, addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)), tp: C.Http, + pdName: option.ProviderName, tfo: option.TFO, mpTcp: option.MPTCP, iface: option.Interface, rmark: option.RoutingMark, - prefer: C.NewDNSPrefer(option.IPVersion), + prefer: option.IPVersion, }, user: option.UserName, pass: option.Password, diff --git a/mihomo/adapter/outbound/hysteria.go b/mihomo/adapter/outbound/hysteria.go index 4673779861..67c9af0b6e 100644 --- a/mihomo/adapter/outbound/hysteria.go +++ b/mihomo/adapter/outbound/hysteria.go @@ -243,11 +243,12 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) { name: option.Name, addr: addr, tp: C.Hysteria, + pdName: option.ProviderName, udp: true, tfo: option.FastOpen, iface: option.Interface, rmark: option.RoutingMark, - prefer: C.NewDNSPrefer(option.IPVersion), + prefer: option.IPVersion, }, option: &option, client: client, diff --git a/mihomo/adapter/outbound/hysteria2.go b/mihomo/adapter/outbound/hysteria2.go index 78d2b9ff95..2982276938 100644 --- a/mihomo/adapter/outbound/hysteria2.go +++ b/mihomo/adapter/outbound/hysteria2.go @@ -9,7 +9,7 @@ import ( "strconv" "time" - CN "github.com/metacubex/mihomo/common/net" + N "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/component/ca" "github.com/metacubex/mihomo/component/proxydialer" @@ -85,7 +85,7 @@ func (h *Hysteria2) ListenPacketContext(ctx context.Context, metadata *C.Metadat if pc == nil { return nil, errors.New("packetConn is nil") } - return newPacketConn(CN.NewThreadSafePacketConn(pc), h), nil + return newPacketConn(N.NewThreadSafePacketConn(pc), h), nil } // Close implements C.ProxyAdapter @@ -110,10 +110,11 @@ func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) { name: option.Name, addr: addr, tp: C.Hysteria2, + pdName: option.ProviderName, udp: true, iface: option.Interface, rmark: option.RoutingMark, - prefer: C.NewDNSPrefer(option.IPVersion), + prefer: option.IPVersion, }, option: &option, } @@ -189,7 +190,7 @@ func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) { CWND: option.CWND, UdpMTU: option.UdpMTU, ServerAddress: func(ctx context.Context) (*net.UDPAddr, error) { - udpAddr, err := resolveUDPAddr(ctx, "udp", addr, C.NewDNSPrefer(option.IPVersion)) + udpAddr, err := resolveUDPAddr(ctx, "udp", addr, option.IPVersion) if err != nil { return nil, err } diff --git a/mihomo/adapter/outbound/mieru.go b/mihomo/adapter/outbound/mieru.go index 5a2db6309b..af790b6e05 100644 --- a/mihomo/adapter/outbound/mieru.go +++ b/mihomo/adapter/outbound/mieru.go @@ -8,7 +8,7 @@ import ( "strconv" "sync" - CN "github.com/metacubex/mihomo/common/net" + N "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/component/resolver" C "github.com/metacubex/mihomo/constant" @@ -104,7 +104,7 @@ func (m *Mieru) ListenPacketContext(ctx context.Context, metadata *C.Metadata) ( if err != nil { return nil, fmt.Errorf("dial to %s failed: %w", metadata.UDPAddr(), err) } - return newPacketConn(CN.NewThreadSafePacketConn(mierucommon.NewUDPAssociateWrapper(mierucommon.NewPacketOverStreamTunnel(c))), m), nil + return newPacketConn(N.NewThreadSafePacketConn(mierucommon.NewUDPAssociateWrapper(mierucommon.NewPacketOverStreamTunnel(c))), m), nil } // SupportUOT implements C.ProxyAdapter @@ -167,12 +167,13 @@ func NewMieru(option MieruOption) (*Mieru, error) { Base: &Base{ name: option.Name, addr: addr, - iface: option.Interface, tp: C.Mieru, + pdName: option.ProviderName, udp: option.UDP, xudp: false, + iface: option.Interface, rmark: option.RoutingMark, - prefer: C.NewDNSPrefer(option.IPVersion), + prefer: option.IPVersion, }, option: &option, client: c, diff --git a/mihomo/adapter/outbound/shadowsocks.go b/mihomo/adapter/outbound/shadowsocks.go index 0ece815125..8e4d9354ad 100644 --- a/mihomo/adapter/outbound/shadowsocks.go +++ b/mihomo/adapter/outbound/shadowsocks.go @@ -458,12 +458,13 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) { name: option.Name, addr: addr, tp: C.Shadowsocks, + pdName: option.ProviderName, udp: option.UDP, tfo: option.TFO, mpTcp: option.MPTCP, iface: option.Interface, rmark: option.RoutingMark, - prefer: C.NewDNSPrefer(option.IPVersion), + prefer: option.IPVersion, }, method: method, diff --git a/mihomo/adapter/outbound/shadowsocksr.go b/mihomo/adapter/outbound/shadowsocksr.go index 8eecf677e3..09c3924e27 100644 --- a/mihomo/adapter/outbound/shadowsocksr.go +++ b/mihomo/adapter/outbound/shadowsocksr.go @@ -162,12 +162,13 @@ func NewShadowSocksR(option ShadowSocksROption) (*ShadowSocksR, error) { name: option.Name, addr: addr, tp: C.ShadowsocksR, + pdName: option.ProviderName, udp: option.UDP, tfo: option.TFO, mpTcp: option.MPTCP, iface: option.Interface, rmark: option.RoutingMark, - prefer: C.NewDNSPrefer(option.IPVersion), + prefer: option.IPVersion, }, option: &option, cipher: coreCiph, diff --git a/mihomo/adapter/outbound/singmux.go b/mihomo/adapter/outbound/singmux.go index 310e3b61e1..55bd6ac385 100644 --- a/mihomo/adapter/outbound/singmux.go +++ b/mihomo/adapter/outbound/singmux.go @@ -3,7 +3,7 @@ package outbound import ( "context" - CN "github.com/metacubex/mihomo/common/net" + N "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/component/proxydialer" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" @@ -59,7 +59,7 @@ func (s *SingMux) ListenPacketContext(ctx context.Context, metadata *C.Metadata) if pc == nil { return nil, E.New("packetConn is nil") } - return newPacketConn(CN.NewThreadSafePacketConn(pc), s), nil + return newPacketConn(N.NewThreadSafePacketConn(pc), s), nil } func (s *SingMux) SupportUDP() bool { diff --git a/mihomo/adapter/outbound/snell.go b/mihomo/adapter/outbound/snell.go index 0c7d8876ee..64295a7083 100644 --- a/mihomo/adapter/outbound/snell.go +++ b/mihomo/adapter/outbound/snell.go @@ -165,12 +165,13 @@ func NewSnell(option SnellOption) (*Snell, error) { name: option.Name, addr: addr, tp: C.Snell, + pdName: option.ProviderName, udp: option.UDP, tfo: option.TFO, mpTcp: option.MPTCP, iface: option.Interface, rmark: option.RoutingMark, - prefer: C.NewDNSPrefer(option.IPVersion), + prefer: option.IPVersion, }, option: &option, psk: psk, diff --git a/mihomo/adapter/outbound/socks5.go b/mihomo/adapter/outbound/socks5.go index 3bc48d8626..c00480844a 100644 --- a/mihomo/adapter/outbound/socks5.go +++ b/mihomo/adapter/outbound/socks5.go @@ -190,12 +190,13 @@ func NewSocks5(option Socks5Option) (*Socks5, error) { name: option.Name, addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)), tp: C.Socks5, + pdName: option.ProviderName, udp: option.UDP, tfo: option.TFO, mpTcp: option.MPTCP, iface: option.Interface, rmark: option.RoutingMark, - prefer: C.NewDNSPrefer(option.IPVersion), + prefer: option.IPVersion, }, option: &option, user: option.UserName, diff --git a/mihomo/adapter/outbound/ssh.go b/mihomo/adapter/outbound/ssh.go index ec9068dd9b..5dca2b1720 100644 --- a/mihomo/adapter/outbound/ssh.go +++ b/mihomo/adapter/outbound/ssh.go @@ -186,10 +186,11 @@ func NewSsh(option SshOption) (*Ssh, error) { name: option.Name, addr: addr, tp: C.Ssh, + pdName: option.ProviderName, udp: false, iface: option.Interface, rmark: option.RoutingMark, - prefer: C.NewDNSPrefer(option.IPVersion), + prefer: option.IPVersion, }, option: &option, config: &config, diff --git a/mihomo/adapter/outbound/sudoku.go b/mihomo/adapter/outbound/sudoku.go index 0e6c3bba68..44affacccf 100644 --- a/mihomo/adapter/outbound/sudoku.go +++ b/mihomo/adapter/outbound/sudoku.go @@ -238,12 +238,13 @@ func NewSudoku(option SudokuOption) (*Sudoku, error) { name: option.Name, addr: baseConf.ServerAddress, tp: C.Sudoku, + pdName: option.ProviderName, udp: true, tfo: option.TFO, mpTcp: option.MPTCP, iface: option.Interface, rmark: option.RoutingMark, - prefer: C.NewDNSPrefer(option.IPVersion), + prefer: option.IPVersion, }, option: &option, table: table, diff --git a/mihomo/adapter/outbound/trojan.go b/mihomo/adapter/outbound/trojan.go index d2f7f38afb..cf86f7b452 100644 --- a/mihomo/adapter/outbound/trojan.go +++ b/mihomo/adapter/outbound/trojan.go @@ -288,12 +288,13 @@ func NewTrojan(option TrojanOption) (*Trojan, error) { name: option.Name, addr: addr, tp: C.Trojan, + pdName: option.ProviderName, udp: option.UDP, tfo: option.TFO, mpTcp: option.MPTCP, iface: option.Interface, rmark: option.RoutingMark, - prefer: C.NewDNSPrefer(option.IPVersion), + prefer: option.IPVersion, }, option: &option, hexPassword: trojan.Key(option.Password), diff --git a/mihomo/adapter/outbound/tuic.go b/mihomo/adapter/outbound/tuic.go index 8070359ed8..e29ad3ccbc 100644 --- a/mihomo/adapter/outbound/tuic.go +++ b/mihomo/adapter/outbound/tuic.go @@ -252,11 +252,12 @@ func NewTuic(option TuicOption) (*Tuic, error) { name: option.Name, addr: addr, tp: C.Tuic, + pdName: option.ProviderName, udp: true, tfo: option.FastOpen, iface: option.Interface, rmark: option.RoutingMark, - prefer: C.NewDNSPrefer(option.IPVersion), + prefer: option.IPVersion, }, option: &option, tlsConfig: tlsClientConfig, diff --git a/mihomo/adapter/outbound/vless.go b/mihomo/adapter/outbound/vless.go index d630107693..c5c759ac90 100644 --- a/mihomo/adapter/outbound/vless.go +++ b/mihomo/adapter/outbound/vless.go @@ -417,13 +417,14 @@ func NewVless(option VlessOption) (*Vless, error) { name: option.Name, addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)), tp: C.Vless, + pdName: option.ProviderName, udp: option.UDP, xudp: option.XUDP, tfo: option.TFO, mpTcp: option.MPTCP, iface: option.Interface, rmark: option.RoutingMark, - prefer: C.NewDNSPrefer(option.IPVersion), + prefer: option.IPVersion, }, client: client, option: &option, diff --git a/mihomo/adapter/outbound/vmess.go b/mihomo/adapter/outbound/vmess.go index 7af32c6db2..ea874250ff 100644 --- a/mihomo/adapter/outbound/vmess.go +++ b/mihomo/adapter/outbound/vmess.go @@ -427,13 +427,14 @@ func NewVmess(option VmessOption) (*Vmess, error) { name: option.Name, addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)), tp: C.Vmess, + pdName: option.ProviderName, udp: option.UDP, xudp: option.XUDP, tfo: option.TFO, mpTcp: option.MPTCP, iface: option.Interface, rmark: option.RoutingMark, - prefer: C.NewDNSPrefer(option.IPVersion), + prefer: option.IPVersion, }, client: client, option: &option, diff --git a/mihomo/adapter/outbound/wireguard.go b/mihomo/adapter/outbound/wireguard.go index 66d46b0219..86f8a61bbf 100644 --- a/mihomo/adapter/outbound/wireguard.go +++ b/mihomo/adapter/outbound/wireguard.go @@ -170,10 +170,11 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { name: option.Name, addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)), tp: C.WireGuard, + pdName: option.ProviderName, udp: option.UDP, iface: option.Interface, rmark: option.RoutingMark, - prefer: C.NewDNSPrefer(option.IPVersion), + prefer: option.IPVersion, }, } outbound.dialer = option.NewDialer(outbound.DialOptions()) diff --git a/mihomo/adapter/parser.go b/mihomo/adapter/parser.go index 0ad45ca956..08f90afe34 100644 --- a/mihomo/adapter/parser.go +++ b/mihomo/adapter/parser.go @@ -18,6 +18,7 @@ func ParseProxy(mapping map[string]any, options ...ProxyOption) (C.Proxy, error) opt := applyProxyOptions(options...) basicOption := outbound.BasicOption{ DialerForAPI: opt.DialerForAPI, + ProviderName: opt.ProviderName, } var ( @@ -186,6 +187,7 @@ func ParseProxy(mapping map[string]any, options ...ProxyOption) (C.Proxy, error) type proxyOption struct { DialerForAPI C.Dialer + ProviderName string } func applyProxyOptions(options ...ProxyOption) proxyOption { @@ -203,3 +205,9 @@ func WithDialerForAPI(dialer C.Dialer) ProxyOption { opt.DialerForAPI = dialer } } + +func WithProviderName(name string) ProxyOption { + return func(opt *proxyOption) { + opt.ProviderName = name + } +} diff --git a/mihomo/adapter/provider/parser.go b/mihomo/adapter/provider/parser.go index d6297b5662..6e0da67837 100644 --- a/mihomo/adapter/provider/parser.go +++ b/mihomo/adapter/provider/parser.go @@ -99,7 +99,7 @@ func ParseProxyProvider(name string, mapping map[string]any) (P.ProxyProvider, e } hc := NewHealthCheck([]C.Proxy{}, schema.HealthCheck.URL, uint(schema.HealthCheck.TestTimeout), hcInterval, schema.HealthCheck.Lazy, expectedStatus) - parser, err := NewProxiesParser(schema.Filter, schema.ExcludeFilter, schema.ExcludeType, schema.DialerProxy, schema.Override) + parser, err := NewProxiesParser(name, schema.Filter, schema.ExcludeFilter, schema.ExcludeType, schema.DialerProxy, schema.Override) if err != nil { return nil, err } diff --git a/mihomo/adapter/provider/provider.go b/mihomo/adapter/provider/provider.go index e5d3b8acd5..71713d8fbf 100644 --- a/mihomo/adapter/provider/provider.go +++ b/mihomo/adapter/provider/provider.go @@ -156,7 +156,7 @@ func (pp *proxySetProvider) Initial() error { func (pp *proxySetProvider) closeAllConnections() { statistic.DefaultManager.Range(func(c statistic.Tracker) bool { - for _, chain := range c.Chains() { + for _, chain := range c.ProviderChains() { if chain == pp.Name() { _ = c.Close() break @@ -330,7 +330,7 @@ func (cp *CompatibleProvider) Close() error { return cp.compatibleProvider.Close() } -func NewProxiesParser(filter string, excludeFilter string, excludeType string, dialerProxy string, override OverrideSchema) (resource.Parser[[]C.Proxy], error) { +func NewProxiesParser(pdName string, filter string, excludeFilter string, excludeType string, dialerProxy string, override OverrideSchema) (resource.Parser[[]C.Proxy], error) { var excludeTypeArray []string if excludeType != "" { excludeTypeArray = strings.Split(excludeType, "|") @@ -448,7 +448,7 @@ func NewProxiesParser(filter string, excludeFilter string, excludeType string, d } } - proxy, err := adapter.ParseProxy(mapping) + proxy, err := adapter.ParseProxy(mapping, adapter.WithProviderName(pdName)) if err != nil { return nil, fmt.Errorf("proxy %d error: %w", idx, err) } diff --git a/mihomo/common/structure/structure.go b/mihomo/common/structure/structure.go index 840e4f6cad..d43dec033e 100644 --- a/mihomo/common/structure/structure.go +++ b/mihomo/common/structure/structure.go @@ -517,6 +517,10 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e fieldName = tagValue } + if tagValue == "-" { + continue + } + rawMapKey := reflect.ValueOf(fieldName) rawMapVal := dataVal.MapIndex(rawMapKey) if !rawMapVal.IsValid() { diff --git a/mihomo/common/structure/structure_test.go b/mihomo/common/structure/structure_test.go index 06dffd0f32..c79c2eb46f 100644 --- a/mihomo/common/structure/structure_test.go +++ b/mihomo/common/structure/structure_test.go @@ -308,3 +308,27 @@ func TestStructure_Ignore(t *testing.T) { assert.Nil(t, err) assert.Equal(t, s.MustIgnore, "oldData") } + +func TestStructure_IgnoreInNest(t *testing.T) { + rawMap := map[string]any{ + "-": "newData", + } + + type TP struct { + MustIgnore string `test:"-"` + } + + s := struct { + TP + }{TP{MustIgnore: "oldData"}} + + err := decoder.Decode(rawMap, &s) + assert.Nil(t, err) + assert.Equal(t, s.MustIgnore, "oldData") + + // test omitempty + delete(rawMap, "-") + err = decoder.Decode(rawMap, &s) + assert.Nil(t, err) + assert.Equal(t, s.MustIgnore, "oldData") +} diff --git a/mihomo/constant/adapters.go b/mihomo/constant/adapters.go index 0b9098fd69..07ae5de1d8 100644 --- a/mihomo/constant/adapters.go +++ b/mihomo/constant/adapters.go @@ -59,6 +59,7 @@ var ErrNotSupport = errors.New("no support") type Connection interface { Chains() Chain + ProviderChains() Chain AppendToChains(adapter ProxyAdapter) RemoteDestination() string } @@ -102,13 +103,14 @@ type Dialer interface { } type ProxyInfo struct { - XUDP bool - TFO bool - MPTCP bool - SMUX bool - Interface string - RoutingMark int - DialerProxy string + XUDP bool + TFO bool + MPTCP bool + SMUX bool + Interface string + RoutingMark int + ProviderName string + DialerProxy string } type ProxyAdapter interface { diff --git a/mihomo/constant/dns.go b/mihomo/constant/dns.go index 13a2ed362a..79dafd2bb9 100644 --- a/mihomo/constant/dns.go +++ b/mihomo/constant/dns.go @@ -86,12 +86,17 @@ func (d DNSPrefer) String() string { } } -func NewDNSPrefer(prefer string) DNSPrefer { - if p, ok := dnsPreferMap[prefer]; ok { - return p - } else { - return DualStack +func (d DNSPrefer) MarshalText() ([]byte, error) { + return []byte(d.String()), nil +} + +func (d *DNSPrefer) UnmarshalText(data []byte) error { + p, exist := dnsPreferMap[strings.ToLower(string(data))] + if !exist { + p = DualStack } + *d = p + return nil } // FilterModeMapping is a mapping for FilterMode enum diff --git a/mihomo/tunnel/statistic/tracker.go b/mihomo/tunnel/statistic/tracker.go index ffa2e71ede..2fbb1cf9e3 100644 --- a/mihomo/tunnel/statistic/tracker.go +++ b/mihomo/tunnel/statistic/tracker.go @@ -28,6 +28,7 @@ type TrackerInfo struct { DownloadTotal atomic.Int64 `json:"download"` Start time.Time `json:"start"` Chain C.Chain `json:"chains"` + ProviderChain C.Chain `json:"providerChains"` Rule string `json:"rule"` RulePayload string `json:"rulePayload"` } @@ -126,6 +127,7 @@ func NewTCPTracker(conn C.Conn, manager *Manager, metadata *C.Metadata, rule C.R Start: time.Now(), Metadata: metadata, Chain: conn.Chains(), + ProviderChain: conn.ProviderChains(), Rule: "", UploadTotal: atomic.NewInt64(uploadTotal), DownloadTotal: atomic.NewInt64(downloadTotal), @@ -217,6 +219,7 @@ func NewUDPTracker(conn C.PacketConn, manager *Manager, metadata *C.Metadata, ru Start: time.Now(), Metadata: metadata, Chain: conn.Chains(), + ProviderChain: conn.ProviderChains(), Rule: "", UploadTotal: atomic.NewInt64(uploadTotal), DownloadTotal: atomic.NewInt64(downloadTotal), diff --git a/openwrt-passwall/luci-app-passwall/luasrc/controller/passwall.lua b/openwrt-passwall/luci-app-passwall/luasrc/controller/passwall.lua index e46862f4b1..0c7400dd5a 100644 --- a/openwrt-passwall/luci-app-passwall/luasrc/controller/passwall.lua +++ b/openwrt-passwall/luci-app-passwall/luasrc/controller/passwall.lua @@ -82,6 +82,7 @@ function index() entry({"admin", "services", appname, "copy_node"}, call("copy_node")).leaf = true entry({"admin", "services", appname, "clear_all_nodes"}, call("clear_all_nodes")).leaf = true entry({"admin", "services", appname, "delete_select_nodes"}, call("delete_select_nodes")).leaf = true + entry({"admin", "services", appname, "reassign_group"}, call("reassign_group")).leaf = true entry({"admin", "services", appname, "get_node"}, call("get_node")).leaf = true entry({"admin", "services", appname, "save_node_order"}, call("save_node_order")).leaf = true entry({"admin", "services", appname, "update_rules"}, call("update_rules")).leaf = true @@ -640,6 +641,20 @@ function save_node_order() http_write_json({ status = "ok" }) end +function reassign_group() + local ids = http.formvalue("ids") or "" + local group = http.formvalue("group") or "default" + for id in ids:gmatch("([^,]+)") do + if group ~="" and group ~= "default" then + api.sh_uci_set(appname, id, "group", group) + else + api.sh_uci_del(appname, id, "group") + end + end + api.sh_uci_commit(appname) + http_write_json({ status = "ok" }) +end + function update_rules() local update = http.formvalue("update") luci.sys.call("lua /usr/share/passwall/rule_update.lua log '" .. update .. "' > /dev/null 2>&1 &") diff --git a/openwrt-passwall/luci-app-passwall/luasrc/view/passwall/node_list/link_add_node.htm b/openwrt-passwall/luci-app-passwall/luasrc/view/passwall/node_list/link_add_node.htm index 3caee6fb99..63863ee27e 100644 --- a/openwrt-passwall/luci-app-passwall/luasrc/view/passwall/node_list/link_add_node.htm +++ b/openwrt-passwall/luci-app-passwall/luasrc/view/passwall/node_list/link_add_node.htm @@ -41,12 +41,16 @@ local api = require "luci.passwall.api" } function open_add_link_div() { + document.getElementById('modal-mask').style.display = 'block'; document.getElementById("add_link_div").style.display = "block"; + document.body.classList.add('modal-open'); document.getElementById("nodes_link").focus(); } function close_add_link_div() { + document.getElementById('modal-mask').style.display = 'none'; document.getElementById("add_link_div").style.display = "none"; + document.body.classList.remove('modal-open'); } function add_node() { @@ -81,13 +85,69 @@ local api = require "luci.passwall.api" } } + function open_reassign_group_div() { + var ids = []; + var visibleContainer = document.querySelector('#cbi-passwall-nodes > .cbi-tabcontainer[style*="display:block"], #cbi-passwall-nodes > .cbi-tabcontainer[style*="display: block"]'); + if (!visibleContainer) return; + var doms = visibleContainer.getElementsByClassName("nodes_select"); + if (doms && doms.length > 0) { + for (var i = 0 ; i < doms.length; i++) { + if (doms[i].checked) { + ids.push(doms[i].getAttribute("cbid")) + } + } + if (ids.length > 0) { + document.getElementById('modal-mask').style.display = 'block'; + document.getElementById("reassign_group_div").style.display = "block"; + document.body.classList.add('modal-open'); + } else { + alert("<%:You no select nodes !%>"); + } + } + } + + function close_reassign_group_div() { + document.getElementById('modal-mask').style.display = 'none'; + document.getElementById("reassign_group_div").style.display = "none"; + document.body.classList.remove('modal-open'); + } + + function reassign_group() { + var ids = []; + var group = (document.querySelector('#reassign_group_custom input[type="hidden"]')?.value || "default"); + var visibleContainer = document.querySelector('#cbi-passwall-nodes > .cbi-tabcontainer[style*="display:block"], #cbi-passwall-nodes > .cbi-tabcontainer[style*="display: block"]'); + if (!visibleContainer) return; + var doms = visibleContainer.getElementsByClassName("nodes_select"); + if (doms && doms.length > 0) { + for (var i = 0 ; i < doms.length; i++) { + if (doms[i].checked) { + ids.push(doms[i].getAttribute("cbid")) + } + } + if (ids.length > 0) { + XHR.get('<%=api.url("reassign_group")%>', { + group: group, + ids: ids.join(",") + }, + function(x, data) { + if (x && x.status == 200) { + window.location.href = '<%=api.url("node_list")%>'; + } + else { + alert("<%:Error%>"); + } + }); + } + } + } + function add_new_node() { window.location.href = '<%=api.url("add_node")%>?redirect=1'; } //自定义分组下拉列表事件 - document.addEventListener("DOMContentLoaded", function() { - var dropdown = document.getElementById("addlink_group_custom"); + function dropdown_list_fun(div_id) { + var dropdown = document.getElementById(div_id); if (!dropdown) return; var display = dropdown.querySelector(".selected-display"); @@ -99,7 +159,7 @@ local api = require "luci.passwall.api" display.addEventListener("click", function() { list.style.display = list.style.display === "none" ? "block" : "none"; input.value = ""; - input.focus(); + //input.focus(); }); function selectItem(li) { @@ -119,7 +179,8 @@ local api = require "luci.passwall.api" }); input.addEventListener("keydown", function(e){ - if (e.keyCode !== 13) return; + var isEnter = e.key === "Enter" || e.keyCode === 13; + if (!isEnter) return; e.stopPropagation(); e.preventDefault(); @@ -135,7 +196,7 @@ local api = require "luci.passwall.api" } var li = Array.from(list.querySelectorAll(".dropdown-item")).find(function(el){ - return el.dataset.value.toLowerCase() === val.toLowerCase(); + return el.dataset.value && el.dataset.value.toLowerCase() === val.toLowerCase(); }); if (!li) { li = document.createElement("li"); @@ -176,11 +237,20 @@ local api = require "luci.passwall.api" list.style.display = "none"; } }); + } + + document.addEventListener("DOMContentLoaded", function() { + dropdown_list_fun("addlink_group_custom"); }); + document.addEventListener("DOMContentLoaded", function() { + dropdown_list_fun("reassign_group_custom"); + }); //]]> +
+