Update On Sat May 24 20:33:34 CEST 2025

This commit is contained in:
github-action[bot]
2025-05-24 20:33:34 +02:00
parent fb810787a1
commit 8a13333e48
63 changed files with 76727 additions and 22350 deletions
+1
View File
@@ -1008,3 +1008,4 @@ Update On Tue May 20 20:37:19 CEST 2025
Update On Wed May 21 20:37:50 CEST 2025
Update On Thu May 22 20:37:12 CEST 2025
Update On Fri May 23 20:34:50 CEST 2025
Update On Sat May 24 20:33:25 CEST 2025
@@ -1,10 +1,12 @@
package outbound
import (
"context"
"encoding/base64"
"fmt"
"github.com/metacubex/mihomo/component/ech"
"github.com/metacubex/mihomo/component/resolver"
)
type ECHOptions struct {
@@ -22,7 +24,13 @@ func (o ECHOptions) Parse() (*ech.Config, error) {
if err != nil {
return nil, fmt.Errorf("base64 decode ech config string failed: %v", err)
}
echConfig.EncryptedClientHelloConfigList = list
echConfig.GetEncryptedClientHelloConfigList = func(ctx context.Context, serverName string) ([]byte, error) {
return list, nil
}
} else {
echConfig.GetEncryptedClientHelloConfigList = func(ctx context.Context, serverName string) ([]byte, error) {
return resolver.ResolveECHWithResolver(ctx, serverName, resolver.ProxyServerHostResolver)
}
}
return echConfig, nil
}
@@ -34,8 +34,8 @@ func (l *handleContextListener) init() {
}
}
}()
if c, err := l.handle(l.ctx, c); err == nil {
l.conns <- c
if conn, err := l.handle(l.ctx, c); err == nil {
l.conns <- conn
} else {
// handle failed, close the underlying connection.
_ = c.Close()
@@ -4,24 +4,20 @@ import (
"context"
"fmt"
"github.com/metacubex/mihomo/component/resolver"
tlsC "github.com/metacubex/mihomo/component/tls"
)
type Config struct {
EncryptedClientHelloConfigList []byte
GetEncryptedClientHelloConfigList func(ctx context.Context, serverName string) ([]byte, error)
}
func (cfg *Config) ClientHandle(ctx context.Context, tlsConfig *tlsC.Config) (err error) {
if cfg == nil {
return nil
}
echConfigList := cfg.EncryptedClientHelloConfigList
if len(echConfigList) == 0 {
echConfigList, err = resolver.ResolveECH(ctx, tlsConfig.ServerName)
if err != nil {
return fmt.Errorf("resolve ECH config error: %w", err)
}
echConfigList, err := cfg.GetEncryptedClientHelloConfigList(ctx, tlsConfig.ServerName)
if err != nil {
return fmt.Errorf("resolve ECH config error: %w", err)
}
tlsConfig.EncryptedClientHelloConfigList = echConfigList
@@ -26,8 +26,9 @@ var (
)
type ifaceCache struct {
ifMap map[string]*Interface
ifTable bart.Table[*Interface]
ifMapByName map[string]*Interface
ifMapByAddr map[netip.Addr]*Interface
ifTable bart.Table[*Interface]
}
var caches = singledo.NewSingle[*ifaceCache](time.Second * 20)
@@ -40,7 +41,8 @@ func getCache() (*ifaceCache, error) {
}
cache := &ifaceCache{
ifMap: make(map[string]*Interface),
ifMapByName: make(map[string]*Interface),
ifMapByAddr: make(map[netip.Addr]*Interface),
}
for _, iface := range ifaces {
@@ -78,12 +80,13 @@ func getCache() (*ifaceCache, error) {
Flags: iface.Flags,
Addresses: ipNets,
}
cache.ifMap[iface.Name] = ifaceObj
cache.ifMapByName[iface.Name] = ifaceObj
if iface.Flags&net.FlagUp == 0 {
continue // interface down
}
for _, prefix := range ipNets {
cache.ifMapByAddr[prefix.Addr()] = ifaceObj
cache.ifTable.Insert(prefix, ifaceObj)
}
}
@@ -98,7 +101,7 @@ func Interfaces() (map[string]*Interface, error) {
if err != nil {
return nil, err
}
return cache.ifMap, nil
return cache.ifMapByName, nil
}
func ResolveInterface(name string) (*Interface, error) {
@@ -120,6 +123,11 @@ func ResolveInterfaceByAddr(addr netip.Addr) (*Interface, error) {
if err != nil {
return nil, err
}
// maybe two interfaces have the same prefix but different address
// so direct check address equal before do a route lookup (longest prefix match)
if iface, ok := cache.ifMapByAddr[addr]; ok {
return iface, nil
}
iface, ok := cache.ifTable.Lookup(addr)
if !ok {
return nil, ErrIfaceNotFound
@@ -133,7 +141,8 @@ func IsLocalIp(addr netip.Addr) (bool, error) {
if err != nil {
return false, err
}
return cache.ifTable.Contains(addr), nil
_, ok := cache.ifMapByAddr[addr]
return ok, nil
}
func FlushCache() {
@@ -4,6 +4,7 @@ import (
"context"
"net"
"net/http"
"runtime/debug"
"time"
N "github.com/metacubex/mihomo/common/net"
@@ -63,6 +64,7 @@ func NewListenerForHttps(l net.Listener, httpServer *http.Server, tlsConfig *Con
}
return c, nil
}, func(a any) {
log.Errorln("https server panic: %s", a)
stack := debug.Stack()
log.Errorln("https server panic: %s\n%s", a, stack)
})
}
@@ -25,13 +25,13 @@ require (
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759
github.com/metacubex/quic-go v0.52.1-0.20250522021943-aef454b9e639
github.com/metacubex/randv2 v0.2.0
github.com/metacubex/sing v0.5.3-0.20250504031621-1f99e54c15b7
github.com/metacubex/sing v0.5.3
github.com/metacubex/sing-mux v0.3.2
github.com/metacubex/sing-quic v0.0.0-20250520025433-6e556a6bef7a
github.com/metacubex/sing-quic v0.0.0-20250523120938-f1a248e5ec7f
github.com/metacubex/sing-shadowsocks v0.2.9
github.com/metacubex/sing-shadowsocks2 v0.2.3
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2
github.com/metacubex/sing-tun v0.4.6-0.20250503065609-efb9f0beb6f6
github.com/metacubex/sing-tun v0.4.6-0.20250524142129-9d110c0af70c
github.com/metacubex/sing-vmess v0.2.1
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f
github.com/metacubex/smux v0.0.0-20250503055512-501391591dee
@@ -116,20 +116,20 @@ github.com/metacubex/quic-go v0.52.1-0.20250522021943-aef454b9e639/go.mod h1:Kc6
github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs=
github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY=
github.com/metacubex/sing v0.5.2/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w=
github.com/metacubex/sing v0.5.3-0.20250504031621-1f99e54c15b7 h1:m4nSxvw46JEgxMzzmnXams+ebwabcry4Ydep/zNiesQ=
github.com/metacubex/sing v0.5.3-0.20250504031621-1f99e54c15b7/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w=
github.com/metacubex/sing v0.5.3 h1:QWdN16WFKMk06x4nzkc8SvZ7y2x+TLQrpkPoHs+WSVM=
github.com/metacubex/sing v0.5.3/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w=
github.com/metacubex/sing-mux v0.3.2 h1:nJv52pyRivHcaZJKk2JgxpaVvj1GAXG81scSa9N7ncw=
github.com/metacubex/sing-mux v0.3.2/go.mod h1:3rt1soewn0O6j89GCLmwAQFsq257u0jf2zQSPhTL3Bw=
github.com/metacubex/sing-quic v0.0.0-20250520025433-6e556a6bef7a h1:Ho73vGiB94LmtK5T+tKVwtCNEi/YiHmPjlqpHSAmAVs=
github.com/metacubex/sing-quic v0.0.0-20250520025433-6e556a6bef7a/go.mod h1:JPTpf7fpnojsSuwRJExhSZSy63pVbp3VM39+zj+sAJM=
github.com/metacubex/sing-quic v0.0.0-20250523120938-f1a248e5ec7f h1:mP3vIm+9hRFI0C0Vl3pE0NESF/L85FDbuB0tGgUii6I=
github.com/metacubex/sing-quic v0.0.0-20250523120938-f1a248e5ec7f/go.mod h1:JPTpf7fpnojsSuwRJExhSZSy63pVbp3VM39+zj+sAJM=
github.com/metacubex/sing-shadowsocks v0.2.9 h1:2e++13WNN7EGjGtvrGLUzW1xrCdQbW2gIFpgw5GEw00=
github.com/metacubex/sing-shadowsocks v0.2.9/go.mod h1:CJSEGO4FWQAWe+ZiLZxCweGdjRR60A61SIoVjdjQeBA=
github.com/metacubex/sing-shadowsocks2 v0.2.3 h1:v3rNS/5Ywh0NIZ6VU/NmdERQIN5RePzyxCFeQsU4Cx0=
github.com/metacubex/sing-shadowsocks2 v0.2.3/go.mod h1:/WNy/Q8ahLCoPRriWuFZFD0Jy+JNp1MEQl28Zw6SaF8=
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 h1:gXU+MYPm7Wme3/OAY2FFzVq9d9GxPHOqu5AQfg/ddhI=
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2/go.mod h1:mbfboaXauKJNIHJYxQRa+NJs4JU9NZfkA+I33dS2+9E=
github.com/metacubex/sing-tun v0.4.6-0.20250503065609-efb9f0beb6f6 h1:TAwL91XPa6x1QK55CRm+VTzPvLPUfEr/uFDnOZArqEU=
github.com/metacubex/sing-tun v0.4.6-0.20250503065609-efb9f0beb6f6/go.mod h1:HDaHDL6onAX2ZGbAGUXKp++PohRdNb7Nzt6zxzhox+U=
github.com/metacubex/sing-tun v0.4.6-0.20250524142129-9d110c0af70c h1:Y6jk7AH5BEg9Dsvczrf/KokYsvxeKSZZlCLHg+hC4ro=
github.com/metacubex/sing-tun v0.4.6-0.20250524142129-9d110c0af70c/go.mod h1:HDaHDL6onAX2ZGbAGUXKp++PohRdNb7Nzt6zxzhox+U=
github.com/metacubex/sing-vmess v0.2.1 h1:I6gM3VUjtvJ15D805EUbNH+SRBuqzJeFnuIbKYUsWZ0=
github.com/metacubex/sing-vmess v0.2.1/go.mod h1:DsODWItJtOMZNna8Qhheg8r3tUivrcO3vWgaTYKnfTo=
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f h1:Sr/DYKYofKHKc4GF3qkRGNuj6XA6c0eqPgEDN+VAsYU=
@@ -7,6 +7,7 @@ import (
"errors"
"fmt"
"net"
"runtime/debug"
"time"
N "github.com/metacubex/mihomo/common/net"
@@ -89,7 +90,8 @@ func (b Builder) NewListener(l net.Listener) net.Listener {
// We fixed it by calling Close() directly.
return realityConnWrapper{c}, nil
}, func(a any) {
log.Errorln("reality server panic: %s", a)
stack := debug.Stack()
log.Errorln("reality server panic: %s\n%s", a, stack)
})
}
@@ -16,6 +16,7 @@ import (
mux "github.com/metacubex/sing-mux"
vmess "github.com/metacubex/sing-vmess"
"github.com/metacubex/sing/common"
"github.com/metacubex/sing/common/buf"
"github.com/metacubex/sing/common/bufio"
"github.com/metacubex/sing/common/bufio/deadline"
@@ -146,11 +147,11 @@ func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, meta
func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.PacketConn, metadata M.Metadata) error {
defer func() { _ = conn.Close() }()
mutex := sync.Mutex{}
conn2 := bufio.NewNetPacketConn(conn) // a new interface to set nil in defer
writer := bufio.NewNetPacketWriter(conn) // a new interface to set nil in defer
defer func() {
mutex.Lock() // this goroutine must exit after all conn.WritePacket() is not running
defer mutex.Unlock()
conn2 = nil
writer = nil
}()
rwOptions := network.ReadWaitOptions{}
readWaiter, isReadWaiter := bufio.CreatePacketReadWaiter(conn)
@@ -180,32 +181,56 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.
return err
}
cPacket := &packet{
conn: &conn2,
mutex: &mutex,
rAddr: metadata.Source.UDPAddr(),
lAddr: conn.LocalAddr(),
buff: buff,
writer: &writer,
mutex: &mutex,
rAddr: metadata.Source.UDPAddr(),
lAddr: conn.LocalAddr(),
buff: buff,
}
cMetadata := &C.Metadata{
NetWork: C.UDP,
Type: h.Type,
}
if metadata.Source.IsIP() && metadata.Source.Fqdn == "" {
cMetadata.RawSrcAddr = metadata.Source.Unwrap().UDPAddr()
}
if dest.IsIP() && dest.Fqdn == "" {
cMetadata.RawDstAddr = dest.Unwrap().UDPAddr()
}
inbound.ApplyAdditions(cMetadata, inbound.WithDstAddr(dest), inbound.WithSrcAddr(metadata.Source), inbound.WithInAddr(conn.LocalAddr()))
inbound.ApplyAdditions(cMetadata, h.Additions...)
inbound.ApplyAdditions(cMetadata, getAdditions(ctx)...)
h.Tunnel.HandleUDPPacket(cPacket, cMetadata)
h.handlePacket(ctx, cPacket, metadata.Source, dest)
}
return nil
}
type localAddr interface {
LocalAddr() net.Addr
}
func (h *ListenerHandler) NewPacket(ctx context.Context, key netip.AddrPort, buffer *buf.Buffer, metadata M.Metadata, init func(natConn network.PacketConn) network.PacketWriter) {
writer := bufio.NewNetPacketWriter(init(nil))
mutex := sync.Mutex{}
cPacket := &packet{
writer: &writer,
mutex: &mutex,
rAddr: metadata.Source.UDPAddr(),
buff: buffer,
}
if conn, ok := common.Cast[localAddr](writer); ok {
cPacket.rAddr = conn.LocalAddr()
} else {
cPacket.rAddr = metadata.Source.UDPAddr() // tun does not have real inAddr
}
h.handlePacket(ctx, cPacket, metadata.Source, metadata.Destination)
}
func (h *ListenerHandler) handlePacket(ctx context.Context, cPacket *packet, source M.Socksaddr, destination M.Socksaddr) {
cMetadata := &C.Metadata{
NetWork: C.UDP,
Type: h.Type,
}
if source.IsIP() && source.Fqdn == "" {
cMetadata.RawSrcAddr = source.Unwrap().UDPAddr()
}
if destination.IsIP() && destination.Fqdn == "" {
cMetadata.RawDstAddr = destination.Unwrap().UDPAddr()
}
inbound.ApplyAdditions(cMetadata, inbound.WithDstAddr(destination), inbound.WithSrcAddr(source), inbound.WithInAddr(cPacket.InAddr()))
inbound.ApplyAdditions(cMetadata, h.Additions...)
inbound.ApplyAdditions(cMetadata, getAdditions(ctx)...)
h.Tunnel.HandleUDPPacket(cPacket, cMetadata)
}
func (h *ListenerHandler) NewError(ctx context.Context, err error) {
log.Warnln("%s listener get error: %+v", h.Type.String(), err)
}
@@ -225,11 +250,11 @@ func ShouldIgnorePacketError(err error) bool {
}
type packet struct {
conn *network.NetPacketConn
mutex *sync.Mutex
rAddr net.Addr
lAddr net.Addr
buff *buf.Buffer
writer *network.NetPacketWriter
mutex *sync.Mutex
rAddr net.Addr
lAddr net.Addr
buff *buf.Buffer
}
func (c *packet) Data() []byte {
@@ -245,7 +270,7 @@ func (c *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) {
c.mutex.Lock()
defer c.mutex.Unlock()
conn := *c.conn
conn := *c.writer
if conn == nil {
err = errors.New("writeBack to closed connection")
return
@@ -43,16 +43,31 @@ func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, meta
return h.ListenerHandler.NewConnection(ctx, conn, metadata)
}
func (h *ListenerHandler) NewPacket(ctx context.Context, key netip.AddrPort, buffer *buf.Buffer, metadata M.Metadata, init func(natConn network.PacketConn) network.PacketWriter) {
if h.ShouldHijackDns(metadata.Destination.AddrPort()) {
log.Debugln("[DNS] hijack udp:%s from %s", metadata.Destination.String(), metadata.Source.String())
writer := init(nil)
rwOptions := network.ReadWaitOptions{
FrontHeadroom: network.CalculateFrontHeadroom(writer),
RearHeadroom: network.CalculateRearHeadroom(writer),
MTU: resolver.SafeDnsPacketSize,
}
go relayDnsPacket(ctx, buffer, rwOptions, metadata.Destination, nil, &writer)
return
}
h.ListenerHandler.NewPacket(ctx, key, buffer, metadata, init)
}
func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.PacketConn, metadata M.Metadata) error {
if h.ShouldHijackDns(metadata.Destination.AddrPort()) {
log.Debugln("[DNS] hijack udp:%s from %s", metadata.Destination.String(), metadata.Source.String())
defer func() { _ = conn.Close() }()
mutex := sync.Mutex{}
conn2 := conn // a new interface to set nil in defer
var writer network.PacketWriter = conn // a new interface to set nil in defer
defer func() {
mutex.Lock() // this goroutine must exit after all conn.WritePacket() is not running
defer mutex.Unlock()
conn2 = nil
writer = nil
}()
rwOptions := network.ReadWaitOptions{
FrontHeadroom: network.CalculateFrontHeadroom(conn),
@@ -89,43 +104,47 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.
}
return err
}
go func() {
ctx, cancel := context.WithTimeout(ctx, resolver.DefaultDnsRelayTimeout)
defer cancel()
inData := readBuff.Bytes()
writeBuff := readBuff
writeBuff.Resize(writeBuff.Start(), 0)
if len(writeBuff.FreeBytes()) < resolver.SafeDnsPacketSize { // only create a new buffer when space don't enough
writeBuff = rwOptions.NewPacketBuffer()
}
msg, err := resolver.RelayDnsPacket(ctx, inData, writeBuff.FreeBytes())
if writeBuff != readBuff {
readBuff.Release()
}
if err != nil {
writeBuff.Release()
return
}
writeBuff.Truncate(len(msg))
mutex.Lock()
defer mutex.Unlock()
conn := conn2
if conn == nil {
writeBuff.Release()
return
}
err = conn.WritePacket(writeBuff, dest) // WritePacket will release writeBuff
if err != nil {
writeBuff.Release()
return
}
}()
go relayDnsPacket(ctx, readBuff, rwOptions, dest, &mutex, &writer)
}
return nil
}
return h.ListenerHandler.NewPacketConnection(ctx, conn, metadata)
}
func relayDnsPacket(ctx context.Context, readBuff *buf.Buffer, rwOptions network.ReadWaitOptions, dest M.Socksaddr, mutex *sync.Mutex, writer *network.PacketWriter) {
ctx, cancel := context.WithTimeout(ctx, resolver.DefaultDnsRelayTimeout)
defer cancel()
inData := readBuff.Bytes()
writeBuff := readBuff
writeBuff.Resize(writeBuff.Start(), 0)
if len(writeBuff.FreeBytes()) < resolver.SafeDnsPacketSize { // only create a new buffer when space don't enough
writeBuff = rwOptions.NewPacketBuffer()
}
msg, err := resolver.RelayDnsPacket(ctx, inData, writeBuff.FreeBytes())
if writeBuff != readBuff {
readBuff.Release()
}
if err != nil {
writeBuff.Release()
return
}
writeBuff.Truncate(len(msg))
if mutex != nil {
mutex.Lock()
defer mutex.Unlock()
}
conn := *writer
if conn == nil {
writeBuff.Release()
return
}
err = conn.WritePacket(writeBuff, dest) // WritePacket will release writeBuff
if err != nil {
writeBuff.Release()
return
}
}
func (h *ListenerHandler) TypeMutation(typ C.Type) *ListenerHandler {
handle := *h
handle.ListenerHandler = h.ListenerHandler.TypeMutation(typ)
@@ -55,13 +55,13 @@ require (
github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793 // indirect
github.com/metacubex/quic-go v0.52.1-0.20250522021943-aef454b9e639 // indirect
github.com/metacubex/randv2 v0.2.0 // indirect
github.com/metacubex/sing v0.5.3-0.20250504031621-1f99e54c15b7 // indirect
github.com/metacubex/sing v0.5.3 // indirect
github.com/metacubex/sing-mux v0.3.2 // indirect
github.com/metacubex/sing-quic v0.0.0-20250520025433-6e556a6bef7a // indirect
github.com/metacubex/sing-quic v0.0.0-20250523120938-f1a248e5ec7f // indirect
github.com/metacubex/sing-shadowsocks v0.2.9 // indirect
github.com/metacubex/sing-shadowsocks2 v0.2.3 // indirect
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 // indirect
github.com/metacubex/sing-tun v0.4.6-0.20250503065609-efb9f0beb6f6 // indirect
github.com/metacubex/sing-tun v0.4.6-0.20250524142129-9d110c0af70c // indirect
github.com/metacubex/sing-vmess v0.2.1 // indirect
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f // indirect
github.com/metacubex/smux v0.0.0-20250503055512-501391591dee // indirect
@@ -116,20 +116,20 @@ github.com/metacubex/quic-go v0.52.1-0.20250522021943-aef454b9e639/go.mod h1:Kc6
github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs=
github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY=
github.com/metacubex/sing v0.5.2/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w=
github.com/metacubex/sing v0.5.3-0.20250504031621-1f99e54c15b7 h1:m4nSxvw46JEgxMzzmnXams+ebwabcry4Ydep/zNiesQ=
github.com/metacubex/sing v0.5.3-0.20250504031621-1f99e54c15b7/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w=
github.com/metacubex/sing v0.5.3 h1:QWdN16WFKMk06x4nzkc8SvZ7y2x+TLQrpkPoHs+WSVM=
github.com/metacubex/sing v0.5.3/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w=
github.com/metacubex/sing-mux v0.3.2 h1:nJv52pyRivHcaZJKk2JgxpaVvj1GAXG81scSa9N7ncw=
github.com/metacubex/sing-mux v0.3.2/go.mod h1:3rt1soewn0O6j89GCLmwAQFsq257u0jf2zQSPhTL3Bw=
github.com/metacubex/sing-quic v0.0.0-20250520025433-6e556a6bef7a h1:Ho73vGiB94LmtK5T+tKVwtCNEi/YiHmPjlqpHSAmAVs=
github.com/metacubex/sing-quic v0.0.0-20250520025433-6e556a6bef7a/go.mod h1:JPTpf7fpnojsSuwRJExhSZSy63pVbp3VM39+zj+sAJM=
github.com/metacubex/sing-quic v0.0.0-20250523120938-f1a248e5ec7f h1:mP3vIm+9hRFI0C0Vl3pE0NESF/L85FDbuB0tGgUii6I=
github.com/metacubex/sing-quic v0.0.0-20250523120938-f1a248e5ec7f/go.mod h1:JPTpf7fpnojsSuwRJExhSZSy63pVbp3VM39+zj+sAJM=
github.com/metacubex/sing-shadowsocks v0.2.9 h1:2e++13WNN7EGjGtvrGLUzW1xrCdQbW2gIFpgw5GEw00=
github.com/metacubex/sing-shadowsocks v0.2.9/go.mod h1:CJSEGO4FWQAWe+ZiLZxCweGdjRR60A61SIoVjdjQeBA=
github.com/metacubex/sing-shadowsocks2 v0.2.3 h1:v3rNS/5Ywh0NIZ6VU/NmdERQIN5RePzyxCFeQsU4Cx0=
github.com/metacubex/sing-shadowsocks2 v0.2.3/go.mod h1:/WNy/Q8ahLCoPRriWuFZFD0Jy+JNp1MEQl28Zw6SaF8=
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 h1:gXU+MYPm7Wme3/OAY2FFzVq9d9GxPHOqu5AQfg/ddhI=
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2/go.mod h1:mbfboaXauKJNIHJYxQRa+NJs4JU9NZfkA+I33dS2+9E=
github.com/metacubex/sing-tun v0.4.6-0.20250503065609-efb9f0beb6f6 h1:TAwL91XPa6x1QK55CRm+VTzPvLPUfEr/uFDnOZArqEU=
github.com/metacubex/sing-tun v0.4.6-0.20250503065609-efb9f0beb6f6/go.mod h1:HDaHDL6onAX2ZGbAGUXKp++PohRdNb7Nzt6zxzhox+U=
github.com/metacubex/sing-tun v0.4.6-0.20250524142129-9d110c0af70c h1:Y6jk7AH5BEg9Dsvczrf/KokYsvxeKSZZlCLHg+hC4ro=
github.com/metacubex/sing-tun v0.4.6-0.20250524142129-9d110c0af70c/go.mod h1:HDaHDL6onAX2ZGbAGUXKp++PohRdNb7Nzt6zxzhox+U=
github.com/metacubex/sing-vmess v0.2.1 h1:I6gM3VUjtvJ15D805EUbNH+SRBuqzJeFnuIbKYUsWZ0=
github.com/metacubex/sing-vmess v0.2.1/go.mod h1:DsODWItJtOMZNna8Qhheg8r3tUivrcO3vWgaTYKnfTo=
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f h1:Sr/DYKYofKHKc4GF3qkRGNuj6XA6c0eqPgEDN+VAsYU=
@@ -61,13 +61,13 @@ require (
github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793 // indirect
github.com/metacubex/quic-go v0.52.1-0.20250522021943-aef454b9e639 // indirect
github.com/metacubex/randv2 v0.2.0 // indirect
github.com/metacubex/sing v0.5.3-0.20250504031621-1f99e54c15b7 // indirect
github.com/metacubex/sing v0.5.3 // indirect
github.com/metacubex/sing-mux v0.3.2 // indirect
github.com/metacubex/sing-quic v0.0.0-20250520025433-6e556a6bef7a // indirect
github.com/metacubex/sing-quic v0.0.0-20250523120938-f1a248e5ec7f // indirect
github.com/metacubex/sing-shadowsocks v0.2.9 // indirect
github.com/metacubex/sing-shadowsocks2 v0.2.3 // indirect
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 // indirect
github.com/metacubex/sing-tun v0.4.6-0.20250503065609-efb9f0beb6f6 // indirect
github.com/metacubex/sing-tun v0.4.6-0.20250524142129-9d110c0af70c // indirect
github.com/metacubex/sing-vmess v0.2.1 // indirect
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f // indirect
github.com/metacubex/smux v0.0.0-20250503055512-501391591dee // indirect
@@ -117,20 +117,20 @@ github.com/metacubex/quic-go v0.52.1-0.20250522021943-aef454b9e639/go.mod h1:Kc6
github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs=
github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY=
github.com/metacubex/sing v0.5.2/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w=
github.com/metacubex/sing v0.5.3-0.20250504031621-1f99e54c15b7 h1:m4nSxvw46JEgxMzzmnXams+ebwabcry4Ydep/zNiesQ=
github.com/metacubex/sing v0.5.3-0.20250504031621-1f99e54c15b7/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w=
github.com/metacubex/sing v0.5.3 h1:QWdN16WFKMk06x4nzkc8SvZ7y2x+TLQrpkPoHs+WSVM=
github.com/metacubex/sing v0.5.3/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w=
github.com/metacubex/sing-mux v0.3.2 h1:nJv52pyRivHcaZJKk2JgxpaVvj1GAXG81scSa9N7ncw=
github.com/metacubex/sing-mux v0.3.2/go.mod h1:3rt1soewn0O6j89GCLmwAQFsq257u0jf2zQSPhTL3Bw=
github.com/metacubex/sing-quic v0.0.0-20250520025433-6e556a6bef7a h1:Ho73vGiB94LmtK5T+tKVwtCNEi/YiHmPjlqpHSAmAVs=
github.com/metacubex/sing-quic v0.0.0-20250520025433-6e556a6bef7a/go.mod h1:JPTpf7fpnojsSuwRJExhSZSy63pVbp3VM39+zj+sAJM=
github.com/metacubex/sing-quic v0.0.0-20250523120938-f1a248e5ec7f h1:mP3vIm+9hRFI0C0Vl3pE0NESF/L85FDbuB0tGgUii6I=
github.com/metacubex/sing-quic v0.0.0-20250523120938-f1a248e5ec7f/go.mod h1:JPTpf7fpnojsSuwRJExhSZSy63pVbp3VM39+zj+sAJM=
github.com/metacubex/sing-shadowsocks v0.2.9 h1:2e++13WNN7EGjGtvrGLUzW1xrCdQbW2gIFpgw5GEw00=
github.com/metacubex/sing-shadowsocks v0.2.9/go.mod h1:CJSEGO4FWQAWe+ZiLZxCweGdjRR60A61SIoVjdjQeBA=
github.com/metacubex/sing-shadowsocks2 v0.2.3 h1:v3rNS/5Ywh0NIZ6VU/NmdERQIN5RePzyxCFeQsU4Cx0=
github.com/metacubex/sing-shadowsocks2 v0.2.3/go.mod h1:/WNy/Q8ahLCoPRriWuFZFD0Jy+JNp1MEQl28Zw6SaF8=
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 h1:gXU+MYPm7Wme3/OAY2FFzVq9d9GxPHOqu5AQfg/ddhI=
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2/go.mod h1:mbfboaXauKJNIHJYxQRa+NJs4JU9NZfkA+I33dS2+9E=
github.com/metacubex/sing-tun v0.4.6-0.20250503065609-efb9f0beb6f6 h1:TAwL91XPa6x1QK55CRm+VTzPvLPUfEr/uFDnOZArqEU=
github.com/metacubex/sing-tun v0.4.6-0.20250503065609-efb9f0beb6f6/go.mod h1:HDaHDL6onAX2ZGbAGUXKp++PohRdNb7Nzt6zxzhox+U=
github.com/metacubex/sing-tun v0.4.6-0.20250524142129-9d110c0af70c h1:Y6jk7AH5BEg9Dsvczrf/KokYsvxeKSZZlCLHg+hC4ro=
github.com/metacubex/sing-tun v0.4.6-0.20250524142129-9d110c0af70c/go.mod h1:HDaHDL6onAX2ZGbAGUXKp++PohRdNb7Nzt6zxzhox+U=
github.com/metacubex/sing-vmess v0.2.1 h1:I6gM3VUjtvJ15D805EUbNH+SRBuqzJeFnuIbKYUsWZ0=
github.com/metacubex/sing-vmess v0.2.1/go.mod h1:DsODWItJtOMZNna8Qhheg8r3tUivrcO3vWgaTYKnfTo=
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f h1:Sr/DYKYofKHKc4GF3qkRGNuj6XA6c0eqPgEDN+VAsYU=
+16 -14
View File
@@ -26,8 +26,9 @@ var (
)
type ifaceCache struct {
ifMap map[string]*Interface
ifTable bart.Table[*Interface]
ifMapByName map[string]*Interface
ifMapByAddr map[netip.Addr]*Interface
ifTable bart.Table[*Interface]
}
var caches = singledo.NewSingle[*ifaceCache](time.Second * 20)
@@ -40,7 +41,8 @@ func getCache() (*ifaceCache, error) {
}
cache := &ifaceCache{
ifMap: make(map[string]*Interface),
ifMapByName: make(map[string]*Interface),
ifMapByAddr: make(map[netip.Addr]*Interface),
}
for _, iface := range ifaces {
@@ -78,20 +80,14 @@ func getCache() (*ifaceCache, error) {
Flags: iface.Flags,
Addresses: ipNets,
}
cache.ifMap[iface.Name] = ifaceObj
cache.ifMapByName[iface.Name] = ifaceObj
if iface.Flags&net.FlagUp == 0 {
continue // interface down
}
for _, prefix := range ipNets {
if _, ok := cache.ifTable.Get(prefix); ok {
// maybe two interfaces have the same prefix but different address,
// so we add a special /32(ipv4) or /128(ipv6) item to let ResolveInterfaceByAddr
// could find the correct interface
cache.ifTable.Insert(netip.PrefixFrom(prefix.Addr(), prefix.Addr().BitLen()), ifaceObj)
} else {
cache.ifTable.Insert(prefix, ifaceObj)
}
cache.ifMapByAddr[prefix.Addr()] = ifaceObj
cache.ifTable.Insert(prefix, ifaceObj)
}
}
@@ -105,7 +101,7 @@ func Interfaces() (map[string]*Interface, error) {
if err != nil {
return nil, err
}
return cache.ifMap, nil
return cache.ifMapByName, nil
}
func ResolveInterface(name string) (*Interface, error) {
@@ -127,6 +123,11 @@ func ResolveInterfaceByAddr(addr netip.Addr) (*Interface, error) {
if err != nil {
return nil, err
}
// maybe two interfaces have the same prefix but different address
// so direct check address equal before do a route lookup (longest prefix match)
if iface, ok := cache.ifMapByAddr[addr]; ok {
return iface, nil
}
iface, ok := cache.ifTable.Lookup(addr)
if !ok {
return nil, ErrIfaceNotFound
@@ -140,7 +141,8 @@ func IsLocalIp(addr netip.Addr) (bool, error) {
if err != nil {
return false, err
}
return cache.ifTable.Contains(addr), nil
_, ok := cache.ifMapByAddr[addr]
return ok, nil
}
func FlushCache() {
+1 -1
View File
@@ -31,7 +31,7 @@ require (
github.com/metacubex/sing-shadowsocks v0.2.9
github.com/metacubex/sing-shadowsocks2 v0.2.3
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2
github.com/metacubex/sing-tun v0.4.6-0.20250523121712-972bc1c2b1e7
github.com/metacubex/sing-tun v0.4.6-0.20250524142129-9d110c0af70c
github.com/metacubex/sing-vmess v0.2.1
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f
github.com/metacubex/smux v0.0.0-20250503055512-501391591dee
+2 -2
View File
@@ -128,8 +128,8 @@ github.com/metacubex/sing-shadowsocks2 v0.2.3 h1:v3rNS/5Ywh0NIZ6VU/NmdERQIN5RePz
github.com/metacubex/sing-shadowsocks2 v0.2.3/go.mod h1:/WNy/Q8ahLCoPRriWuFZFD0Jy+JNp1MEQl28Zw6SaF8=
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 h1:gXU+MYPm7Wme3/OAY2FFzVq9d9GxPHOqu5AQfg/ddhI=
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2/go.mod h1:mbfboaXauKJNIHJYxQRa+NJs4JU9NZfkA+I33dS2+9E=
github.com/metacubex/sing-tun v0.4.6-0.20250523121712-972bc1c2b1e7 h1:vxkBCkZocH2de2tqeeCZxWvT1VjSJPFkE/8hGqvcLCQ=
github.com/metacubex/sing-tun v0.4.6-0.20250523121712-972bc1c2b1e7/go.mod h1:HDaHDL6onAX2ZGbAGUXKp++PohRdNb7Nzt6zxzhox+U=
github.com/metacubex/sing-tun v0.4.6-0.20250524142129-9d110c0af70c h1:Y6jk7AH5BEg9Dsvczrf/KokYsvxeKSZZlCLHg+hC4ro=
github.com/metacubex/sing-tun v0.4.6-0.20250524142129-9d110c0af70c/go.mod h1:HDaHDL6onAX2ZGbAGUXKp++PohRdNb7Nzt6zxzhox+U=
github.com/metacubex/sing-vmess v0.2.1 h1:I6gM3VUjtvJ15D805EUbNH+SRBuqzJeFnuIbKYUsWZ0=
github.com/metacubex/sing-vmess v0.2.1/go.mod h1:DsODWItJtOMZNna8Qhheg8r3tUivrcO3vWgaTYKnfTo=
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f h1:Sr/DYKYofKHKc4GF3qkRGNuj6XA6c0eqPgEDN+VAsYU=
+54 -29
View File
@@ -16,6 +16,7 @@ import (
mux "github.com/metacubex/sing-mux"
vmess "github.com/metacubex/sing-vmess"
"github.com/metacubex/sing/common"
"github.com/metacubex/sing/common/buf"
"github.com/metacubex/sing/common/bufio"
"github.com/metacubex/sing/common/bufio/deadline"
@@ -146,11 +147,11 @@ func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, meta
func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.PacketConn, metadata M.Metadata) error {
defer func() { _ = conn.Close() }()
mutex := sync.Mutex{}
conn2 := bufio.NewNetPacketConn(conn) // a new interface to set nil in defer
writer := bufio.NewNetPacketWriter(conn) // a new interface to set nil in defer
defer func() {
mutex.Lock() // this goroutine must exit after all conn.WritePacket() is not running
defer mutex.Unlock()
conn2 = nil
writer = nil
}()
rwOptions := network.ReadWaitOptions{}
readWaiter, isReadWaiter := bufio.CreatePacketReadWaiter(conn)
@@ -180,32 +181,56 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.
return err
}
cPacket := &packet{
conn: &conn2,
mutex: &mutex,
rAddr: metadata.Source.UDPAddr(),
lAddr: conn.LocalAddr(),
buff: buff,
writer: &writer,
mutex: &mutex,
rAddr: metadata.Source.UDPAddr(),
lAddr: conn.LocalAddr(),
buff: buff,
}
cMetadata := &C.Metadata{
NetWork: C.UDP,
Type: h.Type,
}
if metadata.Source.IsIP() && metadata.Source.Fqdn == "" {
cMetadata.RawSrcAddr = metadata.Source.Unwrap().UDPAddr()
}
if dest.IsIP() && dest.Fqdn == "" {
cMetadata.RawDstAddr = dest.Unwrap().UDPAddr()
}
inbound.ApplyAdditions(cMetadata, inbound.WithDstAddr(dest), inbound.WithSrcAddr(metadata.Source), inbound.WithInAddr(conn.LocalAddr()))
inbound.ApplyAdditions(cMetadata, h.Additions...)
inbound.ApplyAdditions(cMetadata, getAdditions(ctx)...)
h.Tunnel.HandleUDPPacket(cPacket, cMetadata)
h.handlePacket(ctx, cPacket, metadata.Source, dest)
}
return nil
}
type localAddr interface {
LocalAddr() net.Addr
}
func (h *ListenerHandler) NewPacket(ctx context.Context, key netip.AddrPort, buffer *buf.Buffer, metadata M.Metadata, init func(natConn network.PacketConn) network.PacketWriter) {
writer := bufio.NewNetPacketWriter(init(nil))
mutex := sync.Mutex{}
cPacket := &packet{
writer: &writer,
mutex: &mutex,
rAddr: metadata.Source.UDPAddr(),
buff: buffer,
}
if conn, ok := common.Cast[localAddr](writer); ok {
cPacket.rAddr = conn.LocalAddr()
} else {
cPacket.rAddr = metadata.Source.UDPAddr() // tun does not have real inAddr
}
h.handlePacket(ctx, cPacket, metadata.Source, metadata.Destination)
}
func (h *ListenerHandler) handlePacket(ctx context.Context, cPacket *packet, source M.Socksaddr, destination M.Socksaddr) {
cMetadata := &C.Metadata{
NetWork: C.UDP,
Type: h.Type,
}
if source.IsIP() && source.Fqdn == "" {
cMetadata.RawSrcAddr = source.Unwrap().UDPAddr()
}
if destination.IsIP() && destination.Fqdn == "" {
cMetadata.RawDstAddr = destination.Unwrap().UDPAddr()
}
inbound.ApplyAdditions(cMetadata, inbound.WithDstAddr(destination), inbound.WithSrcAddr(source), inbound.WithInAddr(cPacket.InAddr()))
inbound.ApplyAdditions(cMetadata, h.Additions...)
inbound.ApplyAdditions(cMetadata, getAdditions(ctx)...)
h.Tunnel.HandleUDPPacket(cPacket, cMetadata)
}
func (h *ListenerHandler) NewError(ctx context.Context, err error) {
log.Warnln("%s listener get error: %+v", h.Type.String(), err)
}
@@ -225,11 +250,11 @@ func ShouldIgnorePacketError(err error) bool {
}
type packet struct {
conn *network.NetPacketConn
mutex *sync.Mutex
rAddr net.Addr
lAddr net.Addr
buff *buf.Buffer
writer *network.NetPacketWriter
mutex *sync.Mutex
rAddr net.Addr
lAddr net.Addr
buff *buf.Buffer
}
func (c *packet) Data() []byte {
@@ -245,7 +270,7 @@ func (c *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) {
c.mutex.Lock()
defer c.mutex.Unlock()
conn := *c.conn
conn := *c.writer
if conn == nil {
err = errors.New("writeBack to closed connection")
return
+52 -33
View File
@@ -43,16 +43,31 @@ func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, meta
return h.ListenerHandler.NewConnection(ctx, conn, metadata)
}
func (h *ListenerHandler) NewPacket(ctx context.Context, key netip.AddrPort, buffer *buf.Buffer, metadata M.Metadata, init func(natConn network.PacketConn) network.PacketWriter) {
if h.ShouldHijackDns(metadata.Destination.AddrPort()) {
log.Debugln("[DNS] hijack udp:%s from %s", metadata.Destination.String(), metadata.Source.String())
writer := init(nil)
rwOptions := network.ReadWaitOptions{
FrontHeadroom: network.CalculateFrontHeadroom(writer),
RearHeadroom: network.CalculateRearHeadroom(writer),
MTU: resolver.SafeDnsPacketSize,
}
go relayDnsPacket(ctx, buffer, rwOptions, metadata.Destination, nil, &writer)
return
}
h.ListenerHandler.NewPacket(ctx, key, buffer, metadata, init)
}
func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.PacketConn, metadata M.Metadata) error {
if h.ShouldHijackDns(metadata.Destination.AddrPort()) {
log.Debugln("[DNS] hijack udp:%s from %s", metadata.Destination.String(), metadata.Source.String())
defer func() { _ = conn.Close() }()
mutex := sync.Mutex{}
conn2 := conn // a new interface to set nil in defer
var writer network.PacketWriter = conn // a new interface to set nil in defer
defer func() {
mutex.Lock() // this goroutine must exit after all conn.WritePacket() is not running
defer mutex.Unlock()
conn2 = nil
writer = nil
}()
rwOptions := network.ReadWaitOptions{
FrontHeadroom: network.CalculateFrontHeadroom(conn),
@@ -89,43 +104,47 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.
}
return err
}
go func() {
ctx, cancel := context.WithTimeout(ctx, resolver.DefaultDnsRelayTimeout)
defer cancel()
inData := readBuff.Bytes()
writeBuff := readBuff
writeBuff.Resize(writeBuff.Start(), 0)
if len(writeBuff.FreeBytes()) < resolver.SafeDnsPacketSize { // only create a new buffer when space don't enough
writeBuff = rwOptions.NewPacketBuffer()
}
msg, err := resolver.RelayDnsPacket(ctx, inData, writeBuff.FreeBytes())
if writeBuff != readBuff {
readBuff.Release()
}
if err != nil {
writeBuff.Release()
return
}
writeBuff.Truncate(len(msg))
mutex.Lock()
defer mutex.Unlock()
conn := conn2
if conn == nil {
writeBuff.Release()
return
}
err = conn.WritePacket(writeBuff, dest) // WritePacket will release writeBuff
if err != nil {
writeBuff.Release()
return
}
}()
go relayDnsPacket(ctx, readBuff, rwOptions, dest, &mutex, &writer)
}
return nil
}
return h.ListenerHandler.NewPacketConnection(ctx, conn, metadata)
}
func relayDnsPacket(ctx context.Context, readBuff *buf.Buffer, rwOptions network.ReadWaitOptions, dest M.Socksaddr, mutex *sync.Mutex, writer *network.PacketWriter) {
ctx, cancel := context.WithTimeout(ctx, resolver.DefaultDnsRelayTimeout)
defer cancel()
inData := readBuff.Bytes()
writeBuff := readBuff
writeBuff.Resize(writeBuff.Start(), 0)
if len(writeBuff.FreeBytes()) < resolver.SafeDnsPacketSize { // only create a new buffer when space don't enough
writeBuff = rwOptions.NewPacketBuffer()
}
msg, err := resolver.RelayDnsPacket(ctx, inData, writeBuff.FreeBytes())
if writeBuff != readBuff {
readBuff.Release()
}
if err != nil {
writeBuff.Release()
return
}
writeBuff.Truncate(len(msg))
if mutex != nil {
mutex.Lock()
defer mutex.Unlock()
}
conn := *writer
if conn == nil {
writeBuff.Release()
return
}
err = conn.WritePacket(writeBuff, dest) // WritePacket will release writeBuff
if err != nil {
writeBuff.Release()
return
}
}
func (h *ListenerHandler) TypeMutation(typ C.Type) *ListenerHandler {
handle := *h
handle.ListenerHandler = h.ListenerHandler.TypeMutation(typ)
+9 -8
View File
@@ -1688,7 +1688,7 @@ version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e"
dependencies = [
"windows-sys 0.59.0",
"windows-sys 0.48.0",
]
[[package]]
@@ -5344,9 +5344,9 @@ dependencies = [
[[package]]
name = "mlua"
version = "0.10.3"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3f763c1041eff92ffb5d7169968a327e1ed2ebfe425dac0ee5a35f29082534b"
checksum = "c1f5f8fbebc7db5f671671134b9321c4b9aa9adeafccfd9a8c020ae45c6a35d0"
dependencies = [
"bstr",
"either",
@@ -5356,15 +5356,16 @@ dependencies = [
"num-traits",
"parking_lot",
"rustc-hash 2.1.1",
"rustversion",
"serde",
"serde-value",
]
[[package]]
name = "mlua-sys"
version = "0.6.7"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1901c1a635a22fe9250ffcc4fcc937c16b47c2e9e71adba8784af8bca1f69594"
checksum = "380c1f7e2099cafcf40e51d3a9f20a346977587aa4d012eae1f043149a728a93"
dependencies = [
"cc",
"cfg-if",
@@ -6440,9 +6441,9 @@ dependencies = [
[[package]]
name = "os_pipe"
version = "1.2.1"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ffd2b0a5634335b135d5728d84c5e0fd726954b87111f7506a61c502280d982"
checksum = "db335f4760b14ead6290116f2427bf33a14d4f0617d49f78a246de10c1831224"
dependencies = [
"libc",
"windows-sys 0.59.0",
@@ -11200,7 +11201,7 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [
"windows-sys 0.59.0",
"windows-sys 0.48.0",
]
[[package]]
@@ -43,7 +43,7 @@
"react-error-boundary": "6.0.0",
"react-fast-marquee": "1.6.5",
"react-hook-form-mui": "7.6.0",
"react-i18next": "15.5.1",
"react-i18next": "15.5.2",
"react-markdown": "10.1.0",
"react-split-grid": "1.0.4",
"react-use": "17.6.0",
@@ -58,9 +58,9 @@
"@iconify/json": "2.2.341",
"@monaco-editor/react": "4.7.0",
"@tanstack/react-query": "5.76.2",
"@tanstack/react-router": "1.120.7",
"@tanstack/react-router-devtools": "1.120.7",
"@tanstack/router-plugin": "1.120.7",
"@tanstack/react-router": "1.120.10",
"@tanstack/react-router-devtools": "1.120.10",
"@tanstack/router-plugin": "1.120.10",
"@tauri-apps/plugin-clipboard-manager": "2.2.2",
"@tauri-apps/plugin-dialog": "2.2.1",
"@tauri-apps/plugin-fs": "2.2.1",
+1 -1
View File
@@ -27,7 +27,7 @@
"react": "19.1.0",
"react-dom": "19.1.0",
"react-error-boundary": "6.0.0",
"react-i18next": "15.5.1",
"react-i18next": "15.5.2",
"react-use": "17.6.0",
"tailwindcss": "4.1.7",
"vite": "6.3.5",
+2 -2
View File
@@ -2,7 +2,7 @@
"manifest_version": 1,
"latest": {
"mihomo": "v1.19.9",
"mihomo_alpha": "alpha-b1d12a1",
"mihomo_alpha": "alpha-989f4ec",
"clash_rs": "v0.7.8",
"clash_premium": "2023-09-05-gdcc8d87",
"clash_rs_alpha": "0.7.8-alpha+sha.5e5a732"
@@ -69,5 +69,5 @@
"linux-armv7hf": "clash-armv7-unknown-linux-gnueabihf"
}
},
"updated_at": "2025-05-22T22:21:09.549Z"
"updated_at": "2025-05-23T22:21:03.987Z"
}
+47 -55
View File
@@ -247,7 +247,7 @@ importers:
version: 4.1.7
'@tanstack/router-zod-adapter':
specifier: 1.81.5
version: 1.81.5(@tanstack/react-router@1.120.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(zod@3.24.4)
version: 1.81.5(@tanstack/react-router@1.120.10(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(zod@3.24.4)
'@tauri-apps/api':
specifier: 2.5.0
version: 2.5.0
@@ -306,8 +306,8 @@ importers:
specifier: 7.6.0
version: 7.6.0(2588b6a67b6c6c4acc5dcc0a1fcbcc64)
react-i18next:
specifier: 15.5.1
version: 15.5.1(i18next@25.1.3(typescript@5.8.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.8.3)
specifier: 15.5.2
version: 15.5.2(i18next@25.1.3(typescript@5.8.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.8.3)
react-markdown:
specifier: 10.1.0
version: 10.1.0(@types/react@19.1.4)(react@19.1.0)
@@ -346,14 +346,14 @@ importers:
specifier: 5.76.2
version: 5.76.2(react@19.1.0)
'@tanstack/react-router':
specifier: 1.120.7
version: 1.120.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
specifier: 1.120.10
version: 1.120.10(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@tanstack/react-router-devtools':
specifier: 1.120.7
version: 1.120.7(@tanstack/react-router@1.120.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@tanstack/router-core@1.120.7)(csstype@3.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(tiny-invariant@1.3.3)
specifier: 1.120.10
version: 1.120.10(@tanstack/react-router@1.120.10(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@tanstack/router-core@1.120.10)(csstype@3.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(tiny-invariant@1.3.3)
'@tanstack/router-plugin':
specifier: 1.120.7
version: 1.120.7(@tanstack/react-router@1.120.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite@6.3.5(@types/node@22.15.18)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.88.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.4)(yaml@2.8.0))
specifier: 1.120.10
version: 1.120.10(@tanstack/react-router@1.120.10(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite@6.3.5(@types/node@22.15.18)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.88.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.4)(yaml@2.8.0))
'@tauri-apps/plugin-clipboard-manager':
specifier: 2.2.2
version: 2.2.2
@@ -502,8 +502,8 @@ importers:
specifier: 6.0.0
version: 6.0.0(react@19.1.0)
react-i18next:
specifier: 15.5.1
version: 15.5.1(i18next@25.1.3(typescript@5.8.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.8.3)
specifier: 15.5.2
version: 15.5.2(i18next@25.1.3(typescript@5.8.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.8.3)
react-use:
specifier: 17.6.0
version: 17.6.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
@@ -1177,10 +1177,6 @@ packages:
resolution: {integrity: sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==}
engines: {node: '>=6.9.0'}
'@babel/runtime@7.26.10':
resolution: {integrity: sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==}
engines: {node: '>=6.9.0'}
'@babel/runtime@7.27.1':
resolution: {integrity: sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==}
engines: {node: '>=6.9.0'}
@@ -2694,16 +2690,16 @@ packages:
peerDependencies:
react: ^18 || ^19
'@tanstack/react-router-devtools@1.120.7':
resolution: {integrity: sha512-0CsgRHYZhFHvHxexBBMAWi88+QFZzQLYVnezO6BrIcL5Y8FmMK2Ww+hSdVbOzUAZZHFPR/LkUduCoUBsYEMQMA==}
'@tanstack/react-router-devtools@1.120.10':
resolution: {integrity: sha512-0Pc7ttT44MzcW9S0BE8V5Y4APUVnlTxP84VBTtd8z4MCMFbukiMCNqSQIR/jHJ/6zyGZNhjYIBEzB2Oftgo6QQ==}
engines: {node: '>=12'}
peerDependencies:
'@tanstack/react-router': ^1.120.7
'@tanstack/react-router': ^1.120.10
react: '>=18.0.0 || >=19.0.0'
react-dom: '>=18.0.0 || >=19.0.0'
'@tanstack/react-router@1.120.7':
resolution: {integrity: sha512-fKzZXYC0mIY6/deHXvHqUuWrZ9fWOkdXB7IOofLDCBuIyi8eeSAoB6qlUbHnI4adZZVaDVuDqkiXp+1Yp4FBgA==}
'@tanstack/react-router@1.120.10':
resolution: {integrity: sha512-+SE3CAP/YYMNt2jhPsT49LhPyJcABaTzrowDfY/Ru6osR+byNlxbooqhXLIvtxc5WsMLY/aB8TpTcTft1W5IPA==}
engines: {node: '>=12'}
peerDependencies:
react: '>=18.0.0 || >=19.0.0'
@@ -2728,15 +2724,15 @@ packages:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
'@tanstack/router-core@1.120.7':
resolution: {integrity: sha512-CAoRZtDJv5XIn4dBNsg/JqCfG1/ioMJjPR+G71ZhwUgPOBt7gmAGEAwdXD0gwdcBoOupL4//6/NPArNdMJj7yg==}
'@tanstack/router-core@1.120.10':
resolution: {integrity: sha512-AmEJAYt+6w/790zTnfddVhnK1QJCnd96H4xg1aD65Oohc8+OTQBxgWky/wzqwhHRdkdsBgRT7iWac9x5Y8UrQA==}
engines: {node: '>=12'}
'@tanstack/router-devtools-core@1.120.7':
resolution: {integrity: sha512-ijBs21+DdLx5llm+wHJgZ/qBF1m4QLq+b2C0/aA54NGvjSdenWPg+TYnjyUK/34LoDYUw+poIal1aSynaVribw==}
'@tanstack/router-devtools-core@1.120.10':
resolution: {integrity: sha512-fysPrKH7dL/G/guHm0HN+ceFEBZnbKaU9R8KZHo/Qzue7WxQV+g4or2EWnbBJ8/aF+C/WYgxR1ATFqfZEjHSfg==}
engines: {node: '>=12'}
peerDependencies:
'@tanstack/router-core': ^1.120.7
'@tanstack/router-core': ^1.120.10
csstype: ^3.0.10
solid-js: '>=1.9.5'
tiny-invariant: ^1.3.3
@@ -2744,21 +2740,21 @@ packages:
csstype:
optional: true
'@tanstack/router-generator@1.120.7':
resolution: {integrity: sha512-vMXzEuNDQBmMwJ96VPzfhxpxm4SkL1Dn/iDjPv/LVXSLbIgZT4szln2qUq5B4VchSFTnVUZdPG9rXdnR/YeRzw==}
'@tanstack/router-generator@1.120.10':
resolution: {integrity: sha512-oUhzCAeIDfupXGwIf3oMqqdSRw62fTtvdUhMLfnTimGMuSp1ErxIj52PeyVGFAFr/ORP85ZxNqRpAecZal247A==}
engines: {node: '>=12'}
peerDependencies:
'@tanstack/react-router': ^1.120.7
'@tanstack/react-router': ^1.120.10
peerDependenciesMeta:
'@tanstack/react-router':
optional: true
'@tanstack/router-plugin@1.120.7':
resolution: {integrity: sha512-dk/Td/0vunav1UmViIqtGdnP/6MYAnf78UHgy+o8CwtelxAT6B2l0IQ6YAHlRL5EuWcYGlQ0BT7BFY7eCIUNvA==}
'@tanstack/router-plugin@1.120.10':
resolution: {integrity: sha512-jAaL0Vh8Kuy+wFUEUiKSoCiGNljXChldFsuvcqnTo4/4qWtKgHlQminGMmx2z5eGZ0EsIfP+NSAMbCpYPFvEng==}
engines: {node: '>=12'}
peerDependencies:
'@rsbuild/core': '>=1.0.2'
'@tanstack/react-router': ^1.120.7
'@tanstack/react-router': ^1.120.10
vite: '>=5.0.0 || >=6.0.0'
vite-plugin-solid: ^2.11.2
webpack: '>=5.92.0'
@@ -6739,8 +6735,8 @@ packages:
peerDependencies:
react: ^16.8.0 || ^17 || ^18 || ^19
react-i18next@15.5.1:
resolution: {integrity: sha512-C8RZ7N7H0L+flitiX6ASjq9p5puVJU1Z8VyL3OgM/QOMRf40BMZX+5TkpxzZVcTmOLPX5zlti4InEX5pFyiVeA==}
react-i18next@15.5.2:
resolution: {integrity: sha512-ePODyXgmZQAOYTbZXQn5rRsSBu3Gszo69jxW6aKmlSgxKAI1fOhDwSu6bT4EKHciWPKQ7v7lPrjeiadR6Gi+1A==}
peerDependencies:
i18next: '>= 23.2.3'
react: '>= 16.8.0'
@@ -8971,10 +8967,6 @@ snapshots:
dependencies:
regenerator-runtime: 0.14.1
'@babel/runtime@7.26.10':
dependencies:
regenerator-runtime: 0.14.1
'@babel/runtime@7.27.1': {}
'@babel/template@7.27.0':
@@ -10490,10 +10482,10 @@ snapshots:
'@tanstack/query-core': 5.76.2
react: 19.1.0
'@tanstack/react-router-devtools@1.120.7(@tanstack/react-router@1.120.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@tanstack/router-core@1.120.7)(csstype@3.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(tiny-invariant@1.3.3)':
'@tanstack/react-router-devtools@1.120.10(@tanstack/react-router@1.120.10(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@tanstack/router-core@1.120.10)(csstype@3.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(tiny-invariant@1.3.3)':
dependencies:
'@tanstack/react-router': 1.120.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@tanstack/router-devtools-core': 1.120.7(@tanstack/router-core@1.120.7)(csstype@3.1.3)(solid-js@1.9.5)(tiny-invariant@1.3.3)
'@tanstack/react-router': 1.120.10(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@tanstack/router-devtools-core': 1.120.10(@tanstack/router-core@1.120.10)(csstype@3.1.3)(solid-js@1.9.5)(tiny-invariant@1.3.3)
react: 19.1.0
react-dom: 19.1.0(react@19.1.0)
solid-js: 1.9.5
@@ -10502,11 +10494,11 @@ snapshots:
- csstype
- tiny-invariant
'@tanstack/react-router@1.120.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
'@tanstack/react-router@1.120.10(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
dependencies:
'@tanstack/history': 1.115.0
'@tanstack/react-store': 0.7.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@tanstack/router-core': 1.120.7
'@tanstack/router-core': 1.120.10
jsesc: 3.1.0
react: 19.1.0
react-dom: 19.1.0(react@19.1.0)
@@ -10532,15 +10524,15 @@ snapshots:
react: 19.1.0
react-dom: 19.1.0(react@19.1.0)
'@tanstack/router-core@1.120.7':
'@tanstack/router-core@1.120.10':
dependencies:
'@tanstack/history': 1.115.0
'@tanstack/store': 0.7.0
tiny-invariant: 1.3.3
'@tanstack/router-devtools-core@1.120.7(@tanstack/router-core@1.120.7)(csstype@3.1.3)(solid-js@1.9.5)(tiny-invariant@1.3.3)':
'@tanstack/router-devtools-core@1.120.10(@tanstack/router-core@1.120.10)(csstype@3.1.3)(solid-js@1.9.5)(tiny-invariant@1.3.3)':
dependencies:
'@tanstack/router-core': 1.120.7
'@tanstack/router-core': 1.120.10
clsx: 2.1.1
goober: 2.1.16(csstype@3.1.3)
solid-js: 1.9.5
@@ -10548,16 +10540,16 @@ snapshots:
optionalDependencies:
csstype: 3.1.3
'@tanstack/router-generator@1.120.7(@tanstack/react-router@1.120.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0))':
'@tanstack/router-generator@1.120.10(@tanstack/react-router@1.120.10(react-dom@19.1.0(react@19.1.0))(react@19.1.0))':
dependencies:
'@tanstack/virtual-file-routes': 1.115.0
prettier: 3.5.3
tsx: 4.19.4
zod: 3.24.4
optionalDependencies:
'@tanstack/react-router': 1.120.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@tanstack/react-router': 1.120.10(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@tanstack/router-plugin@1.120.7(@tanstack/react-router@1.120.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite@6.3.5(@types/node@22.15.18)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.88.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.4)(yaml@2.8.0))':
'@tanstack/router-plugin@1.120.10(@tanstack/react-router@1.120.10(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite@6.3.5(@types/node@22.15.18)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.88.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.4)(yaml@2.8.0))':
dependencies:
'@babel/core': 7.26.10
'@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.10)
@@ -10565,8 +10557,8 @@ snapshots:
'@babel/template': 7.27.0
'@babel/traverse': 7.27.0
'@babel/types': 7.27.0
'@tanstack/router-core': 1.120.7
'@tanstack/router-generator': 1.120.7(@tanstack/react-router@1.120.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0))
'@tanstack/router-core': 1.120.10
'@tanstack/router-generator': 1.120.10(@tanstack/react-router@1.120.10(react-dom@19.1.0(react@19.1.0))(react@19.1.0))
'@tanstack/router-utils': 1.115.0
'@tanstack/virtual-file-routes': 1.115.0
'@types/babel__core': 7.20.5
@@ -10577,7 +10569,7 @@ snapshots:
unplugin: 2.3.2
zod: 3.24.4
optionalDependencies:
'@tanstack/react-router': 1.120.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@tanstack/react-router': 1.120.10(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
vite: 6.3.5(@types/node@22.15.18)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.30.1)(sass-embedded@1.88.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.4)(yaml@2.8.0)
transitivePeerDependencies:
- supports-color
@@ -10589,9 +10581,9 @@ snapshots:
ansis: 3.12.0
diff: 7.0.0
'@tanstack/router-zod-adapter@1.81.5(@tanstack/react-router@1.120.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(zod@3.24.4)':
'@tanstack/router-zod-adapter@1.81.5(@tanstack/react-router@1.120.10(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(zod@3.24.4)':
dependencies:
'@tanstack/react-router': 1.120.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@tanstack/react-router': 1.120.10(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
zod: 3.24.4
'@tanstack/store@0.7.0': {}
@@ -15026,9 +15018,9 @@ snapshots:
dependencies:
react: 19.1.0
react-i18next@15.5.1(i18next@25.1.3(typescript@5.8.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.8.3):
react-i18next@15.5.2(i18next@25.1.3(typescript@5.8.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.8.3):
dependencies:
'@babel/runtime': 7.26.10
'@babel/runtime': 7.27.1
html-parse-stringify: 3.0.1
i18next: 25.1.3(typescript@5.8.3)
react: 19.1.0
-4
View File
@@ -77,13 +77,9 @@
- 异步化配置:优化端口查找和配置保存逻辑
- 重构事件通知机制到独立线程,避免前端卡死
- 优化端口设置,每个端口可随机设置端口号
- 优化了随机端口和密钥机制,防止随机时卡死!
- 优化了保存机制,使用平滑函数,防止客户端卡死!
- 优化端口设置退出和保存机制!
- 强制为 Mihomo 配置补全并覆盖 external-controller-cors 字段,默认不允许跨域和仅本地请求,提升 cors 安全性,升级配置时自动覆盖。
- 优化了 随机API端口和密钥刷新按钮
- 简化了 随机API端口和密钥(重启即可刷新)
- 增强了 随机API端口和密钥的安全生成
## v2.2.3
-3
View File
@@ -1074,9 +1074,6 @@ dependencies = [
"parking_lot",
"percent-encoding",
"port_scanner",
"rand 0.8.5",
"rand_chacha 0.3.1",
"rand_core 0.6.4",
"regex",
"reqwest",
"reqwest_dav",
-3
View File
@@ -80,9 +80,6 @@ gethostname = "1.0.2"
hmac = "0.12.1"
sha2 = "0.10.9"
hex = "0.4.3"
rand = "0.8.5"
rand_chacha = "0.3.1"
rand_core = "0.6.4"
[target.'cfg(windows)'.dependencies]
runas = "=1.2.0"
+6 -69
View File
@@ -1,8 +1,5 @@
use crate::utils::{dirs, help};
use anyhow::Result;
use rand::{Rng, SeedableRng};
use rand_chacha::ChaCha8Rng;
use rand_core::OsRng;
use serde::{Deserialize, Serialize};
use serde_yaml::{Mapping, Value};
use std::{
@@ -35,6 +32,7 @@ impl IClashTemp {
pub fn template() -> Self {
let mut map = Mapping::new();
let mut tun = Mapping::new();
let mut cors_map = Mapping::new();
tun.insert("enable".into(), false.into());
tun.insert("stack".into(), "gvisor".into());
tun.insert("auto-route".into(), true.into());
@@ -52,10 +50,7 @@ impl IClashTemp {
map.insert("allow-lan".into(), false.into());
map.insert("ipv6".into(), true.into());
map.insert("mode".into(), "rule".into());
map.insert("external-controller".into(), "127.0.0.1:0".into());
map.insert("secret".into(), "".into());
let mut cors_map = Mapping::new();
map.insert("external-controller".into(), "127.0.0.1:9097".into());
cors_map.insert("allow-private-network".into(), true.into());
cors_map.insert(
"allow-origins".into(),
@@ -69,42 +64,14 @@ impl IClashTemp {
]
.into(),
);
map.insert("external-controller-cors".into(), cors_map.into());
map.insert("secret".into(), "".into());
map.insert("tun".into(), tun.into());
map.insert("external-controller-cors".into(), cors_map.into());
map.insert("unified-delay".into(), true.into());
Self(map)
}
// 生成随机端口(动态端口范围:1111-65535)
fn generate_random_port() -> u16 {
let seed = Self::generate_seed();
let mut rng = ChaCha8Rng::from_seed(seed);
rng.gen_range(1111..=65535)
}
// 生成64位强密码(包含大小写字母、数字、特殊符号)
fn generate_secret() -> String {
const CHARS: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+-=[]{}|;':\",.<>/?`~";
let seed = Self::generate_seed();
let mut rng = ChaCha8Rng::from_seed(seed);
(0..64)
.map(|_| CHARS[rng.gen_range(0..CHARS.len())] as char)
.collect()
}
// 生成加密安全的随机种子
fn generate_seed() -> [u8; 32] {
let mut seed = [0u8; 32];
OsRng.fill(&mut seed as &mut [u8]);
seed
}
fn guard(mut config: Mapping) -> Mapping {
// 填入随机控制器端口和密钥
let ctrl_port = Self::generate_random_port();
let ctrl_addr = format!("127.0.0.1:{}", ctrl_port);
let secret = Self::generate_secret();
#[cfg(not(target_os = "windows"))]
let redir_port = Self::guard_redir_port(&config);
#[cfg(target_os = "linux")]
@@ -112,11 +79,7 @@ impl IClashTemp {
let mixed_port = Self::guard_mixed_port(&config);
let socks_port = Self::guard_socks_port(&config);
let port = Self::guard_port(&config);
// 加注随机值
config.insert("external-controller".into(), Value::String(ctrl_addr));
config.insert("secret".into(), Value::String(secret));
let ctrl = Self::guard_server_ctrl(&config);
#[cfg(not(target_os = "windows"))]
config.insert("redir-port".into(), redir_port.into());
#[cfg(target_os = "linux")]
@@ -124,6 +87,7 @@ impl IClashTemp {
config.insert("mixed-port".into(), mixed_port.into());
config.insert("socks-port".into(), socks_port.into());
config.insert("port".into(), port.into());
config.insert("external-controller".into(), ctrl.into());
// 强制覆盖 external-controller-cors 字段,允许本地和 tauri 前端
let mut cors_map = Mapping::new();
@@ -381,33 +345,6 @@ fn test_clash_info() {
);
}
// 验证随机生成
#[test]
fn test_random_config() {
let config = IClashTemp::template();
let guarded = IClashTemp::guard(config.0.clone());
// 验证端口有效性
let ctrl_addr = guarded
.get("external-controller")
.and_then(|v| v.as_str())
.unwrap();
let (ip_str, port_str) = ctrl_addr.split_once(':').unwrap();
assert_eq!(ip_str, "127.0.0.1");
let port: u16 = port_str.parse().unwrap();
assert!(port >= 1111 && port <= 65535);
// 验证密钥强度
let secret = guarded.get("secret").and_then(|v| v.as_str()).unwrap();
assert_eq!(secret.len(), 32);
assert!(secret.chars().any(|c| c.is_uppercase()));
assert!(secret.chars().any(|c| c.is_lowercase()));
assert!(secret.chars().any(|c| c.is_numeric()));
assert!(secret
.chars()
.any(|c| "!@#$%^&*()_+-=[]{}|;':\",.<>/?`~".contains(c)));
}
#[derive(Default, Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub struct IClashExternalControllerCors {
@@ -1,10 +1,8 @@
import { BaseDialog, DialogRef } from "@/components/base";
import { useClashInfo } from "@/hooks/use-clash";
import { useVerge } from "@/hooks/use-verge";
import { patchClashConfig } from "@/services/cmds";
import { showNotice } from "@/services/noticeService";
import {
ContentCopy,
} from "@mui/icons-material";
import { ContentCopy } from "@mui/icons-material";
import {
Alert,
Box,
@@ -18,7 +16,7 @@ import {
Tooltip
} from "@mui/material";
import { useLockFn } from "ahooks";
import { forwardRef, useImperativeHandle, useState } from "react";
import { forwardRef, useEffect, useImperativeHandle, useState } from "react";
import { useTranslation } from "react-i18next";
export const ControllerViewer = forwardRef<DialogRef>((props, ref) => {
@@ -26,50 +24,22 @@ export const ControllerViewer = forwardRef<DialogRef>((props, ref) => {
const [open, setOpen] = useState(false);
const [copySuccess, setCopySuccess] = useState<null | string>(null);
const [isSaving, setIsSaving] = useState(false);
const [isRestarting, setIsRestarting] = useState(false);
const { clashInfo, patchInfo } = useClashInfo();
const { verge, patchVerge } = useVerge();
const [controller, setController] = useState(clashInfo?.server || "");
const [secret, setSecret] = useState(clashInfo?.secret || "");
const restartCoreDirectly = useLockFn(async () => {
try {
const controllerUrl = controller || clashInfo?.server || 'http://localhost:9090';
const headers: Record<string, string> = {
'Content-Type': 'application/json',
};
if (secret) {
headers['Authorization'] = `Bearer ${secret}`;
}
const response = await fetch(`${controllerUrl}/restart`, {
method: 'POST',
headers,
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(errorText || 'Failed to restart core');
}
const contentType = response.headers.get('content-type');
if (contentType && contentType.includes('application/json')) {
return await response.json();
} else {
const text = await response.text();
console.log('Non-JSON response:', text);
return { message: 'Restart request sent successfully' };
}
} catch (err: any) {
console.error('Error restarting core:', err);
throw err;
}
});
// 对话框打开时初始化配置
useImperativeHandle(ref, () => ({
open: async () => {
setOpen(true);
setController(clashInfo?.server || "");
setSecret(clashInfo?.secret || "");
},
close: () => setOpen(false),
}));
// 保存配置
const onSave = useLockFn(async () => {
if (!controller.trim()) {
showNotice('info', t("Controller address cannot be empty"), 3000);
@@ -78,16 +48,11 @@ export const ControllerViewer = forwardRef<DialogRef>((props, ref) => {
try {
setIsSaving(true);
await patchInfo({ "external-controller": controller, secret });
await patchVerge({ "external-controller": controller, secret });
await restartCoreDirectly();
showNotice('success', t("Configuration saved and core restarted successfully"), 2000);
showNotice('success', t("Configuration saved successfully"), 2000);
setOpen(false);
} catch (err: any) {
showNotice('error', err.message || t("Failed to save configuration or restart core"), 4000);
showNotice('error', err.message || t("Failed to save configuration"), 4000);
} finally {
setIsSaving(false);
}
@@ -104,32 +69,19 @@ export const ControllerViewer = forwardRef<DialogRef>((props, ref) => {
}
});
// 初始化对话框
useImperativeHandle(ref, () => ({
open: async () => {
setOpen(true);
// 加载现有配置
setController(clashInfo?.server || "");
setSecret(clashInfo?.secret || "");
},
close: () => setOpen(false),
}));
return (
<BaseDialog
open={open}
title={t("External Controller")}
contentSx={{ width: 400 }}
okBtn={
isSaving ? (
<Box display="flex" alignItems="center" gap={1}>
<CircularProgress size={16} color="inherit" />
{t("Saving...")}
</Box>
) : (
t("Save")
)
}
okBtn={isSaving ? (
<Box display="flex" alignItems="center" gap={1}>
<CircularProgress size={16} color="inherit" />
{t("Saving...")}
</Box>
) : (
t("Save")
)}
cancelBtn={t("Cancel")}
onClose={() => setOpen(false)}
onCancel={() => setOpen(false)}
@@ -140,19 +92,24 @@ export const ControllerViewer = forwardRef<DialogRef>((props, ref) => {
<ListItemText primary={t("External Controller")} />
<Box display="flex" alignItems="center" gap={1}>
<TextField
autoComplete="new-password"
size="small"
sx={{ width: 175 }}
sx={{
width: 175,
opacity: 0.7,
pointerEvents: 'none'
}}
value={controller}
placeholder="Required"
onChange={(e) => setController(e.target.value)}
onChange={() => {}}
disabled={true}
inputProps={{ readOnly: true }}
/>
<Tooltip title={t("Copy to clipboard")}>
<IconButton
size="small"
onClick={() => handleCopyToClipboard(controller, "controller")}
color="primary"
disabled={isSaving}
>
<ContentCopy fontSize="small" />
</IconButton>
@@ -164,19 +121,24 @@ export const ControllerViewer = forwardRef<DialogRef>((props, ref) => {
<ListItemText primary={t("Core Secret")} />
<Box display="flex" alignItems="center" gap={1}>
<TextField
autoComplete="new-password"
size="small"
sx={{ width: 175 }}
sx={{
width: 175,
opacity: 0.7,
pointerEvents: 'none'
}}
value={secret}
placeholder={t("Recommended")}
onChange={(e) => setSecret(e.target.value)}
onChange={() => {}}
disabled={true}
inputProps={{ readOnly: true }}
/>
<Tooltip title={t("Copy to clipboard")}>
<IconButton
size="small"
onClick={() => handleCopyToClipboard(secret, "secret")}
color="primary"
disabled={isSaving}
>
<ContentCopy fontSize="small" />
</IconButton>
@@ -190,10 +152,7 @@ export const ControllerViewer = forwardRef<DialogRef>((props, ref) => {
autoHideDuration={2000}
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
>
<Alert
severity="success"
sx={{ width: '100%' }}
>
<Alert severity="success">
{copySuccess === "controller"
? t("Controller address copied to clipboard")
: t("Secret copied to clipboard")
+4 -45
View File
@@ -1,18 +1,6 @@
import useSWR, { mutate } from "swr";
import { useLockFn } from "ahooks";
import useSWR from "swr";
import { getVergeConfig, patchVergeConfig } from "@/services/cmds";
interface IVergeConfigExtended extends IVergeConfig {
"port"?: number;
"socks-port"?: number;
"mixed-port"?: number;
"redir-port"?: number;
"tproxy-port"?: number;
"api-port"?: number;
"secret"?: string;
"external-controller"?: string; // 添加 missing 属性
}
export const useVerge = () => {
const { data: verge, mutate: mutateVerge } = useSWR(
"getVergeConfig",
@@ -22,39 +10,10 @@ export const useVerge = () => {
},
);
const patchVerge = useLockFn(async (patch: Partial<IVergeConfigExtended>) => {
const hasInfo =
patch["redir-port"] != null ||
patch["tproxy-port"] != null ||
patch["mixed-port"] != null ||
patch["socks-port"] != null ||
patch["port"] != null ||
patch["external-controller"] != null ||
patch.secret != null;
if (!hasInfo) return;
// 端口验证逻辑
const validatePort = (portName: string, portValue: number) => {
if (portValue < 1000) {
throw new Error(`The ${portName} should not < 1000`);
}
if (portValue > 65535) {
throw new Error(`The ${portName} should not > 65535`);
}
};
if (patch["port"]) validatePort("port", patch["port"]);
if (patch["socks-port"]) validatePort("socks-port", patch["socks-port"]);
if (patch["mixed-port"]) validatePort("mixed-port", patch["mixed-port"]);
if (patch["redir-port"]) validatePort("redir-port", patch["redir-port"]);
if (patch["tproxy-port"]) validatePort("tproxy-port", patch["tproxy-port"]);
if (patch["api-port"]) validatePort("api-port", patch["api-port"]);
await patchVergeConfig(patch);
const patchVerge = async (value: Partial<IVergeConfig>) => {
await patchVergeConfig(value);
mutateVerge();
mutate("getVergeConfig");
});
};
return {
verge,
@@ -304,7 +304,7 @@ mtk_bmt_get_mapping_mask(void)
unsigned long *used;
int i, k;
used = kcalloc(sizeof(unsigned long), BIT_WORD(bmtd.bmt_blk_idx) + 1, GFP_KERNEL);
used = kcalloc(BIT_WORD(bmtd.bmt_blk_idx) + 1, sizeof(unsigned long), GFP_KERNEL);
if (!used)
return NULL;
+16 -14
View File
@@ -26,8 +26,9 @@ var (
)
type ifaceCache struct {
ifMap map[string]*Interface
ifTable bart.Table[*Interface]
ifMapByName map[string]*Interface
ifMapByAddr map[netip.Addr]*Interface
ifTable bart.Table[*Interface]
}
var caches = singledo.NewSingle[*ifaceCache](time.Second * 20)
@@ -40,7 +41,8 @@ func getCache() (*ifaceCache, error) {
}
cache := &ifaceCache{
ifMap: make(map[string]*Interface),
ifMapByName: make(map[string]*Interface),
ifMapByAddr: make(map[netip.Addr]*Interface),
}
for _, iface := range ifaces {
@@ -78,20 +80,14 @@ func getCache() (*ifaceCache, error) {
Flags: iface.Flags,
Addresses: ipNets,
}
cache.ifMap[iface.Name] = ifaceObj
cache.ifMapByName[iface.Name] = ifaceObj
if iface.Flags&net.FlagUp == 0 {
continue // interface down
}
for _, prefix := range ipNets {
if _, ok := cache.ifTable.Get(prefix); ok {
// maybe two interfaces have the same prefix but different address,
// so we add a special /32(ipv4) or /128(ipv6) item to let ResolveInterfaceByAddr
// could find the correct interface
cache.ifTable.Insert(netip.PrefixFrom(prefix.Addr(), prefix.Addr().BitLen()), ifaceObj)
} else {
cache.ifTable.Insert(prefix, ifaceObj)
}
cache.ifMapByAddr[prefix.Addr()] = ifaceObj
cache.ifTable.Insert(prefix, ifaceObj)
}
}
@@ -105,7 +101,7 @@ func Interfaces() (map[string]*Interface, error) {
if err != nil {
return nil, err
}
return cache.ifMap, nil
return cache.ifMapByName, nil
}
func ResolveInterface(name string) (*Interface, error) {
@@ -127,6 +123,11 @@ func ResolveInterfaceByAddr(addr netip.Addr) (*Interface, error) {
if err != nil {
return nil, err
}
// maybe two interfaces have the same prefix but different address
// so direct check address equal before do a route lookup (longest prefix match)
if iface, ok := cache.ifMapByAddr[addr]; ok {
return iface, nil
}
iface, ok := cache.ifTable.Lookup(addr)
if !ok {
return nil, ErrIfaceNotFound
@@ -140,7 +141,8 @@ func IsLocalIp(addr netip.Addr) (bool, error) {
if err != nil {
return false, err
}
return cache.ifTable.Contains(addr), nil
_, ok := cache.ifMapByAddr[addr]
return ok, nil
}
func FlushCache() {
+1 -1
View File
@@ -31,7 +31,7 @@ require (
github.com/metacubex/sing-shadowsocks v0.2.9
github.com/metacubex/sing-shadowsocks2 v0.2.3
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2
github.com/metacubex/sing-tun v0.4.6-0.20250523121712-972bc1c2b1e7
github.com/metacubex/sing-tun v0.4.6-0.20250524142129-9d110c0af70c
github.com/metacubex/sing-vmess v0.2.1
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f
github.com/metacubex/smux v0.0.0-20250503055512-501391591dee
+2 -2
View File
@@ -128,8 +128,8 @@ github.com/metacubex/sing-shadowsocks2 v0.2.3 h1:v3rNS/5Ywh0NIZ6VU/NmdERQIN5RePz
github.com/metacubex/sing-shadowsocks2 v0.2.3/go.mod h1:/WNy/Q8ahLCoPRriWuFZFD0Jy+JNp1MEQl28Zw6SaF8=
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 h1:gXU+MYPm7Wme3/OAY2FFzVq9d9GxPHOqu5AQfg/ddhI=
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2/go.mod h1:mbfboaXauKJNIHJYxQRa+NJs4JU9NZfkA+I33dS2+9E=
github.com/metacubex/sing-tun v0.4.6-0.20250523121712-972bc1c2b1e7 h1:vxkBCkZocH2de2tqeeCZxWvT1VjSJPFkE/8hGqvcLCQ=
github.com/metacubex/sing-tun v0.4.6-0.20250523121712-972bc1c2b1e7/go.mod h1:HDaHDL6onAX2ZGbAGUXKp++PohRdNb7Nzt6zxzhox+U=
github.com/metacubex/sing-tun v0.4.6-0.20250524142129-9d110c0af70c h1:Y6jk7AH5BEg9Dsvczrf/KokYsvxeKSZZlCLHg+hC4ro=
github.com/metacubex/sing-tun v0.4.6-0.20250524142129-9d110c0af70c/go.mod h1:HDaHDL6onAX2ZGbAGUXKp++PohRdNb7Nzt6zxzhox+U=
github.com/metacubex/sing-vmess v0.2.1 h1:I6gM3VUjtvJ15D805EUbNH+SRBuqzJeFnuIbKYUsWZ0=
github.com/metacubex/sing-vmess v0.2.1/go.mod h1:DsODWItJtOMZNna8Qhheg8r3tUivrcO3vWgaTYKnfTo=
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f h1:Sr/DYKYofKHKc4GF3qkRGNuj6XA6c0eqPgEDN+VAsYU=
+54 -29
View File
@@ -16,6 +16,7 @@ import (
mux "github.com/metacubex/sing-mux"
vmess "github.com/metacubex/sing-vmess"
"github.com/metacubex/sing/common"
"github.com/metacubex/sing/common/buf"
"github.com/metacubex/sing/common/bufio"
"github.com/metacubex/sing/common/bufio/deadline"
@@ -146,11 +147,11 @@ func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, meta
func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.PacketConn, metadata M.Metadata) error {
defer func() { _ = conn.Close() }()
mutex := sync.Mutex{}
conn2 := bufio.NewNetPacketConn(conn) // a new interface to set nil in defer
writer := bufio.NewNetPacketWriter(conn) // a new interface to set nil in defer
defer func() {
mutex.Lock() // this goroutine must exit after all conn.WritePacket() is not running
defer mutex.Unlock()
conn2 = nil
writer = nil
}()
rwOptions := network.ReadWaitOptions{}
readWaiter, isReadWaiter := bufio.CreatePacketReadWaiter(conn)
@@ -180,32 +181,56 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.
return err
}
cPacket := &packet{
conn: &conn2,
mutex: &mutex,
rAddr: metadata.Source.UDPAddr(),
lAddr: conn.LocalAddr(),
buff: buff,
writer: &writer,
mutex: &mutex,
rAddr: metadata.Source.UDPAddr(),
lAddr: conn.LocalAddr(),
buff: buff,
}
cMetadata := &C.Metadata{
NetWork: C.UDP,
Type: h.Type,
}
if metadata.Source.IsIP() && metadata.Source.Fqdn == "" {
cMetadata.RawSrcAddr = metadata.Source.Unwrap().UDPAddr()
}
if dest.IsIP() && dest.Fqdn == "" {
cMetadata.RawDstAddr = dest.Unwrap().UDPAddr()
}
inbound.ApplyAdditions(cMetadata, inbound.WithDstAddr(dest), inbound.WithSrcAddr(metadata.Source), inbound.WithInAddr(conn.LocalAddr()))
inbound.ApplyAdditions(cMetadata, h.Additions...)
inbound.ApplyAdditions(cMetadata, getAdditions(ctx)...)
h.Tunnel.HandleUDPPacket(cPacket, cMetadata)
h.handlePacket(ctx, cPacket, metadata.Source, dest)
}
return nil
}
type localAddr interface {
LocalAddr() net.Addr
}
func (h *ListenerHandler) NewPacket(ctx context.Context, key netip.AddrPort, buffer *buf.Buffer, metadata M.Metadata, init func(natConn network.PacketConn) network.PacketWriter) {
writer := bufio.NewNetPacketWriter(init(nil))
mutex := sync.Mutex{}
cPacket := &packet{
writer: &writer,
mutex: &mutex,
rAddr: metadata.Source.UDPAddr(),
buff: buffer,
}
if conn, ok := common.Cast[localAddr](writer); ok {
cPacket.rAddr = conn.LocalAddr()
} else {
cPacket.rAddr = metadata.Source.UDPAddr() // tun does not have real inAddr
}
h.handlePacket(ctx, cPacket, metadata.Source, metadata.Destination)
}
func (h *ListenerHandler) handlePacket(ctx context.Context, cPacket *packet, source M.Socksaddr, destination M.Socksaddr) {
cMetadata := &C.Metadata{
NetWork: C.UDP,
Type: h.Type,
}
if source.IsIP() && source.Fqdn == "" {
cMetadata.RawSrcAddr = source.Unwrap().UDPAddr()
}
if destination.IsIP() && destination.Fqdn == "" {
cMetadata.RawDstAddr = destination.Unwrap().UDPAddr()
}
inbound.ApplyAdditions(cMetadata, inbound.WithDstAddr(destination), inbound.WithSrcAddr(source), inbound.WithInAddr(cPacket.InAddr()))
inbound.ApplyAdditions(cMetadata, h.Additions...)
inbound.ApplyAdditions(cMetadata, getAdditions(ctx)...)
h.Tunnel.HandleUDPPacket(cPacket, cMetadata)
}
func (h *ListenerHandler) NewError(ctx context.Context, err error) {
log.Warnln("%s listener get error: %+v", h.Type.String(), err)
}
@@ -225,11 +250,11 @@ func ShouldIgnorePacketError(err error) bool {
}
type packet struct {
conn *network.NetPacketConn
mutex *sync.Mutex
rAddr net.Addr
lAddr net.Addr
buff *buf.Buffer
writer *network.NetPacketWriter
mutex *sync.Mutex
rAddr net.Addr
lAddr net.Addr
buff *buf.Buffer
}
func (c *packet) Data() []byte {
@@ -245,7 +270,7 @@ func (c *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) {
c.mutex.Lock()
defer c.mutex.Unlock()
conn := *c.conn
conn := *c.writer
if conn == nil {
err = errors.New("writeBack to closed connection")
return
+52 -33
View File
@@ -43,16 +43,31 @@ func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, meta
return h.ListenerHandler.NewConnection(ctx, conn, metadata)
}
func (h *ListenerHandler) NewPacket(ctx context.Context, key netip.AddrPort, buffer *buf.Buffer, metadata M.Metadata, init func(natConn network.PacketConn) network.PacketWriter) {
if h.ShouldHijackDns(metadata.Destination.AddrPort()) {
log.Debugln("[DNS] hijack udp:%s from %s", metadata.Destination.String(), metadata.Source.String())
writer := init(nil)
rwOptions := network.ReadWaitOptions{
FrontHeadroom: network.CalculateFrontHeadroom(writer),
RearHeadroom: network.CalculateRearHeadroom(writer),
MTU: resolver.SafeDnsPacketSize,
}
go relayDnsPacket(ctx, buffer, rwOptions, metadata.Destination, nil, &writer)
return
}
h.ListenerHandler.NewPacket(ctx, key, buffer, metadata, init)
}
func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.PacketConn, metadata M.Metadata) error {
if h.ShouldHijackDns(metadata.Destination.AddrPort()) {
log.Debugln("[DNS] hijack udp:%s from %s", metadata.Destination.String(), metadata.Source.String())
defer func() { _ = conn.Close() }()
mutex := sync.Mutex{}
conn2 := conn // a new interface to set nil in defer
var writer network.PacketWriter = conn // a new interface to set nil in defer
defer func() {
mutex.Lock() // this goroutine must exit after all conn.WritePacket() is not running
defer mutex.Unlock()
conn2 = nil
writer = nil
}()
rwOptions := network.ReadWaitOptions{
FrontHeadroom: network.CalculateFrontHeadroom(conn),
@@ -89,43 +104,47 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.
}
return err
}
go func() {
ctx, cancel := context.WithTimeout(ctx, resolver.DefaultDnsRelayTimeout)
defer cancel()
inData := readBuff.Bytes()
writeBuff := readBuff
writeBuff.Resize(writeBuff.Start(), 0)
if len(writeBuff.FreeBytes()) < resolver.SafeDnsPacketSize { // only create a new buffer when space don't enough
writeBuff = rwOptions.NewPacketBuffer()
}
msg, err := resolver.RelayDnsPacket(ctx, inData, writeBuff.FreeBytes())
if writeBuff != readBuff {
readBuff.Release()
}
if err != nil {
writeBuff.Release()
return
}
writeBuff.Truncate(len(msg))
mutex.Lock()
defer mutex.Unlock()
conn := conn2
if conn == nil {
writeBuff.Release()
return
}
err = conn.WritePacket(writeBuff, dest) // WritePacket will release writeBuff
if err != nil {
writeBuff.Release()
return
}
}()
go relayDnsPacket(ctx, readBuff, rwOptions, dest, &mutex, &writer)
}
return nil
}
return h.ListenerHandler.NewPacketConnection(ctx, conn, metadata)
}
func relayDnsPacket(ctx context.Context, readBuff *buf.Buffer, rwOptions network.ReadWaitOptions, dest M.Socksaddr, mutex *sync.Mutex, writer *network.PacketWriter) {
ctx, cancel := context.WithTimeout(ctx, resolver.DefaultDnsRelayTimeout)
defer cancel()
inData := readBuff.Bytes()
writeBuff := readBuff
writeBuff.Resize(writeBuff.Start(), 0)
if len(writeBuff.FreeBytes()) < resolver.SafeDnsPacketSize { // only create a new buffer when space don't enough
writeBuff = rwOptions.NewPacketBuffer()
}
msg, err := resolver.RelayDnsPacket(ctx, inData, writeBuff.FreeBytes())
if writeBuff != readBuff {
readBuff.Release()
}
if err != nil {
writeBuff.Release()
return
}
writeBuff.Truncate(len(msg))
if mutex != nil {
mutex.Lock()
defer mutex.Unlock()
}
conn := *writer
if conn == nil {
writeBuff.Release()
return
}
err = conn.WritePacket(writeBuff, dest) // WritePacket will release writeBuff
if err != nil {
writeBuff.Release()
return
}
}
func (h *ListenerHandler) TypeMutation(typ C.Type) *ListenerHandler {
handle := *h
handle.ListenerHandler = h.ListenerHandler.TypeMutation(typ)
+4 -4
View File
@@ -7,13 +7,13 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=alist
PKG_VERSION:=3.44.0
PKG_WEB_VERSION:=3.44.0
PKG_VERSION:=3.45.0
PKG_WEB_VERSION:=3.45.0
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/AlistGo/alist/tar.gz/v$(PKG_VERSION)?
PKG_HASH:=8511e147e912933ba05c31c28f25c4245cc3529f854b0471ba947e33c09f297d
PKG_HASH:=224119ea5a3b43694e5342c460ab471d6477db1bf7ade5180d542a32363cb097
PKG_LICENSE:=GPL-3.0
PKG_LICENSE_FILE:=LICENSE
@@ -23,7 +23,7 @@ define Download/$(PKG_NAME)-web
FILE:=$(PKG_NAME)-web-$(PKG_WEB_VERSION).tar.gz
URL_FILE:=dist.tar.gz
URL:=https://github.com/AlistGo/alist-web/releases/download/$(PKG_WEB_VERSION)/
HASH:=3709bec59bbc14f0f9f74193cebbb25a317c978fa3a5ae06a900eb341e1b5ae7
HASH:=408b1822893ba6dd6bbeb4055d6c8b96c178d10f4fbb8e5696cf14dcc88dd2fb
endef
PKG_BUILD_DEPENDS:=golang/host
+2 -2
View File
@@ -10,11 +10,11 @@ include $(TOPDIR)/rules.mk
PKG_ARCH_quickstart:=$(ARCH)
PKG_NAME:=quickstart
PKG_VERSION:=0.9.9
PKG_VERSION:=0.9.10
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-binary-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://github.com/linkease/istore-packages/releases/download/prebuilt/
PKG_HASH:=7716e89b4d95364698e8cfd50241e5f4d7a3bea869895fd9e55b97763ac4bca0
PKG_HASH:=79a2d3a7e8bfb35031498327131d1ebd6be618796219f6252339370ef2a7da8b
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-binary-$(PKG_VERSION)
+6 -6
View File
@@ -1963,7 +1963,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a793df0d7afeac54f95b471d3af7f0d4fb975699f972341a4b76988d49cdf0c"
dependencies = [
"cfg-if",
"windows-targets 0.53.0",
"windows-targets 0.48.5",
]
[[package]]
@@ -3906,9 +3906,9 @@ dependencies = [
[[package]]
name = "tokio-tfo"
version = "0.3.3"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "247648fd148ab7cad752fe689b44854600783e52f7005f5107f3d8b4ad74bca0"
checksum = "1e3d7ba01a66a3a6dedebca3788232547ac193b31530bb625cb42eeb8e5154b6"
dependencies = [
"cfg-if",
"futures",
@@ -4043,9 +4043,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
[[package]]
name = "tun"
version = "0.7.19"
version = "0.7.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96826f74fb6301ef2954661a43a9ff37cd1158de10a138c1184300333d2d98e4"
checksum = "c27de50ee1bc396132d25d6fc97c32bc9f9ecd6b960a592a2eb59aa8c161aa81"
dependencies = [
"bytes",
"cfg-if",
@@ -4345,7 +4345,7 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [
"windows-sys 0.59.0",
"windows-sys 0.48.0",
]
[[package]]
+2 -3
View File
@@ -1,11 +1,10 @@
NAME = sing-box
COMMIT = $(shell git rev-parse --short HEAD)
TAGS ?= with_gvisor,with_dhcp,with_wireguard,with_clash_api,with_quic,with_utls,with_tailscale
TAGS_TEST ?= with_gvisor,with_quic,with_wireguard,with_grpc,with_utls
TAGS ?= with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale
GOHOSTOS = $(shell go env GOHOSTOS)
GOHOSTARCH = $(shell go env GOHOSTARCH)
VERSION=$(shell CGO_ENABLED=0 GOOS=$(GOHOSTOS) GOARCH=$(GOHOSTARCH) go run ./cmd/internal/read_tag)
VERSION=$(shell CGO_ENABLED=0 GOOS=$(GOHOSTOS) GOARCH=$(GOHOSTARCH) go run github.com/sagernet/sing-box/cmd/internal/read_tag@latest)
PARAMS = -v -trimpath -ldflags "-X 'github.com/sagernet/sing-box/constant.Version=$(VERSION)' -s -w -buildid="
MAIN_PARAMS = $(PARAMS) -tags "$(TAGS)"
+1 -3
View File
@@ -7,7 +7,6 @@ import (
"strconv"
"time"
"github.com/sagernet/sing-box"
"github.com/sagernet/sing-box/experimental/deprecated"
"github.com/sagernet/sing-box/include"
"github.com/sagernet/sing-box/log"
@@ -68,6 +67,5 @@ func preRun(cmd *cobra.Command, args []string) {
if len(configPaths) == 0 && len(configDirectories) == 0 {
configPaths = append(configPaths, "config.json")
}
globalCtx = service.ContextWith(globalCtx, deprecated.NewStderrManager(log.StdLogger()))
globalCtx = box.Context(globalCtx, include.InboundRegistry(), include.OutboundRegistry(), include.EndpointRegistry(), include.DNSTransportRegistry(), include.ServiceRegistry())
globalCtx = include.Context(service.ContextWith(globalCtx, deprecated.NewStderrManager(log.StdLogger())))
}
+4
View File
@@ -2,6 +2,10 @@
icon: material/alert-decagram
---
#### 1.12.0-beta.18
* Fixes and improvements
#### 1.12.0-beta.17
* Update quic-go to v0.52.0
-4
View File
@@ -165,8 +165,6 @@ github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZN
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I=
github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8=
github.com/sagernet/quic-go v0.51.0-beta.5 h1:/mME3sJvQ8k/JKP0oC/9XoWrm0znO7hWXviB5yiipJY=
github.com/sagernet/quic-go v0.51.0-beta.5/go.mod h1:OV+V5kEBb8kJS7k29MzDu6oj9GyMc7HA07sE1tedxz4=
github.com/sagernet/quic-go v0.52.0-beta.1 h1:hWkojLg64zjV+MJOvJU/kOeWndm3tiEfBLx5foisszs=
github.com/sagernet/quic-go v0.52.0-beta.1/go.mod h1:OV+V5kEBb8kJS7k29MzDu6oj9GyMc7HA07sE1tedxz4=
github.com/sagernet/sing v0.6.9/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
@@ -174,8 +172,6 @@ github.com/sagernet/sing v0.6.11-0.20250521033217-30d675ea099b h1:ZjTCYPb5f7aHdf
github.com/sagernet/sing v0.6.11-0.20250521033217-30d675ea099b/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/sagernet/sing-mux v0.3.2 h1:meZVFiiStvHThb/trcpAkCrmtJOuItG5Dzl1RRP5/NE=
github.com/sagernet/sing-mux v0.3.2/go.mod h1:pht8iFY4c9Xltj7rhVd208npkNaeCxzyXCgulDPLUDA=
github.com/sagernet/sing-quic v0.4.1-0.20250511050139-d459f561c9c3 h1:1J+s1yyZ8+YAYaClI+az8YuFgV9NGXUUCZnriKmos6w=
github.com/sagernet/sing-quic v0.4.1-0.20250511050139-d459f561c9c3/go.mod h1:Mv7CdSyLepmqoLT8rd88Qn3QMv5AbsgjEm3DvEhDVNE=
github.com/sagernet/sing-quic v0.5.0-beta.1 h1:nC0i/s8LhlZB8ev6laZCXF/uiwAE4kRdT4PcDdE4rI4=
github.com/sagernet/sing-quic v0.5.0-beta.1/go.mod h1:SAv/qdeDN+75msGG5U5ZIwG+3Ua50jVIKNrRSY8pkx0=
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
+5
View File
@@ -3,6 +3,7 @@ package include
import (
"context"
"github.com/sagernet/sing-box"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/adapter/endpoint"
"github.com/sagernet/sing-box/adapter/inbound"
@@ -39,6 +40,10 @@ import (
E "github.com/sagernet/sing/common/exceptions"
)
func Context(ctx context.Context) context.Context {
return box.Context(ctx, InboundRegistry(), OutboundRegistry(), EndpointRegistry(), DNSTransportRegistry(), ServiceRegistry())
}
func InboundRegistry() *inbound.Registry {
registry := inbound.NewRegistry()
+8
View File
@@ -221,6 +221,14 @@ func (t *Endpoint) Start(stage adapter.StartStage) error {
}
ipStack := t.server.ExportNetstack().ExportIPStack()
gErr := ipStack.SetSpoofing(tun.DefaultNIC, true)
if gErr != nil {
return gonet.TranslateNetstackError(gErr)
}
gErr = ipStack.SetPromiscuousMode(tun.DefaultNIC, true)
if gErr != nil {
return gonet.TranslateNetstackError(gErr)
}
ipStack.SetTransportProtocolHandler(tcp.ProtocolNumber, tun.NewTCPForwarder(t.ctx, ipStack, t).HandlePacket)
udpForwarder := tun.NewUDPForwarder(t.ctx, ipStack, t, t.udpTimeout)
ipStack.SetTransportProtocolHandler(udp.ProtocolNumber, udpForwarder.HandlePacket)
-2
View File
@@ -172,8 +172,6 @@ func (r *Router) RoutePacketConnectionEx(ctx context.Context, conn N.PacketConn,
} else {
r.logger.ErrorContext(ctx, err)
}
} else if onClose != nil {
onClose(nil)
}
}
+1 -1
View File
@@ -58,7 +58,7 @@ func NewService(ctx context.Context, logger log.ContextLogger, tag string, optio
for i, entry := range options.Servers.Entries() {
inbound, loaded := inboundManager.Get(entry.Value)
if !loaded {
return nil, E.New("parse SSM server[", i, "]: inbound ", entry.Value, "not found")
return nil, E.New("parse SSM server[", i, "]: inbound ", entry.Value, " not found")
}
managedServer, isManaged := inbound.(adapter.ManagedSSMServer)
if !isManaged {
+1 -1
View File
@@ -55,7 +55,7 @@ func (m *UserManager) Add(username string, password string) error {
m.access.Lock()
defer m.access.Unlock()
if _, found := m.usersMap[username]; found {
return E.New("user", username, "already exists")
return E.New("user ", username, " already exists")
}
m.usersMap[username] = password
return m.postUpdate()
+1 -1
View File
@@ -32,7 +32,7 @@ func TestMain(m *testing.M) {
var globalCtx context.Context
func init() {
globalCtx = box.Context(context.Background(), include.InboundRegistry(), include.OutboundRegistry(), include.EndpointRegistry(), include.DNSTransportRegistry(), include.ServiceRegistry())
globalCtx = include.Context(context.Background())
}
func startInstance(t *testing.T, options option.Options) *box.Box {
+22 -22
View File
@@ -5,7 +5,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=naiveproxy
PKG_VERSION:=136.0.7103.44-1
PKG_VERSION:=136.0.7103.44-2
PKG_RELEASE:=1
# intel 80386 & riscv64 & cortex-a76
@@ -20,47 +20,47 @@ else ifeq ($(ARCH_PREBUILT),riscv64_riscv64)
endif
ifeq ($(ARCH_PACKAGES),aarch64_cortex-a53)
PKG_HASH:=639e748f39380bbb146b437c2c8844d0ba73157bd0bb6570656effbf0573b2d5
PKG_HASH:=c8bfc343b40f8412a0dc61e5e972c7aa74d16dbe106c09cc2aa6d74ab691d88f
else ifeq ($(ARCH_PACKAGES),aarch64_cortex-a72)
PKG_HASH:=8c27f966bd9b98f68e594b0c876fc3cb08a4bd46d0c6c796b98765cd3d52456a
PKG_HASH:=0f4976f74289506eb97197d9f07d34e9772c18d3296d940b1ff92180020ec8ee
else ifeq ($(ARCH_PACKAGES),aarch64_generic)
PKG_HASH:=fa2608512e2ccdb532474de847daa9519e100274ecb5e0b8a28702f04e322306
PKG_HASH:=a1205581557629a936d03b93dafb0546356b0849943bf9262c6c89be50314cf8
else ifeq ($(ARCH_PACKAGES),arm_arm1176jzf-s_vfp)
PKG_HASH:=8630dd33bdaa7c3b70f83e709f99a8dff223ce44cd259c6b74c1937117c9adf3
PKG_HASH:=65466cf93a5c46c3ea3ec8c3bdfe275cd0cff61eaca7d27c80f4136a4e7f7832
else ifeq ($(ARCH_PACKAGES),arm_arm926ej-s)
PKG_HASH:=9fc02031a8bca7352f68bd7375e56c8e2149fdd1cd7f77534a86be8cd40532bf
PKG_HASH:=1d8822ff5f77dde52dfe8f108802e232ee74fc85931c1b536f15ffcce4f99c1a
else ifeq ($(ARCH_PACKAGES),arm_cortex-a15_neon-vfpv4)
PKG_HASH:=392a4c05e22cfa5bf13d70675b0f1fa3d977f1396192aa626baad998089d5097
PKG_HASH:=cec6aff2351c6db6a7e6ef512470c9f2b883fc368a87f071594c19425109eabe
else ifeq ($(ARCH_PACKAGES),arm_cortex-a5_vfpv4)
PKG_HASH:=0302503e6270ec48427f13816b6608eb80241598256692a2911c9f52057153ca
PKG_HASH:=dae6a7439f8049ecdcc658478f0ca473cddf00d4a377df591d6967fb8698da0a
else ifeq ($(ARCH_PACKAGES),arm_cortex-a7)
PKG_HASH:=2b1d569eb8f47ad6499d8f26b15fede9257c69e447dc8ad3a36954dbc131f0a4
PKG_HASH:=817b84e650372ea33b7fd2ed6e484e13a6d0aaf559d5f96caafdc4ab1116b7e7
else ifeq ($(ARCH_PACKAGES),arm_cortex-a7_neon-vfpv4)
PKG_HASH:=3822c2f6ffbbdeacb6182d4e24cd2551f2cf30a31f9f38d6c059833772ee1fd6
PKG_HASH:=73aee449f61c6d18152e3bc5c1dd78c5ca434d89e011863f7e85f2ce7b952224
else ifeq ($(ARCH_PACKAGES),arm_cortex-a7_vfpv4)
PKG_HASH:=3b734e692a6ae2b17e53a10775bb3f23a89b15724b745b7b0d78f738f5cab9b4
PKG_HASH:=7833cf68fcfeffaff848360eefa1db21b731dc13609cecca5d037e1e16d14c0f
else ifeq ($(ARCH_PACKAGES),arm_cortex-a8_vfpv3)
PKG_HASH:=e90eeb26167f1f0360d2c94ca1116ff24a66875fa83105cadaa2665a2aa7d346
PKG_HASH:=629941b6fb2823d35a0cc04cd47bda5c7c8796bc168374483a933bd60fa24d1b
else ifeq ($(ARCH_PACKAGES),arm_cortex-a9)
PKG_HASH:=2ceb7baeec30fee26b15b76ad6e40922934509a6f03598ecdd53e6080e87172d
PKG_HASH:=a85f6facbdaa9ef470ebd235b5c7d026e63e71102a635354d725a1933a9a238c
else ifeq ($(ARCH_PACKAGES),arm_cortex-a9_neon)
PKG_HASH:=c2605ba10c7631c9ada9231de2af5ed23850b04b244003b96462b71959fc908a
PKG_HASH:=a8d01014860037a3f8601f29998f2bbf1a442fbfcb168d11fcc76571697dcf76
else ifeq ($(ARCH_PACKAGES),arm_cortex-a9_vfpv3-d16)
PKG_HASH:=65dae408d2c56accd6e5ddb99df104df792d87a92b0d55f36ed2596ccbdfd8c9
PKG_HASH:=d442c8e6643614c4274efa9c27808c54e16e1eb5e384dbc443c89765f95162b8
else ifeq ($(ARCH_PACKAGES),arm_mpcore)
PKG_HASH:=b4b9073edda26281b6913834f04802beb40308bc06237091de38649c74295c22
PKG_HASH:=a7b2475877ea0c9dee713a56c7483913f47c3666b74f5f7a13e45b846f899a06
else ifeq ($(ARCH_PACKAGES),arm_xscale)
PKG_HASH:=cb051087fa4f3b58546d5c746349190163eff80635a0a1ab9ffc17b9c867129f
PKG_HASH:=f95f34aef471ad8f259312d8e87a6df316d19c40dcc486b7b7004959bd1704f2
else ifeq ($(ARCH_PACKAGES),mipsel_24kc)
PKG_HASH:=f01eb9a10300ac6f7d5cd9759a9a47980a9c9c8c5868e25b705c63e711706032
PKG_HASH:=da36e99151e2d67e4eee8d6e33e5e65b158cbe43306edece7e30c638c4ac6baa
else ifeq ($(ARCH_PACKAGES),mipsel_mips32)
PKG_HASH:=cf4c5f8eafba8420dbdc624b228f48f4cc9089c1cec14bb7178e44709896a798
PKG_HASH:=4e5b5fa559cd153ba60fa66ec5aa3271bfbb7985d23db7f4200b1a4b90977564
else ifeq ($(ARCH_PACKAGES),riscv64)
PKG_HASH:=0b755b2613e2a66b3ca5d6db20b9207a60cd0ac45fd166df6aa6a2d13acc24cb
PKG_HASH:=c865813ae10caa53ac5b6c0960b8e6b40de46af72f96c9b1ed992651860c3a4d
else ifeq ($(ARCH_PACKAGES),x86)
PKG_HASH:=7e90b9c94751c17c674a5da0a7cfd7ab28bc6657e5a698e3f7e60ec6d3ae2858
PKG_HASH:=82d4f3d7b58c7a705610948d81cd7c4b3e67090500b00ea148b8e42b9aaf7f4e
else ifeq ($(ARCH_PACKAGES),x86_64)
PKG_HASH:=bfb9d0b7f7f48e7ab6cbcf8389e2b239c68ad608331bd03b0ab68ddf0ebff019
PKG_HASH:=843ab9f486c807e3c95cbe41d5d4be08b8098740d6f3082309df2a9384844269
else
PKG_HASH:=dummy
endif
+2 -2
View File
@@ -21,13 +21,13 @@ define Download/geoip
HASH:=8023379316bca4713dcfa5ba4ea2fe7f4c127fff64a0cb7859d4756142b2c4dc
endef
GEOSITE_VER:=20250523074055
GEOSITE_VER:=20250523165307
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:=02479c2a08974893a97dfac39c33e32e72b5181ffb669b13ce351de9195e9c47
HASH:=b1d02c3b4f90830e8b4bda83a552da6d218407fe6833ddc8bb2c8b5372998c9f
endef
GEOSITE_IRAN_VER:=202505230820
+2779 -3036
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
+12 -4
View File
@@ -483,10 +483,14 @@
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-V2rayU/Pods-V2rayU-resources-${CONFIGURATION}-input-files.xcfilelist",
);
inputPaths = (
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-V2rayU/Pods-V2rayU-resources-${CONFIGURATION}-output-files.xcfilelist",
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-V2rayU/Pods-V2rayU-resources.sh\"\n";
@@ -500,10 +504,14 @@
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-V2rayU/Pods-V2rayU-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
inputPaths = (
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-V2rayU/Pods-V2rayU-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-V2rayU/Pods-V2rayU-frameworks.sh\"\n";
@@ -834,7 +842,7 @@
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 4.2.4;
CURRENT_PROJECT_VERSION = 4.2.6;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=macosx*]" = "";
@@ -846,7 +854,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 11.0;
MARKETING_VERSION = 4.2.4;
MARKETING_VERSION = 4.2.6;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_BUNDLE_IDENTIFIER = net.yanue.V2rayU;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -872,7 +880,7 @@
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 4.2.4;
CURRENT_PROJECT_VERSION = 4.2.6;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=macosx*]" = "";
@@ -884,7 +892,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 11.0;
MARKETING_VERSION = 4.2.4;
MARKETING_VERSION = 4.2.6;
ONLY_ACTIVE_ARCH = NO;
PRODUCT_BUNDLE_IDENTIFIER = net.yanue.V2rayU;
PRODUCT_NAME = "$(TARGET_NAME)";
+199 -123
View File
File diff suppressed because it is too large Load Diff
Regular → Executable
+23
View File
@@ -94,6 +94,7 @@ class ConfigWindowController: NSWindowController, NSWindowDelegate, NSTabViewDel
@IBOutlet weak var kcpView: NSView!
@IBOutlet weak var dsView: NSView!
@IBOutlet weak var wsView: NSView!
@IBOutlet weak var xhttpView: NSView!
@IBOutlet weak var h2View: NSView!
@IBOutlet weak var quicView: NSView!
@IBOutlet weak var grpcView: NSView!
@@ -118,6 +119,9 @@ class ConfigWindowController: NSWindowController, NSWindowDelegate, NSTabViewDel
@IBOutlet weak var wsHost: NSTextField!
@IBOutlet weak var wsPath: NSTextField!
@IBOutlet weak var xhttpMode: NSTextField!
@IBOutlet weak var xhttpPath: NSTextField!
@IBOutlet weak var h2Host: NSTextField!
@IBOutlet weak var h2Path: NSTextField!
@@ -135,6 +139,7 @@ class ConfigWindowController: NSWindowController, NSWindowDelegate, NSTabViewDel
@IBOutlet weak var streamSecurity: NSPopUpButton!
@IBOutlet weak var streamTlsAllowInsecure: NSButton!
@IBOutlet weak var streamTlsServerName: NSTextField!
@IBOutlet weak var streamTlsAlpn: NSTextField!
@IBOutlet weak var streamRealityServerName: NSTextField!
@IBOutlet weak var streamRealityPublicKey: NSTextField!
@IBOutlet weak var streamRealityShortId: NSTextField!
@@ -362,6 +367,12 @@ class ConfigWindowController: NSWindowController, NSWindowDelegate, NSTabViewDel
// tls
v2rayConfig.securityTls.allowInsecure = self.streamTlsAllowInsecure.state.rawValue > 0
v2rayConfig.securityTls.serverName = self.streamTlsServerName.stringValue
let streamTlsAlpn = self.streamTlsAlpn.stringValue
if streamTlsAlpn.count != 0 {
v2rayConfig.securityTls.alpn = [streamTlsAlpn]
} else {
v2rayConfig.securityTls.alpn = []
}
// reality
v2rayConfig.securityReality.serverName = self.streamRealityServerName.stringValue
v2rayConfig.securityReality.publicKey = self.streamRealityPublicKey.stringValue
@@ -399,6 +410,10 @@ class ConfigWindowController: NSWindowController, NSWindowDelegate, NSTabViewDel
}
v2rayConfig.streamH2.path = self.h2Path.stringValue
// xhttp
v2rayConfig.streamXhttp.mode = self.xhttpMode.stringValue
v2rayConfig.streamXhttp.path = self.xhttpPath.stringValue
// ws
v2rayConfig.streamWs.path = self.wsPath.stringValue
v2rayConfig.streamWs.headers.host = self.wsHost.stringValue
@@ -496,6 +511,7 @@ class ConfigWindowController: NSWindowController, NSWindowDelegate, NSTabViewDel
self.streamSecurity.selectItem(withTitle: v2rayConfig.streamSecurity)
self.streamTlsAllowInsecure.intValue = v2rayConfig.securityTls.allowInsecure ? 1 : 0
self.streamTlsServerName.stringValue = v2rayConfig.securityTls.serverName
self.streamTlsAlpn.stringValue = v2rayConfig.securityTls.alpn.count > 0 ? v2rayConfig.securityTls.alpn[0] : ""
// reality
self.streamRealityServerName.stringValue = v2rayConfig.securityReality.serverName
@@ -530,6 +546,10 @@ class ConfigWindowController: NSWindowController, NSWindowDelegate, NSTabViewDel
self.h2Host.stringValue = v2rayConfig.streamH2.host.count > 0 ? v2rayConfig.streamH2.host[0] : ""
self.h2Path.stringValue = v2rayConfig.streamH2.path
// xhttp
self.xhttpMode.stringValue = v2rayConfig.streamXhttp.mode
self.xhttpPath.stringValue = v2rayConfig.streamXhttp.path
// ws
self.wsPath.stringValue = v2rayConfig.streamWs.path
self.wsHost.stringValue = v2rayConfig.streamWs.headers.host
@@ -738,6 +758,9 @@ class ConfigWindowController: NSWindowController, NSWindowDelegate, NSTabViewDel
case "ws":
self.wsView.isHidden = false
break;
case "xhttp":
self.xhttpView.isHidden = false
break;
case "h2":
self.h2View.isHidden = false
break;
+25 -3
View File
@@ -115,6 +115,7 @@ class V2rayConfig: NSObject {
var streamKcp = KcpSettings()
var streamDs = DsSettings()
var streamWs = WsSettings()
var streamXhttp = XhttpSettings()
var streamH2 = HttpSettings()
var streamQuic = QuicSettings()
var streamGrpc = GrpcSettings()
@@ -462,6 +463,9 @@ class V2rayConfig: NSObject {
case .kcp:
streamSettings.kcpSettings = self.streamKcp
break
case .xhttp:
streamSettings.xhttpSettings = self.streamXhttp
break
case .http, .h2:
streamSettings.httpSettings = self.streamH2
break
@@ -885,6 +889,7 @@ class V2rayConfig: NSObject {
stream.tcpSettings = transport.tcpSettings
stream.kcpSettings = transport.kcpSettings
stream.wsSettings = transport.wsSettings
stream.xhttpSettings = transport.xhttpSettings
stream.httpSettings = transport.httpSettings
stream.dsSettings = transport.dsSettings
stream.quicSettings = transport.quicSettings
@@ -902,6 +907,7 @@ class V2rayConfig: NSObject {
self.securityTls.serverName = transport.tlsSettings!.serverName
self.securityTls.fingerprint = transport.tlsSettings!.fingerprint
self.securityTls.allowInsecure = transport.tlsSettings!.allowInsecure
self.securityTls.alpn = transport.tlsSettings!.alpn
}
if transport.realitySettings != nil {
@@ -929,6 +935,10 @@ class V2rayConfig: NSObject {
self.streamWs = transport.wsSettings!
}
if transport.xhttpSettings != nil {
self.streamXhttp = transport.xhttpSettings!
}
if transport.httpSettings != nil {
self.streamH2 = transport.httpSettings!
}
@@ -956,7 +966,9 @@ class V2rayConfig: NSObject {
let settings = streamJson["tlsSettings"]
var tlsSettings = TlsSettings()
tlsSettings.serverName = settings["serverName"].stringValue
tlsSettings.alpn = settings["alpn"].stringValue
tlsSettings.alpn = settings["alpn"].arrayValue.map {
$0.stringValue
}
tlsSettings.allowInsecure = settings["allowInsecure"].boolValue
tlsSettings.allowInsecureCiphers = settings["allowInsecureCiphers"].boolValue
// certificates
@@ -981,7 +993,9 @@ class V2rayConfig: NSObject {
var tlsSettings = TlsSettings()
tlsSettings.serverName = settings["serverName"].stringValue
tlsSettings.fingerprint = settings["fingerprint"].stringValue // 使 uTLS TLS
tlsSettings.alpn = settings["alpn"].stringValue
tlsSettings.alpn = settings["alpn"].arrayValue.map {
$0.stringValue
}
tlsSettings.allowInsecure = settings["allowInsecure"].boolValue
tlsSettings.allowInsecureCiphers = settings["allowInsecureCiphers"].boolValue
// certificates
@@ -1062,7 +1076,7 @@ class V2rayConfig: NSObject {
// response
if streamJson["tcpSettings"]["header"]["response"].dictionaryValue.count > 0 {
var responseJson = streamJson["tcpSettings"]["header"]["response"]
let responseJson = streamJson["tcpSettings"]["header"]["response"]
var tcpResponse = TcpSettingHeaderResponse()
tcpResponse.version = responseJson["version"].stringValue
@@ -1128,6 +1142,14 @@ class V2rayConfig: NSObject {
stream.httpSettings = httpSettings
}
// xhttpSettings
if streamJson["xhttpSettings"].dictionaryValue.count > 0 && streamJson["xhttpSettings"].dictionaryValue.count > 0 {
var xhttpSettings = XhttpSettings()
xhttpSettings.mode = streamJson["xhttpSettings"]["mode"].stringValue
xhttpSettings.path = streamJson["xhttpSettings"]["path"].stringValue
stream.xhttpSettings = xhttpSettings
}
// dsSettings
if streamJson["dsSettings"].dictionaryValue.count > 0 && streamJson["dsSettings"].dictionaryValue.count > 0 {
+9 -1
View File
@@ -15,6 +15,7 @@ struct V2rayTransport: Codable {
var tcpSettings: TcpSettings?
var kcpSettings: KcpSettings?
var wsSettings: WsSettings?
var xhttpSettings: XhttpSettings?
var httpSettings: HttpSettings?
var dsSettings: DsSettings?
var quicSettings: QuicSettings?
@@ -26,6 +27,7 @@ struct V2rayStreamSettings: Codable {
case tcp
case kcp
case ws
case xhttp
case http
case h2
case domainsocket
@@ -47,6 +49,7 @@ struct V2rayStreamSettings: Codable {
var tcpSettings: TcpSettings?
var kcpSettings: KcpSettings?
var wsSettings: WsSettings?
var xhttpSettings: XhttpSettings?
var httpSettings: HttpSettings?
var dsSettings: DsSettings?
var quicSettings: QuicSettings?
@@ -62,7 +65,7 @@ struct TlsSettings: Codable {
var allowInsecure: Bool = true
var allowInsecureCiphers: Bool?
var certificates: TlsCertificates?
var alpn: String?
var alpn: [String] = [""]
var fingerprint: String = "chrome" // 使 tls TLS
}
@@ -167,6 +170,11 @@ struct WsSettingsHeader: Codable {
var host: String = ""
}
struct XhttpSettings: Codable {
var mode: String = ""
var path: String = ""
}
struct HttpSettings: Codable {
var host: [String] = [""]
var path: String = ""
+50 -6
View File
@@ -20,7 +20,6 @@ from ..utils import (
remove_end,
str_or_none,
strip_or_none,
traverse_obj,
truncate_string,
try_call,
try_get,
@@ -29,6 +28,7 @@ from ..utils import (
url_or_none,
xpath_text,
)
from ..utils.traversal import require, traverse_obj
class TwitterBaseIE(InfoExtractor):
@@ -1596,8 +1596,8 @@ class TwitterAmplifyIE(TwitterBaseIE):
class TwitterBroadcastIE(TwitterBaseIE, PeriscopeBaseIE):
IE_NAME = 'twitter:broadcast'
_VALID_URL = TwitterBaseIE._BASE_REGEX + r'i/broadcasts/(?P<id>[0-9a-zA-Z]{13})'
_VALID_URL = TwitterBaseIE._BASE_REGEX + r'i/(?P<type>broadcasts|events)/(?P<id>\w+)'
_TESTS = [{
# untitled Periscope video
'url': 'https://twitter.com/i/broadcasts/1yNGaQLWpejGj',
@@ -1605,6 +1605,7 @@ class TwitterBroadcastIE(TwitterBaseIE, PeriscopeBaseIE):
'id': '1yNGaQLWpejGj',
'ext': 'mp4',
'title': 'Andrea May Sahouri - Periscope Broadcast',
'display_id': '1yNGaQLWpejGj',
'uploader': 'Andrea May Sahouri',
'uploader_id': 'andreamsahouri',
'uploader_url': 'https://twitter.com/andreamsahouri',
@@ -1612,6 +1613,8 @@ class TwitterBroadcastIE(TwitterBaseIE, PeriscopeBaseIE):
'upload_date': '20200601',
'thumbnail': r're:^https?://[^?#]+\.jpg\?token=',
'view_count': int,
'concurrent_view_count': int,
'live_status': 'was_live',
},
}, {
'url': 'https://twitter.com/i/broadcasts/1ZkKzeyrPbaxv',
@@ -1619,6 +1622,7 @@ class TwitterBroadcastIE(TwitterBaseIE, PeriscopeBaseIE):
'id': '1ZkKzeyrPbaxv',
'ext': 'mp4',
'title': 'Starship | SN10 | High-Altitude Flight Test',
'display_id': '1ZkKzeyrPbaxv',
'uploader': 'SpaceX',
'uploader_id': 'SpaceX',
'uploader_url': 'https://twitter.com/SpaceX',
@@ -1626,6 +1630,8 @@ class TwitterBroadcastIE(TwitterBaseIE, PeriscopeBaseIE):
'upload_date': '20210303',
'thumbnail': r're:^https?://[^?#]+\.jpg\?token=',
'view_count': int,
'concurrent_view_count': int,
'live_status': 'was_live',
},
}, {
'url': 'https://twitter.com/i/broadcasts/1OyKAVQrgzwGb',
@@ -1633,6 +1639,7 @@ class TwitterBroadcastIE(TwitterBaseIE, PeriscopeBaseIE):
'id': '1OyKAVQrgzwGb',
'ext': 'mp4',
'title': 'Starship Flight Test',
'display_id': '1OyKAVQrgzwGb',
'uploader': 'SpaceX',
'uploader_id': 'SpaceX',
'uploader_url': 'https://twitter.com/SpaceX',
@@ -1640,21 +1647,58 @@ class TwitterBroadcastIE(TwitterBaseIE, PeriscopeBaseIE):
'upload_date': '20230420',
'thumbnail': r're:^https?://[^?#]+\.jpg\?token=',
'view_count': int,
'concurrent_view_count': int,
'live_status': 'was_live',
},
}, {
'url': 'https://x.com/i/events/1910629646300762112',
'info_dict': {
'id': '1LyxBWDRNqyKN',
'ext': 'mp4',
'title': '#ガンニバル ウォッチパーティー',
'concurrent_view_count': int,
'display_id': '1910629646300762112',
'live_status': 'was_live',
'release_date': '20250423',
'release_timestamp': 1745409000,
'tags': ['ガンニバル'],
'thumbnail': r're:https?://[^?#]+\.jpg\?token=',
'timestamp': 1745403328,
'upload_date': '20250423',
'uploader': 'ディズニープラス公式',
'uploader_id': 'DisneyPlusJP',
'uploader_url': 'https://twitter.com/DisneyPlusJP',
'view_count': int,
},
}]
def _real_extract(self, url):
broadcast_id = self._match_id(url)
broadcast_type, display_id = self._match_valid_url(url).group('type', 'id')
if broadcast_type == 'events':
timeline = self._call_api(
f'live_event/1/{display_id}/timeline.json', display_id)
broadcast_id = traverse_obj(timeline, (
'twitter_objects', 'broadcasts', ..., ('id', 'broadcast_id'),
{str}, any, {require('broadcast ID')}))
else:
broadcast_id = display_id
broadcast = self._call_api(
'broadcasts/show.json', broadcast_id,
{'ids': broadcast_id})['broadcasts'][broadcast_id]
if not broadcast:
raise ExtractorError('Broadcast no longer exists', expected=True)
info = self._parse_broadcast_data(broadcast, broadcast_id)
info['title'] = broadcast.get('status') or info.get('title')
info['uploader_id'] = broadcast.get('twitter_username') or info.get('uploader_id')
info['uploader_url'] = format_field(broadcast, 'twitter_username', 'https://twitter.com/%s', default=None)
info.update({
'display_id': display_id,
'title': broadcast.get('status') or info.get('title'),
'uploader_id': broadcast.get('twitter_username') or info.get('uploader_id'),
'uploader_url': format_field(
broadcast, 'twitter_username', 'https://twitter.com/%s', default=None),
})
if info['live_status'] == 'is_upcoming':
self.raise_no_formats('This live broadcast has not yet started', expected=True)
return info
media_key = broadcast['media_key']