mirror of
https://github.com/bolucat/Archive.git
synced 2025-12-24 13:28:37 +08:00
Update On Thu Dec 4 19:44:09 CET 2025
This commit is contained in:
1
.github/update.log
vendored
1
.github/update.log
vendored
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -164,10 +166,14 @@ type BasicOption struct {
|
||||
MPTCP bool `proxy:"mptcp,omitempty"`
|
||||
Interface string `proxy:"interface-name,omitempty"`
|
||||
RoutingMark int `proxy:"routing-mark,omitempty"`
|
||||
IPVersion string `proxy:"ip-version,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
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ var ErrNotSupport = errors.New("no support")
|
||||
|
||||
type Connection interface {
|
||||
Chains() Chain
|
||||
ProviderChains() Chain
|
||||
AppendToChains(adapter ProxyAdapter)
|
||||
RemoteDestination() string
|
||||
}
|
||||
@@ -108,6 +109,7 @@ type ProxyInfo struct {
|
||||
SMUX bool
|
||||
Interface string
|
||||
RoutingMark int
|
||||
ProviderName string
|
||||
DialerProxy string
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -164,10 +166,14 @@ type BasicOption struct {
|
||||
MPTCP bool `proxy:"mptcp,omitempty"`
|
||||
Interface string `proxy:"interface-name,omitempty"`
|
||||
RoutingMark int `proxy:"routing-mark,omitempty"`
|
||||
IPVersion string `proxy:"ip-version,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
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ var ErrNotSupport = errors.New("no support")
|
||||
|
||||
type Connection interface {
|
||||
Chains() Chain
|
||||
ProviderChains() Chain
|
||||
AppendToChains(adapter ProxyAdapter)
|
||||
RemoteDestination() string
|
||||
}
|
||||
@@ -108,6 +109,7 @@ type ProxyInfo struct {
|
||||
SMUX bool
|
||||
Interface string
|
||||
RoutingMark int
|
||||
ProviderName string
|
||||
DialerProxy string
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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 &")
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
//]]>
|
||||
</script>
|
||||
|
||||
<div id="modal-mask"></div>
|
||||
|
||||
<div id="add_link_div">
|
||||
<div id="add_link_modal_container">
|
||||
<h3><%:Add the node via the link%></h3>
|
||||
@@ -199,7 +269,7 @@ local api = require "luci.passwall.api"
|
||||
<ul class="dropdown-list" style="display:none;">
|
||||
<li class="dropdown-item" data-value=""><%:default%></li>
|
||||
<li class="dropdown-item custom-input">
|
||||
<input type="text" placeholder="-- <%:custom%> --" class="create-item-input">
|
||||
<input type="text" placeholder="-- <%:custom%> --" class="create-item-input" inputmode="text" enterkeyhint="done">
|
||||
</li>
|
||||
</ul>
|
||||
<input type="hidden" name="addlink_group" value="">
|
||||
@@ -213,6 +283,36 @@ local api = require "luci.passwall.api"
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="reassign_group_div">
|
||||
<div id="reassign_group_modal_container">
|
||||
<h3><%:Reassign Node Group%></h3>
|
||||
<div class="value-custom">
|
||||
<div class="value-field-custom">
|
||||
<label class="value-title-custom" for="reassign_group_custom"><%:Group Name%></label>
|
||||
<div id="reassign_group_custom" class="custom-dropdown">
|
||||
<div class="selected-display">
|
||||
<span class="text"><%:default%></span>
|
||||
<span class="arrow">▾</span>
|
||||
</div>
|
||||
<ul class="dropdown-list" style="display:none;">
|
||||
<li class="dropdown-item" data-value=""><%:default%></li>
|
||||
<li class="dropdown-item custom-input">
|
||||
<input type="text" placeholder="-- <%:custom%> --" class="create-item-input" inputmode="text" enterkeyhint="done">
|
||||
</li>
|
||||
</ul>
|
||||
<input type="hidden" name="to_group" value="">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="reassign_group_button_container">
|
||||
<input class="btn cbi-button cbi-button-edit" type="button" onclick="reassign_group()" value="<%:Save%>" />
|
||||
<input class="btn cbi-button cbi-button-remove" type="button" onclick="close_reassign_group_div()" value="<%:Close%>" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="pw-toolbar">
|
||||
<div class="pw-toolbar-field">
|
||||
<input class="btn cbi-button cbi-button-add" type="button" onclick="add_new_node()" value="<%:Add%>" />
|
||||
@@ -220,9 +320,10 @@ local api = require "luci.passwall.api"
|
||||
<input class="btn cbi-button cbi-button-remove" type="button" onclick="clear_all_nodes()" value="<%:Clear all nodes%>" />
|
||||
<input class="btn cbi-button cbi-button-remove" type="button" onclick="delete_select_nodes()" value="<%:Delete select nodes%>" />
|
||||
<input class="btn cbi-button cbi-button-edit" type="button" id="select_all_btn" onclick="checked_all_node(this)" value="<%:Select all%>" />
|
||||
<input class="btn cbi-button cbi-button-apply" type="submit" name="cbi.apply" value="<%:Save & Apply%>" />
|
||||
<input class="btn cbi-button cbi-button-save" type="submit" name="cbi.save" value="<%:Save%>" />
|
||||
<input class="btn cbi-button cbi-button-reset" type="button" value="<%:Reset%>" onclick="location.href='<%=REQUEST_URI%>'" />
|
||||
<input class="btn cbi-button cbi-button-edit" type="button" onclick="open_reassign_group_div()" value="<%:Reassign Group%>" />
|
||||
<!--<input class="btn cbi-button cbi-button-apply" type="submit" name="cbi.apply" value="<%:Save & Apply%>" />-->
|
||||
<!--<input class="btn cbi-button cbi-button-save" type="submit" name="cbi.save" value="<%:Save%>" />-->
|
||||
<!--<input class="btn cbi-button cbi-button-reset" type="button" value="<%:Reset%>" onclick="location.href='<%=REQUEST_URI%>'" />-->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -239,7 +340,28 @@ local api = require "luci.passwall.api"
|
||||
padding: 5px 0 5px;
|
||||
}
|
||||
|
||||
#add_link_div {
|
||||
#modal-mask {
|
||||
display: none;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background: rgba(0,0,0,0.4);
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
body.modal-open {
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
body.modal-open #add_link_div,
|
||||
body.modal-open #reassign_group_div {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
#add_link_div, #reassign_group_div {
|
||||
display: none;
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
@@ -254,7 +376,7 @@ local api = require "luci.passwall.api"
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
#add_link_modal_container {
|
||||
#add_link_modal_container, #reassign_group_modal_container {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
@@ -281,7 +403,7 @@ local api = require "luci.passwall.api"
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#add_link_button_container {
|
||||
#add_link_button_container, #reassign_group_button_container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
|
||||
@@ -2008,5 +2008,11 @@ msgstr "配置的类型同样适用于手动导入节点时所指定的核心程
|
||||
msgid "Group Name"
|
||||
msgstr "分组名"
|
||||
|
||||
msgid "Reassign Group"
|
||||
msgstr "调整分组"
|
||||
|
||||
msgid "Reassign Node Group"
|
||||
msgstr "调整节点分组"
|
||||
|
||||
msgid "Currently using %s node"
|
||||
msgstr "当前使用的 %s 节点"
|
||||
|
||||
@@ -40,7 +40,7 @@ RUN case "$TARGETARCH" in \
|
||||
&& RUSTFLAGS="-C linker=$CC" CC=$CC cargo build --target "$RUST_TARGET" --release --features "full" \
|
||||
&& mv target/$RUST_TARGET/release/ss* target/release/
|
||||
|
||||
FROM alpine:3.22 AS sslocal
|
||||
FROM alpine:3.23 AS sslocal
|
||||
|
||||
# NOTE: Please be careful to change the path of these binaries, refer to #1149 for more information.
|
||||
COPY --from=builder /root/shadowsocks-rust/target/release/sslocal /usr/bin/
|
||||
@@ -50,7 +50,7 @@ COPY --from=builder /root/shadowsocks-rust/docker/docker-entrypoint.sh /usr/bin/
|
||||
ENTRYPOINT [ "docker-entrypoint.sh" ]
|
||||
CMD [ "sslocal", "--log-without-time", "-c", "/etc/shadowsocks-rust/config.json" ]
|
||||
|
||||
FROM alpine:3.22 AS ssserver
|
||||
FROM alpine:3.23 AS ssserver
|
||||
|
||||
COPY --from=builder /root/shadowsocks-rust/target/release/ssserver /usr/bin/
|
||||
COPY --from=builder /root/shadowsocks-rust/examples/config.json /etc/shadowsocks-rust/
|
||||
|
||||
@@ -124,8 +124,8 @@ const health_checkurls = [
|
||||
|
||||
const inbound_type = [
|
||||
['http', _('HTTP') + ' - ' + _('TCP')],
|
||||
['socks', _('SOCKS') + ' - ' + _('TCP')],
|
||||
['mixed', _('Mixed') + ' - ' + _('TCP')],
|
||||
['socks', _('SOCKS') + ' - ' + _('TCP/UDP')],
|
||||
['mixed', _('Mixed') + ' - ' + _('TCP/UDP')],
|
||||
['shadowsocks', _('Shadowsocks') + ' - ' + _('TCP/UDP')],
|
||||
['mieru', _('Mieru') + ' - ' + _('TCP/UDP')],
|
||||
['sudoku', _('Sudoku') + ' - ' + _('TCP')],
|
||||
@@ -156,7 +156,7 @@ const load_balance_strategy = [
|
||||
const outbound_type = [
|
||||
['direct', _('DIRECT') + ' - ' + _('TCP/UDP')],
|
||||
['http', _('HTTP') + ' - ' + _('TCP')],
|
||||
['socks5', _('SOCKS5') + ' - ' + _('TCP')],
|
||||
['socks5', _('SOCKS5') + ' - ' + _('TCP/UDP')],
|
||||
['ss', _('Shadowsocks') + ' - ' + _('TCP/UDP')],
|
||||
//['ssr', _('ShadowsocksR')], // Deprecated
|
||||
['mieru', _('Mieru') + ' - ' + _('TCP/UDP')],
|
||||
|
||||
@@ -21,13 +21,13 @@ define Download/geoip
|
||||
HASH:=2445b44d9ae3ab9a867c9d1e0e244646c4c378622e14b9afaf3658ecf46a40b9
|
||||
endef
|
||||
|
||||
GEOSITE_VER:=20251203115157
|
||||
GEOSITE_VER:=20251204062603
|
||||
GEOSITE_FILE:=dlc.dat.$(GEOSITE_VER)
|
||||
define Download/geosite
|
||||
URL:=https://github.com/v2fly/domain-list-community/releases/download/$(GEOSITE_VER)/
|
||||
URL_FILE:=dlc.dat
|
||||
FILE:=$(GEOSITE_FILE)
|
||||
HASH:=0a14ebf9162fd0e3486b15a40025b9c807d2d38dd4379830edbbb113ee75ea80
|
||||
HASH:=3355697a91bb72c485aba8fb1748c202573da19b42f4cb050d1a72594411c34d
|
||||
endef
|
||||
|
||||
GEOSITE_IRAN_VER:=202512010051
|
||||
|
||||
4
v2rayn/.github/workflows/build-linux.yml
vendored
4
v2rayn/.github/workflows/build-linux.yml
vendored
@@ -31,7 +31,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6.0.0
|
||||
uses: actions/checkout@v6.0.1
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
fetch-depth: '0'
|
||||
@@ -110,7 +110,7 @@ jobs:
|
||||
dnf -y install sudo git rpm-build rpmdevtools dnf-plugins-core rsync findutils tar gzip unzip which
|
||||
|
||||
- name: Checkout repo (for scripts)
|
||||
uses: actions/checkout@v6.0.0
|
||||
uses: actions/checkout@v6.0.1
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
fetch-depth: '0'
|
||||
|
||||
2
v2rayn/.github/workflows/build-osx.yml
vendored
2
v2rayn/.github/workflows/build-osx.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6.0.0
|
||||
uses: actions/checkout@v6.0.1
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
fetch-depth: '0'
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6.0.0
|
||||
uses: actions/checkout@v6.0.1
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
fetch-depth: '0'
|
||||
|
||||
2
v2rayn/.github/workflows/build-windows.yml
vendored
2
v2rayn/.github/workflows/build-windows.yml
vendored
@@ -27,7 +27,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6.0.0
|
||||
uses: actions/checkout@v6.0.1
|
||||
|
||||
- name: Setup
|
||||
uses: actions/setup-dotnet@v5.0.1
|
||||
|
||||
@@ -1871,6 +1871,7 @@ The following extractors use this feature:
|
||||
* `pot_trace`: Enable debug logging for PO Token fetching. Either `true` or `false` (default)
|
||||
* `fetch_pot`: Policy to use for fetching a PO Token from providers. One of `always` (always try fetch a PO Token regardless if the client requires one for the given context), `never` (never fetch a PO Token), or `auto` (default; only fetch a PO Token if the client requires one for the given context)
|
||||
* `jsc_trace`: Enable debug logging for JS Challenge fetching. Either `true` or `false` (default)
|
||||
* `use_ad_playback_context`: Skip preroll ads to eliminate the mandatory wait period before download. Do NOT use this when passing premium account cookies to yt-dlp, as it will result in a loss of premium formats. Only effective with the `web`, `web_safari`, `web_music` and `mweb` player clients. Either `true` or `false` (default)
|
||||
|
||||
#### youtube-ejs
|
||||
* `jitless`: Run suported Javascript engines in JIT-less mode. Supported runtimes are `deno`, `node` and `bun`. Provides better security at the cost of performance/speed. Do note that `node` and `bun` are still considered unsecure. Either `true` or `false` (default)
|
||||
|
||||
@@ -704,6 +704,24 @@ class YoutubeWebArchiveIE(InfoExtractor):
|
||||
'thumbnail': 'https://web.archive.org/web/20160108040020if_/https://i.ytimg.com/vi/SQCom7wjGDs/maxresdefault.jpg',
|
||||
'upload_date': '20160107',
|
||||
},
|
||||
}, {
|
||||
# dmuxed formats
|
||||
'url': 'https://web.archive.org/web/20240922160632/https://www.youtube.com/watch?v=z7hzvTL3k1k',
|
||||
'info_dict': {
|
||||
'id': 'z7hzvTL3k1k',
|
||||
'ext': 'webm',
|
||||
'title': 'Praise the Lord and Pass the Ammunition (BARRXN REMIX)',
|
||||
'description': 'md5:45dbf2c71c23b0734c8dfb82dd1e94b6',
|
||||
'uploader': 'Barrxn',
|
||||
'uploader_id': 'TheRockstar6086',
|
||||
'uploader_url': 'https://www.youtube.com/user/TheRockstar6086',
|
||||
'channel_id': 'UCjJPGUTtvR9uizmawn2ThqA',
|
||||
'channel_url': 'https://www.youtube.com/channel/UCjJPGUTtvR9uizmawn2ThqA',
|
||||
'duration': 125,
|
||||
'thumbnail': r're:https?://.*\.(jpg|webp)',
|
||||
'upload_date': '20201207',
|
||||
},
|
||||
'params': {'format': 'bv'},
|
||||
}, {
|
||||
'url': 'https://web.archive.org/web/http://www.youtube.com/watch?v=kH-G_aIBlFw',
|
||||
'only_matching': True,
|
||||
@@ -1060,6 +1078,19 @@ class YoutubeWebArchiveIE(InfoExtractor):
|
||||
capture_dates.extend([self._OLDEST_CAPTURE_DATE, self._NEWEST_CAPTURE_DATE])
|
||||
return orderedSet(filter(None, capture_dates))
|
||||
|
||||
def _parse_fmt(self, fmt, extra_info=None):
|
||||
format_id = traverse_obj(fmt, ('url', {parse_qs}, 'itag', 0))
|
||||
return {
|
||||
'format_id': format_id,
|
||||
**self._FORMATS.get(format_id, {}),
|
||||
**traverse_obj(fmt, {
|
||||
'url': ('url', {lambda x: f'https://web.archive.org/web/2id_/{x}'}),
|
||||
'ext': ('ext', {str}),
|
||||
'filesize': ('url', {parse_qs}, 'clen', 0, {int_or_none}),
|
||||
}),
|
||||
**(extra_info or {}),
|
||||
}
|
||||
|
||||
def _real_extract(self, url):
|
||||
video_id, url_date, url_date_2 = self._match_valid_url(url).group('id', 'date', 'date2')
|
||||
url_date = url_date or url_date_2
|
||||
@@ -1090,17 +1121,14 @@ class YoutubeWebArchiveIE(InfoExtractor):
|
||||
info['thumbnails'] = self._extract_thumbnails(video_id)
|
||||
|
||||
formats = []
|
||||
if video_info.get('dmux'):
|
||||
for vf in traverse_obj(video_info, ('formats', 'video', lambda _, v: url_or_none(v['url']))):
|
||||
formats.append(self._parse_fmt(vf, {'acodec': 'none'}))
|
||||
for af in traverse_obj(video_info, ('formats', 'audio', lambda _, v: url_or_none(v['url']))):
|
||||
formats.append(self._parse_fmt(af, {'vcodec': 'none'}))
|
||||
else:
|
||||
for fmt in traverse_obj(video_info, ('formats', lambda _, v: url_or_none(v['url']))):
|
||||
format_id = traverse_obj(fmt, ('url', {parse_qs}, 'itag', 0))
|
||||
formats.append({
|
||||
'format_id': format_id,
|
||||
**self._FORMATS.get(format_id, {}),
|
||||
**traverse_obj(fmt, {
|
||||
'url': ('url', {lambda x: f'https://web.archive.org/web/2id_/{x}'}),
|
||||
'ext': ('ext', {str}),
|
||||
'filesize': ('url', {parse_qs}, 'clen', 0, {int_or_none}),
|
||||
}),
|
||||
})
|
||||
formats.append(self._parse_fmt(fmt))
|
||||
info['formats'] = formats
|
||||
|
||||
return info
|
||||
|
||||
@@ -104,6 +104,7 @@ INNERTUBE_CLIENTS = {
|
||||
},
|
||||
'INNERTUBE_CONTEXT_CLIENT_NAME': 1,
|
||||
'SUPPORTS_COOKIES': True,
|
||||
'SUPPORTS_AD_PLAYBACK_CONTEXT': True,
|
||||
**WEB_PO_TOKEN_POLICIES,
|
||||
},
|
||||
# Safari UA returns pre-merged video+audio 144p/240p/360p/720p/1080p HLS formats
|
||||
@@ -117,6 +118,7 @@ INNERTUBE_CLIENTS = {
|
||||
},
|
||||
'INNERTUBE_CONTEXT_CLIENT_NAME': 1,
|
||||
'SUPPORTS_COOKIES': True,
|
||||
'SUPPORTS_AD_PLAYBACK_CONTEXT': True,
|
||||
**WEB_PO_TOKEN_POLICIES,
|
||||
},
|
||||
'web_embedded': {
|
||||
@@ -157,6 +159,7 @@ INNERTUBE_CLIENTS = {
|
||||
),
|
||||
},
|
||||
'SUPPORTS_COOKIES': True,
|
||||
'SUPPORTS_AD_PLAYBACK_CONTEXT': True,
|
||||
},
|
||||
# This client now requires sign-in for every video
|
||||
'web_creator': {
|
||||
@@ -313,6 +316,7 @@ INNERTUBE_CLIENTS = {
|
||||
),
|
||||
},
|
||||
'SUPPORTS_COOKIES': True,
|
||||
'SUPPORTS_AD_PLAYBACK_CONTEXT': True,
|
||||
},
|
||||
'tv': {
|
||||
'INNERTUBE_CONTEXT': {
|
||||
@@ -412,6 +416,7 @@ def build_innertube_clients():
|
||||
ytcfg.setdefault('SUBS_PO_TOKEN_POLICY', SubsPoTokenPolicy())
|
||||
ytcfg.setdefault('REQUIRE_AUTH', False)
|
||||
ytcfg.setdefault('SUPPORTS_COOKIES', False)
|
||||
ytcfg.setdefault('SUPPORTS_AD_PLAYBACK_CONTEXT', False)
|
||||
ytcfg.setdefault('PLAYER_PARAMS', None)
|
||||
ytcfg.setdefault('AUTHENTICATED_USER_AGENT', None)
|
||||
ytcfg['INNERTUBE_CONTEXT']['client'].setdefault('hl', 'en')
|
||||
|
||||
@@ -2629,16 +2629,23 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
||||
return {'contentCheckOk': True, 'racyCheckOk': True}
|
||||
|
||||
@classmethod
|
||||
def _generate_player_context(cls, sts=None):
|
||||
def _generate_player_context(cls, sts=None, use_ad_playback_context=False):
|
||||
context = {
|
||||
'html5Preference': 'HTML5_PREF_WANTS',
|
||||
}
|
||||
if sts is not None:
|
||||
context['signatureTimestamp'] = sts
|
||||
return {
|
||||
'playbackContext': {
|
||||
|
||||
playback_context = {
|
||||
'contentPlaybackContext': context,
|
||||
},
|
||||
}
|
||||
if use_ad_playback_context:
|
||||
playback_context['adPlaybackContext'] = {
|
||||
'pyv': True,
|
||||
}
|
||||
|
||||
return {
|
||||
'playbackContext': playback_context,
|
||||
**cls._get_checkok_params(),
|
||||
}
|
||||
|
||||
@@ -2866,7 +2873,13 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
||||
yt_query['serviceIntegrityDimensions'] = {'poToken': po_token}
|
||||
|
||||
sts = self._extract_signature_timestamp(video_id, player_url, webpage_ytcfg, fatal=False) if player_url else None
|
||||
yt_query.update(self._generate_player_context(sts))
|
||||
|
||||
use_ad_playback_context = (
|
||||
self._configuration_arg('use_ad_playback_context', ['false'])[0] != 'false'
|
||||
and traverse_obj(INNERTUBE_CLIENTS, (client, 'SUPPORTS_AD_PLAYBACK_CONTEXT', {bool})))
|
||||
|
||||
yt_query.update(self._generate_player_context(sts, use_ad_playback_context))
|
||||
|
||||
return self._extract_response(
|
||||
item_id=video_id, ep='player', query=yt_query,
|
||||
ytcfg=player_ytcfg, headers=headers, fatal=True,
|
||||
|
||||
Reference in New Issue
Block a user