mirror of
https://github.com/bolucat/Archive.git
synced 2026-04-22 16:07:49 +08:00
Update On Sat May 24 20:33:34 CEST 2025
This commit is contained in:
@@ -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=
|
||||
|
||||
@@ -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
@@ -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
@@ -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=
|
||||
|
||||
@@ -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)
|
||||
|
||||
Generated
+9
-8
@@ -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",
|
||||
|
||||
@@ -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,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"
|
||||
}
|
||||
|
||||
Generated
+47
-55
@@ -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
|
||||
|
||||
@@ -77,13 +77,9 @@
|
||||
- 异步化配置:优化端口查找和配置保存逻辑
|
||||
- 重构事件通知机制到独立线程,避免前端卡死
|
||||
- 优化端口设置,每个端口可随机设置端口号
|
||||
- 优化了随机端口和密钥机制,防止随机时卡死!
|
||||
- 优化了保存机制,使用平滑函数,防止客户端卡死!
|
||||
- 优化端口设置退出和保存机制!
|
||||
- 强制为 Mihomo 配置补全并覆盖 external-controller-cors 字段,默认不允许跨域和仅本地请求,提升 cors 安全性,升级配置时自动覆盖。
|
||||
- 优化了 随机API端口和密钥刷新按钮
|
||||
- 简化了 随机API端口和密钥(重启即可刷新)
|
||||
- 增强了 随机API端口和密钥的安全生成
|
||||
|
||||
## v2.2.3
|
||||
|
||||
|
||||
Generated
-3
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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=
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
Generated
+6
-6
@@ -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
@@ -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)"
|
||||
|
||||
@@ -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())))
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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=
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
Binary file not shown.
+73037
-18584
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
@@ -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)";
|
||||
|
||||
Regular → Executable
+199
-123
File diff suppressed because it is too large
Load Diff
Regular → Executable
+23
@@ -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;
|
||||
|
||||
Regular → Executable
+25
-3
@@ -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 {
|
||||
|
||||
Regular → Executable
+9
-1
@@ -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 = ""
|
||||
|
||||
@@ -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']
|
||||
|
||||
Reference in New Issue
Block a user