mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-10-21 00:34:24 +00:00
Compare commits
28
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
43f96c4279 | ||
|
|
d3fc35a529 | ||
|
|
bc200170b2 | ||
|
|
6158af1858 | ||
|
|
f4bab7ee92 | ||
|
|
e5bc72f8ed | ||
|
|
985a48d399 | ||
|
|
f34c3c937e | ||
|
|
faeed289a8 | ||
|
|
3e6124fb17 | ||
|
|
f2f4bd4efb | ||
|
|
87317565a3 | ||
|
|
146793bbe5 | ||
|
|
a0b6fcf5c6 | ||
|
|
5cf02687e1 | ||
|
|
29d157d383 | ||
|
|
89afd175e0 | ||
|
|
961277a988 | ||
|
|
1e9dfb8335 | ||
|
|
9982f29cd7 | ||
|
|
fc90e49924 | ||
|
|
c017a69bc9 | ||
|
|
4377d381c8 | ||
|
|
92d245ad04 | ||
|
|
0908627297 | ||
|
|
7f79458b4f | ||
|
|
9b4c11ba95 | ||
|
|
27c31eac5d |
@@ -61,9 +61,6 @@ proto_install:
|
||||
go install -v google.golang.org/protobuf/cmd/protoc-gen-go@latest
|
||||
go install -v google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
|
||||
|
||||
update_public_suffix:
|
||||
go generate common/tlsfragment/public_suffix.go
|
||||
|
||||
update_certificates:
|
||||
go run ./cmd/internal/update_certificates
|
||||
|
||||
|
||||
@@ -50,17 +50,17 @@ type CacheFile interface {
|
||||
StoreSelected(group string, selected string) error
|
||||
LoadGroupExpand(group string) (isExpand bool, loaded bool)
|
||||
StoreGroupExpand(group string, expand bool) error
|
||||
LoadRuleSet(tag string) *SavedRuleSet
|
||||
SaveRuleSet(tag string, set *SavedRuleSet) error
|
||||
LoadRuleSet(tag string) *SavedBinary
|
||||
SaveRuleSet(tag string, set *SavedBinary) error
|
||||
}
|
||||
|
||||
type SavedRuleSet struct {
|
||||
type SavedBinary struct {
|
||||
Content []byte
|
||||
LastUpdated time.Time
|
||||
LastEtag string
|
||||
}
|
||||
|
||||
func (s *SavedRuleSet) MarshalBinary() ([]byte, error) {
|
||||
func (s *SavedBinary) MarshalBinary() ([]byte, error) {
|
||||
var buffer bytes.Buffer
|
||||
err := binary.Write(&buffer, binary.BigEndian, uint8(1))
|
||||
if err != nil {
|
||||
@@ -81,7 +81,7 @@ func (s *SavedRuleSet) MarshalBinary() ([]byte, error) {
|
||||
return buffer.Bytes(), nil
|
||||
}
|
||||
|
||||
func (s *SavedRuleSet) UnmarshalBinary(data []byte) error {
|
||||
func (s *SavedBinary) UnmarshalBinary(data []byte) error {
|
||||
reader := bytes.NewReader(data)
|
||||
var version uint8
|
||||
err := binary.Read(reader, binary.BigEndian, &version)
|
||||
|
||||
@@ -202,7 +202,7 @@ func New(options Options) (*Box, error) {
|
||||
transportOptions.Options,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "initialize inbound[", i, "]")
|
||||
return nil, E.Cause(err, "initialize DNS server[", i, "]")
|
||||
}
|
||||
}
|
||||
err = dnsRouter.Initialize(dnsOptions.Rules)
|
||||
@@ -225,7 +225,7 @@ func New(options Options) (*Box, error) {
|
||||
endpointOptions.Options,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "initialize inbound[", i, "]")
|
||||
return nil, E.Cause(err, "initialize endpoint[", i, "]")
|
||||
}
|
||||
}
|
||||
for i, inboundOptions := range options.Inbounds {
|
||||
|
||||
+1
-1
Submodule clients/android updated: 599d8cecac...6df2f60c3a
+1
-1
Submodule clients/apple updated: e43e3a3f64...17fed6b9fc
@@ -30,7 +30,7 @@ func init() {
|
||||
}
|
||||
|
||||
func generateTLSKeyPair(serverName string) error {
|
||||
privateKeyPem, publicKeyPem, err := tls.GenerateKeyPair(time.Now, serverName, time.Now().AddDate(0, flagGenerateTLSKeyPairMonths, 0))
|
||||
privateKeyPem, publicKeyPem, err := tls.GenerateCertificate(nil, nil, time.Now, serverName, time.Now().AddDate(0, flagGenerateTLSKeyPairMonths, 0))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@ type DefaultDialer struct {
|
||||
udpListener net.ListenConfig
|
||||
udpAddr4 string
|
||||
udpAddr6 string
|
||||
isWireGuardListener bool
|
||||
networkManager adapter.NetworkManager
|
||||
networkStrategy *C.NetworkStrategy
|
||||
defaultNetworkStrategy bool
|
||||
@@ -183,11 +182,6 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial
|
||||
}
|
||||
setMultiPathTCP(&dialer4)
|
||||
}
|
||||
if options.IsWireGuardListener {
|
||||
for _, controlFn := range WgControlFns {
|
||||
listener.Control = control.Append(listener.Control, controlFn)
|
||||
}
|
||||
}
|
||||
tcpDialer4, err := newTCPDialer(dialer4, options.TCPFastOpen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -204,7 +198,6 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial
|
||||
udpListener: listener,
|
||||
udpAddr4: udpAddr4,
|
||||
udpAddr6: udpAddr6,
|
||||
isWireGuardListener: options.IsWireGuardListener,
|
||||
networkManager: networkManager,
|
||||
networkStrategy: networkStrategy,
|
||||
defaultNetworkStrategy: defaultNetworkStrategy,
|
||||
|
||||
@@ -29,16 +29,18 @@ func (d *DetourDialer) Start() error {
|
||||
}
|
||||
|
||||
func (d *DetourDialer) Dialer() (N.Dialer, error) {
|
||||
d.initOnce.Do(func() {
|
||||
var loaded bool
|
||||
d.dialer, loaded = d.outboundManager.Outbound(d.detour)
|
||||
if !loaded {
|
||||
d.initErr = E.New("outbound detour not found: ", d.detour)
|
||||
}
|
||||
})
|
||||
d.initOnce.Do(d.init)
|
||||
return d.dialer, d.initErr
|
||||
}
|
||||
|
||||
func (d *DetourDialer) init() {
|
||||
var loaded bool
|
||||
d.dialer, loaded = d.outboundManager.Outbound(d.detour)
|
||||
if !loaded {
|
||||
d.initErr = E.New("outbound detour not found: ", d.detour)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DetourDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
dialer, err := d.Dialer()
|
||||
if err != nil {
|
||||
|
||||
+50
-26
@@ -16,59 +16,82 @@ import (
|
||||
"github.com/sagernet/sing/service"
|
||||
)
|
||||
|
||||
type Options struct {
|
||||
Context context.Context
|
||||
Options option.DialerOptions
|
||||
RemoteIsDomain bool
|
||||
DirectResolver bool
|
||||
ResolverOnDetour bool
|
||||
}
|
||||
|
||||
// TODO: merge with NewWithOptions
|
||||
func New(ctx context.Context, options option.DialerOptions, remoteIsDomain bool) (N.Dialer, error) {
|
||||
if options.IsWireGuardListener {
|
||||
return NewDefault(ctx, options)
|
||||
}
|
||||
return NewWithOptions(Options{
|
||||
Context: ctx,
|
||||
Options: options,
|
||||
RemoteIsDomain: remoteIsDomain,
|
||||
})
|
||||
}
|
||||
|
||||
func NewWithOptions(options Options) (N.Dialer, error) {
|
||||
dialOptions := options.Options
|
||||
var (
|
||||
dialer N.Dialer
|
||||
err error
|
||||
)
|
||||
if options.Detour != "" {
|
||||
outboundManager := service.FromContext[adapter.OutboundManager](ctx)
|
||||
if dialOptions.Detour != "" {
|
||||
outboundManager := service.FromContext[adapter.OutboundManager](options.Context)
|
||||
if outboundManager == nil {
|
||||
return nil, E.New("missing outbound manager")
|
||||
}
|
||||
dialer = NewDetour(outboundManager, options.Detour)
|
||||
dialer = NewDetour(outboundManager, dialOptions.Detour)
|
||||
} else {
|
||||
dialer, err = NewDefault(ctx, options)
|
||||
dialer, err = NewDefault(options.Context, dialOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if remoteIsDomain && options.Detour == "" {
|
||||
networkManager := service.FromContext[adapter.NetworkManager](ctx)
|
||||
dnsTransport := service.FromContext[adapter.DNSTransportManager](ctx)
|
||||
if options.RemoteIsDomain && (dialOptions.Detour == "" || options.ResolverOnDetour) {
|
||||
networkManager := service.FromContext[adapter.NetworkManager](options.Context)
|
||||
dnsTransport := service.FromContext[adapter.DNSTransportManager](options.Context)
|
||||
var defaultOptions adapter.NetworkOptions
|
||||
if networkManager != nil {
|
||||
defaultOptions = networkManager.DefaultOptions()
|
||||
}
|
||||
var (
|
||||
server string
|
||||
dnsQueryOptions adapter.DNSQueryOptions
|
||||
resolveFallbackDelay time.Duration
|
||||
)
|
||||
if options.DomainResolver != nil && options.DomainResolver.Server != "" {
|
||||
transport, loaded := dnsTransport.Transport(options.DomainResolver.Server)
|
||||
if !loaded {
|
||||
return nil, E.New("domain resolver not found: " + options.DomainResolver.Server)
|
||||
if dialOptions.DomainResolver != nil && dialOptions.DomainResolver.Server != "" {
|
||||
var transport adapter.DNSTransport
|
||||
if !options.DirectResolver {
|
||||
var loaded bool
|
||||
transport, loaded = dnsTransport.Transport(dialOptions.DomainResolver.Server)
|
||||
if !loaded {
|
||||
return nil, E.New("domain resolver not found: " + dialOptions.DomainResolver.Server)
|
||||
}
|
||||
}
|
||||
var strategy C.DomainStrategy
|
||||
if options.DomainResolver.Strategy != option.DomainStrategy(C.DomainStrategyAsIS) {
|
||||
strategy = C.DomainStrategy(options.DomainResolver.Strategy)
|
||||
if dialOptions.DomainResolver.Strategy != option.DomainStrategy(C.DomainStrategyAsIS) {
|
||||
strategy = C.DomainStrategy(dialOptions.DomainResolver.Strategy)
|
||||
} else if
|
||||
//nolint:staticcheck
|
||||
options.DomainStrategy != option.DomainStrategy(C.DomainStrategyAsIS) {
|
||||
dialOptions.DomainStrategy != option.DomainStrategy(C.DomainStrategyAsIS) {
|
||||
//nolint:staticcheck
|
||||
strategy = C.DomainStrategy(options.DomainStrategy)
|
||||
strategy = C.DomainStrategy(dialOptions.DomainStrategy)
|
||||
}
|
||||
server = dialOptions.DomainResolver.Server
|
||||
dnsQueryOptions = adapter.DNSQueryOptions{
|
||||
Transport: transport,
|
||||
Strategy: strategy,
|
||||
DisableCache: options.DomainResolver.DisableCache,
|
||||
RewriteTTL: options.DomainResolver.RewriteTTL,
|
||||
ClientSubnet: options.DomainResolver.ClientSubnet.Build(netip.Prefix{}),
|
||||
DisableCache: dialOptions.DomainResolver.DisableCache,
|
||||
RewriteTTL: dialOptions.DomainResolver.RewriteTTL,
|
||||
ClientSubnet: dialOptions.DomainResolver.ClientSubnet.Build(netip.Prefix{}),
|
||||
}
|
||||
resolveFallbackDelay = time.Duration(options.FallbackDelay)
|
||||
resolveFallbackDelay = time.Duration(dialOptions.FallbackDelay)
|
||||
} else if options.DirectResolver {
|
||||
return nil, E.New("missing domain resolver for domain server address")
|
||||
} else if defaultOptions.DomainResolver != "" {
|
||||
dnsQueryOptions = defaultOptions.DomainResolveOptions
|
||||
transport, loaded := dnsTransport.Transport(defaultOptions.DomainResolver)
|
||||
@@ -76,14 +99,15 @@ func New(ctx context.Context, options option.DialerOptions, remoteIsDomain bool)
|
||||
return nil, E.New("default domain resolver not found: " + defaultOptions.DomainResolver)
|
||||
}
|
||||
dnsQueryOptions.Transport = transport
|
||||
resolveFallbackDelay = time.Duration(options.FallbackDelay)
|
||||
resolveFallbackDelay = time.Duration(dialOptions.FallbackDelay)
|
||||
} else {
|
||||
deprecated.Report(ctx, deprecated.OptionMissingDomainResolver)
|
||||
deprecated.Report(options.Context, deprecated.OptionMissingDomainResolver)
|
||||
}
|
||||
dialer = NewResolveDialer(
|
||||
ctx,
|
||||
options.Context,
|
||||
dialer,
|
||||
options.Detour == "" && !options.TCPFastOpen,
|
||||
dialOptions.Detour == "" && !dialOptions.TCPFastOpen,
|
||||
server,
|
||||
dnsQueryOptions,
|
||||
resolveFallbackDelay,
|
||||
)
|
||||
|
||||
+55
-12
@@ -3,12 +3,14 @@ package dialer
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/service"
|
||||
@@ -30,20 +32,26 @@ type ParallelInterfaceResolveDialer interface {
|
||||
}
|
||||
|
||||
type resolveDialer struct {
|
||||
transport adapter.DNSTransportManager
|
||||
router adapter.DNSRouter
|
||||
dialer N.Dialer
|
||||
parallel bool
|
||||
server string
|
||||
initOnce sync.Once
|
||||
initErr error
|
||||
queryOptions adapter.DNSQueryOptions
|
||||
fallbackDelay time.Duration
|
||||
}
|
||||
|
||||
func NewResolveDialer(ctx context.Context, dialer N.Dialer, parallel bool, queryOptions adapter.DNSQueryOptions, fallbackDelay time.Duration) ResolveDialer {
|
||||
func NewResolveDialer(ctx context.Context, dialer N.Dialer, parallel bool, server string, queryOptions adapter.DNSQueryOptions, fallbackDelay time.Duration) ResolveDialer {
|
||||
return &resolveDialer{
|
||||
service.FromContext[adapter.DNSRouter](ctx),
|
||||
dialer,
|
||||
parallel,
|
||||
queryOptions,
|
||||
fallbackDelay,
|
||||
transport: service.FromContext[adapter.DNSTransportManager](ctx),
|
||||
router: service.FromContext[adapter.DNSRouter](ctx),
|
||||
dialer: dialer,
|
||||
parallel: parallel,
|
||||
server: server,
|
||||
queryOptions: queryOptions,
|
||||
fallbackDelay: fallbackDelay,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,20 +60,43 @@ type resolveParallelNetworkDialer struct {
|
||||
dialer ParallelInterfaceDialer
|
||||
}
|
||||
|
||||
func NewResolveParallelInterfaceDialer(ctx context.Context, dialer ParallelInterfaceDialer, parallel bool, queryOptions adapter.DNSQueryOptions, fallbackDelay time.Duration) ParallelInterfaceResolveDialer {
|
||||
func NewResolveParallelInterfaceDialer(ctx context.Context, dialer ParallelInterfaceDialer, parallel bool, server string, queryOptions adapter.DNSQueryOptions, fallbackDelay time.Duration) ParallelInterfaceResolveDialer {
|
||||
return &resolveParallelNetworkDialer{
|
||||
resolveDialer{
|
||||
service.FromContext[adapter.DNSRouter](ctx),
|
||||
dialer,
|
||||
parallel,
|
||||
queryOptions,
|
||||
fallbackDelay,
|
||||
transport: service.FromContext[adapter.DNSTransportManager](ctx),
|
||||
router: service.FromContext[adapter.DNSRouter](ctx),
|
||||
dialer: dialer,
|
||||
parallel: parallel,
|
||||
server: server,
|
||||
queryOptions: queryOptions,
|
||||
fallbackDelay: fallbackDelay,
|
||||
},
|
||||
dialer,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *resolveDialer) initialize() error {
|
||||
d.initOnce.Do(d.initServer)
|
||||
return d.initErr
|
||||
}
|
||||
|
||||
func (d *resolveDialer) initServer() {
|
||||
if d.server == "" {
|
||||
return
|
||||
}
|
||||
transport, loaded := d.transport.Transport(d.server)
|
||||
if !loaded {
|
||||
d.initErr = E.New("domain resolver not found: " + d.server)
|
||||
return
|
||||
}
|
||||
d.queryOptions.Transport = transport
|
||||
}
|
||||
|
||||
func (d *resolveDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
err := d.initialize()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !destination.IsFqdn() {
|
||||
return d.dialer.DialContext(ctx, network, destination)
|
||||
}
|
||||
@@ -82,6 +113,10 @@ func (d *resolveDialer) DialContext(ctx context.Context, network string, destina
|
||||
}
|
||||
|
||||
func (d *resolveDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
err := d.initialize()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !destination.IsFqdn() {
|
||||
return d.dialer.ListenPacket(ctx, destination)
|
||||
}
|
||||
@@ -106,6 +141,10 @@ func (d *resolveDialer) Upstream() any {
|
||||
}
|
||||
|
||||
func (d *resolveParallelNetworkDialer) DialParallelInterface(ctx context.Context, network string, destination M.Socksaddr, strategy *C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.Conn, error) {
|
||||
err := d.initialize()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !destination.IsFqdn() {
|
||||
return d.dialer.DialContext(ctx, network, destination)
|
||||
}
|
||||
@@ -125,6 +164,10 @@ func (d *resolveParallelNetworkDialer) DialParallelInterface(ctx context.Context
|
||||
}
|
||||
|
||||
func (d *resolveParallelNetworkDialer) ListenSerialInterfacePacket(ctx context.Context, destination M.Socksaddr, strategy *C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.PacketConn, error) {
|
||||
err := d.initialize()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !destination.IsFqdn() {
|
||||
return d.dialer.ListenPacket(ctx, destination)
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
func GenerateCertificate(timeFunc func() time.Time, serverName string) (*tls.Certificate, error) {
|
||||
privateKeyPem, publicKeyPem, err := GenerateKeyPair(timeFunc, serverName, timeFunc().Add(time.Hour))
|
||||
func GenerateKeyPair(parent *x509.Certificate, parentKey any, timeFunc func() time.Time, serverName string) (*tls.Certificate, error) {
|
||||
privateKeyPem, publicKeyPem, err := GenerateCertificate(parent, parentKey, timeFunc, serverName, timeFunc().Add(time.Hour))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -23,7 +23,7 @@ func GenerateCertificate(timeFunc func() time.Time, serverName string) (*tls.Cer
|
||||
return &certificate, err
|
||||
}
|
||||
|
||||
func GenerateKeyPair(timeFunc func() time.Time, serverName string, expire time.Time) (privateKeyPem []byte, publicKeyPem []byte, err error) {
|
||||
func GenerateCertificate(parent *x509.Certificate, parentKey any, timeFunc func() time.Time, serverName string, expire time.Time) (privateKeyPem []byte, publicKeyPem []byte, err error) {
|
||||
if timeFunc == nil {
|
||||
timeFunc = time.Now
|
||||
}
|
||||
@@ -47,7 +47,11 @@ func GenerateKeyPair(timeFunc func() time.Time, serverName string, expire time.T
|
||||
},
|
||||
DNSNames: []string{serverName},
|
||||
}
|
||||
publicDer, err := x509.CreateCertificate(rand.Reader, template, template, key.Public(), key)
|
||||
if parent == nil {
|
||||
parent = template
|
||||
parentKey = key
|
||||
}
|
||||
publicDer, err := x509.CreateCertificate(rand.Reader, template, parent, key.Public(), parentKey)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -222,7 +222,7 @@ func NewSTDServer(ctx context.Context, logger log.Logger, options option.Inbound
|
||||
}
|
||||
if certificate == nil && key == nil && options.Insecure {
|
||||
tlsConfig.GetCertificate = func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||
return GenerateCertificate(ntp.TimeFuncFromContext(ctx), info.ServerName)
|
||||
return GenerateKeyPair(nil, nil, ntp.TimeFuncFromContext(ctx), info.ServerName)
|
||||
}
|
||||
} else {
|
||||
if certificate == nil {
|
||||
|
||||
@@ -7,8 +7,9 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing/common"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
|
||||
"golang.org/x/net/publicsuffix"
|
||||
)
|
||||
|
||||
type Conn struct {
|
||||
@@ -42,30 +43,12 @@ func (c *Conn) Write(b []byte) (n int, err error) {
|
||||
return
|
||||
}
|
||||
}
|
||||
splits := strings.Split(string(b[serverName.Index:serverName.Index+serverName.Length]), ".")
|
||||
splits := strings.Split(serverName.ServerName, ".")
|
||||
currentIndex := serverName.Index
|
||||
var striped bool
|
||||
if len(splits) > 3 {
|
||||
suffix := splits[len(splits)-3] + "." + splits[len(splits)-2] + "." + splits[len(splits)-1]
|
||||
if publicSuffixMatcher().Match(suffix) {
|
||||
splits = splits[:len(splits)-3]
|
||||
}
|
||||
striped = true
|
||||
if publicSuffix := publicsuffix.List.PublicSuffix(serverName.ServerName); publicSuffix != "" {
|
||||
splits = splits[:len(splits)-strings.Count(serverName.ServerName, ".")]
|
||||
}
|
||||
if !striped && len(splits) > 2 {
|
||||
suffix := splits[len(splits)-2] + "." + splits[len(splits)-1]
|
||||
if publicSuffixMatcher().Match(suffix) {
|
||||
splits = splits[:len(splits)-2]
|
||||
}
|
||||
striped = true
|
||||
}
|
||||
if !striped && len(splits) > 1 {
|
||||
suffix := splits[len(splits)-1]
|
||||
if publicSuffixMatcher().Match(suffix) {
|
||||
splits = splits[:len(splits)-1]
|
||||
}
|
||||
}
|
||||
if len(splits) > 1 && common.Contains(publicPrefix, splits[0]) {
|
||||
if len(splits) > 1 && splits[0] == "..." {
|
||||
currentIndex += len(splits[0]) + 1
|
||||
splits = splits[1:]
|
||||
}
|
||||
|
||||
@@ -23,9 +23,9 @@ const (
|
||||
)
|
||||
|
||||
type myServerName struct {
|
||||
Index int
|
||||
Length int
|
||||
sex []byte
|
||||
Index int
|
||||
Length int
|
||||
ServerName string
|
||||
}
|
||||
|
||||
func indexTLSServerName(payload []byte) *myServerName {
|
||||
@@ -119,9 +119,9 @@ func indexTLSServerNameFromExtensions(exs []byte) *myServerName {
|
||||
sniLen := uint16(sex[3])<<8 | uint16(sex[4])
|
||||
sex = sex[sniExtensionHeaderLen:]
|
||||
return &myServerName{
|
||||
Index: currentIndex + extensionHeaderLen + sniExtensionHeaderLen,
|
||||
Length: int(sniLen),
|
||||
sex: sex,
|
||||
Index: currentIndex + extensionHeaderLen + sniExtensionHeaderLen,
|
||||
Length: int(sniLen),
|
||||
ServerName: string(sex),
|
||||
}
|
||||
}
|
||||
exs = exs[4+exLen:]
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
package tf
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/domain"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
)
|
||||
|
||||
var publicPrefix = []string{
|
||||
"www",
|
||||
}
|
||||
|
||||
//go:generate wget -O public_suffix_list.dat https://publicsuffix.org/list/public_suffix_list.dat
|
||||
|
||||
//go:embed public_suffix_list.dat
|
||||
var publicSuffix []byte
|
||||
|
||||
var publicSuffixMatcher = common.OnceValue(func() *domain.Matcher {
|
||||
matcher, err := initPublicSuffixMatcher()
|
||||
if err != nil {
|
||||
panic(F.ToString("error in initialize public suffix matcher"))
|
||||
}
|
||||
return matcher
|
||||
})
|
||||
|
||||
func initPublicSuffixMatcher() (*domain.Matcher, error) {
|
||||
reader := bufio.NewReader(bytes.NewReader(publicSuffix))
|
||||
var domainList []string
|
||||
for {
|
||||
line, isPrefix, err := reader.ReadLine()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if isPrefix {
|
||||
return nil, E.New("unexpected prefix line")
|
||||
}
|
||||
lineStr := string(line)
|
||||
lineStr = strings.TrimSpace(lineStr)
|
||||
if lineStr == "" || strings.HasPrefix(lineStr, "//") {
|
||||
continue
|
||||
}
|
||||
domainList = append(domainList, lineStr)
|
||||
}
|
||||
return domain.NewMatcher(domainList, nil, false), nil
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
+6
-5
@@ -252,7 +252,8 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg, options adapte
|
||||
ruleIndex = -1
|
||||
for {
|
||||
dnsCtx := adapter.OverrideContext(ctx)
|
||||
transport, rule, ruleIndex = r.matchDNS(ctx, true, ruleIndex, isAddressQuery(message), &options)
|
||||
dnsOptions := options
|
||||
transport, rule, ruleIndex = r.matchDNS(ctx, true, ruleIndex, isAddressQuery(message), &dnsOptions)
|
||||
if rule != nil {
|
||||
switch action := rule.Action().(type) {
|
||||
case *R.RuleActionReject:
|
||||
@@ -271,10 +272,10 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg, options adapte
|
||||
return rule.MatchAddressLimit(metadata)
|
||||
}
|
||||
}
|
||||
if options.Strategy == C.DomainStrategyAsIS {
|
||||
options.Strategy = r.defaultDomainStrategy
|
||||
if dnsOptions.Strategy == C.DomainStrategyAsIS {
|
||||
dnsOptions.Strategy = r.defaultDomainStrategy
|
||||
}
|
||||
response, err = r.client.Exchange(dnsCtx, transport, message, options, responseCheck)
|
||||
response, err = r.client.Exchange(dnsCtx, transport, message, dnsOptions, responseCheck)
|
||||
var rejected bool
|
||||
if err != nil {
|
||||
if errors.Is(err, ErrResponseRejectedCached) {
|
||||
@@ -300,7 +301,7 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg, options adapte
|
||||
return nil, err
|
||||
}
|
||||
if r.dnsReverseMapping != nil && len(message.Question) > 0 && response != nil && len(response.Answer) > 0 {
|
||||
if transport.Type() != C.DNSTypeFakeIP {
|
||||
if transport == nil || transport.Type() != C.DNSTypeFakeIP {
|
||||
for _, answer := range response.Answer {
|
||||
switch record := answer.(type) {
|
||||
case *mDNS.A:
|
||||
|
||||
@@ -36,7 +36,10 @@ func NewTransport(ctx context.Context, logger log.ContextLogger, tag string, opt
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (t *Transport) Start() error {
|
||||
func (t *Transport) Start(stage adapter.StartStage) error {
|
||||
if stage != adapter.StartStateStart {
|
||||
return nil
|
||||
}
|
||||
return t.store.Start()
|
||||
}
|
||||
|
||||
|
||||
@@ -144,6 +144,13 @@ func (t *UDPTransport) exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.M
|
||||
func (t *UDPTransport) open(ctx context.Context) (*dnsConnection, error) {
|
||||
t.access.Lock()
|
||||
defer t.access.Unlock()
|
||||
if t.conn != nil {
|
||||
select {
|
||||
case <-t.conn.done:
|
||||
default:
|
||||
return t.conn, nil
|
||||
}
|
||||
}
|
||||
conn, err := t.dialer.DialContext(ctx, N.NetworkUDP, t.serverAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -154,6 +161,7 @@ func (t *UDPTransport) open(ctx context.Context) (*dnsConnection, error) {
|
||||
callbacks: make(map[uint16]*dnsCallback),
|
||||
}
|
||||
go t.recvLoop(dnsConn)
|
||||
t.conn = dnsConn
|
||||
return dnsConn, nil
|
||||
}
|
||||
|
||||
@@ -201,8 +209,6 @@ type dnsConnection struct {
|
||||
}
|
||||
|
||||
func (c *dnsConnection) Close(err error) {
|
||||
c.access.Lock()
|
||||
defer c.access.Unlock()
|
||||
c.closeOnce.Do(func() {
|
||||
close(c.done)
|
||||
c.err = err
|
||||
|
||||
@@ -27,9 +27,14 @@ func NewTransportAdapter(transportType string, transportTag string, dependencies
|
||||
}
|
||||
|
||||
func NewTransportAdapterWithLocalOptions(transportType string, transportTag string, localOptions option.LocalDNSServerOptions) TransportAdapter {
|
||||
var dependencies []string
|
||||
if localOptions.DomainResolver != nil && localOptions.DomainResolver.Server != "" {
|
||||
dependencies = append(dependencies, localOptions.DomainResolver.Server)
|
||||
}
|
||||
return TransportAdapter{
|
||||
transportType: transportType,
|
||||
transportTag: transportTag,
|
||||
dependencies: dependencies,
|
||||
strategy: C.DomainStrategy(localOptions.LegacyStrategy),
|
||||
clientSubnet: localOptions.LegacyClientSubnet,
|
||||
}
|
||||
@@ -37,8 +42,11 @@ func NewTransportAdapterWithLocalOptions(transportType string, transportTag stri
|
||||
|
||||
func NewTransportAdapterWithRemoteOptions(transportType string, transportTag string, remoteOptions option.RemoteDNSServerOptions) TransportAdapter {
|
||||
var dependencies []string
|
||||
if remoteOptions.AddressResolver != "" {
|
||||
dependencies = []string{remoteOptions.AddressResolver}
|
||||
if remoteOptions.DomainResolver != nil && remoteOptions.DomainResolver.Server != "" {
|
||||
dependencies = append(dependencies, remoteOptions.DomainResolver.Server)
|
||||
}
|
||||
if remoteOptions.LegacyAddressResolver != "" {
|
||||
dependencies = append(dependencies, remoteOptions.LegacyAddressResolver)
|
||||
}
|
||||
return TransportAdapter{
|
||||
transportType: transportType,
|
||||
|
||||
+29
-27
@@ -19,37 +19,39 @@ func NewLocalDialer(ctx context.Context, options option.LocalDNSServerOptions) (
|
||||
if options.LegacyDefaultDialer {
|
||||
return dialer.NewDefaultOutbound(ctx), nil
|
||||
} else {
|
||||
return dialer.New(ctx, options.DialerOptions, false)
|
||||
return dialer.NewWithOptions(dialer.Options{
|
||||
Context: ctx,
|
||||
Options: options.DialerOptions,
|
||||
DirectResolver: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func NewRemoteDialer(ctx context.Context, options option.RemoteDNSServerOptions) (N.Dialer, error) {
|
||||
var (
|
||||
transportDialer N.Dialer
|
||||
err error
|
||||
)
|
||||
if options.LegacyDefaultDialer {
|
||||
transportDialer = dialer.NewDefaultOutbound(ctx)
|
||||
} else {
|
||||
transportDialer, err = dialer.New(ctx, options.DialerOptions, options.ServerIsDomain())
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if options.AddressResolver != "" {
|
||||
transport := service.FromContext[adapter.DNSTransportManager](ctx)
|
||||
resolverTransport, loaded := transport.Transport(options.AddressResolver)
|
||||
if !loaded {
|
||||
return nil, E.New("address resolver not found: ", options.AddressResolver)
|
||||
transportDialer := dialer.NewDefaultOutbound(ctx)
|
||||
if options.LegacyAddressResolver != "" {
|
||||
transport := service.FromContext[adapter.DNSTransportManager](ctx)
|
||||
resolverTransport, loaded := transport.Transport(options.LegacyAddressResolver)
|
||||
if !loaded {
|
||||
return nil, E.New("address resolver not found: ", options.LegacyAddressResolver)
|
||||
}
|
||||
transportDialer = newTransportDialer(transportDialer, service.FromContext[adapter.DNSRouter](ctx), resolverTransport, C.DomainStrategy(options.LegacyAddressStrategy), time.Duration(options.LegacyAddressFallbackDelay))
|
||||
} else if options.ServerIsDomain() {
|
||||
return nil, E.New("missing address resolver for server: ", options.Server)
|
||||
}
|
||||
transportDialer = NewTransportDialer(transportDialer, service.FromContext[adapter.DNSRouter](ctx), resolverTransport, C.DomainStrategy(options.AddressStrategy), time.Duration(options.AddressFallbackDelay))
|
||||
} else if options.ServerIsDomain() {
|
||||
return nil, E.New("missing address resolver for server: ", options.Server)
|
||||
return transportDialer, nil
|
||||
} else {
|
||||
return dialer.NewWithOptions(dialer.Options{
|
||||
Context: ctx,
|
||||
Options: options.DialerOptions,
|
||||
RemoteIsDomain: options.ServerIsDomain(),
|
||||
DirectResolver: true,
|
||||
})
|
||||
}
|
||||
return transportDialer, nil
|
||||
}
|
||||
|
||||
type TransportDialer struct {
|
||||
type legacyTransportDialer struct {
|
||||
dialer N.Dialer
|
||||
dnsRouter adapter.DNSRouter
|
||||
transport adapter.DNSTransport
|
||||
@@ -57,8 +59,8 @@ type TransportDialer struct {
|
||||
fallbackDelay time.Duration
|
||||
}
|
||||
|
||||
func NewTransportDialer(dialer N.Dialer, dnsRouter adapter.DNSRouter, transport adapter.DNSTransport, strategy C.DomainStrategy, fallbackDelay time.Duration) *TransportDialer {
|
||||
return &TransportDialer{
|
||||
func newTransportDialer(dialer N.Dialer, dnsRouter adapter.DNSRouter, transport adapter.DNSTransport, strategy C.DomainStrategy, fallbackDelay time.Duration) *legacyTransportDialer {
|
||||
return &legacyTransportDialer{
|
||||
dialer,
|
||||
dnsRouter,
|
||||
transport,
|
||||
@@ -67,7 +69,7 @@ func NewTransportDialer(dialer N.Dialer, dnsRouter adapter.DNSRouter, transport
|
||||
}
|
||||
}
|
||||
|
||||
func (d *TransportDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
func (d *legacyTransportDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
if destination.IsIP() {
|
||||
return d.dialer.DialContext(ctx, network, destination)
|
||||
}
|
||||
@@ -81,7 +83,7 @@ func (d *TransportDialer) DialContext(ctx context.Context, network string, desti
|
||||
return N.DialParallel(ctx, d.dialer, network, destination, addresses, d.strategy == C.DomainStrategyPreferIPv6, d.fallbackDelay)
|
||||
}
|
||||
|
||||
func (d *TransportDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
func (d *legacyTransportDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
if destination.IsIP() {
|
||||
return d.dialer.ListenPacket(ctx, destination)
|
||||
}
|
||||
@@ -96,6 +98,6 @@ func (d *TransportDialer) ListenPacket(ctx context.Context, destination M.Socksa
|
||||
return conn, err
|
||||
}
|
||||
|
||||
func (d *TransportDialer) Upstream() any {
|
||||
func (d *legacyTransportDialer) Upstream() any {
|
||||
return d.dialer
|
||||
}
|
||||
|
||||
@@ -2,6 +2,14 @@
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
#### 1.12.0-alpha.3
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
### 1.11.1
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.12.0-alpha.2
|
||||
|
||||
* Update quic-go to v0.49.0
|
||||
|
||||
@@ -284,8 +284,8 @@ func (c *CacheFile) StoreGroupExpand(group string, isExpand bool) error {
|
||||
})
|
||||
}
|
||||
|
||||
func (c *CacheFile) LoadRuleSet(tag string) *adapter.SavedRuleSet {
|
||||
var savedSet adapter.SavedRuleSet
|
||||
func (c *CacheFile) LoadRuleSet(tag string) *adapter.SavedBinary {
|
||||
var savedSet adapter.SavedBinary
|
||||
err := c.DB.View(func(t *bbolt.Tx) error {
|
||||
bucket := c.bucket(t, bucketRuleSet)
|
||||
if bucket == nil {
|
||||
@@ -303,7 +303,7 @@ func (c *CacheFile) LoadRuleSet(tag string) *adapter.SavedRuleSet {
|
||||
return &savedSet
|
||||
}
|
||||
|
||||
func (c *CacheFile) SaveRuleSet(tag string, set *adapter.SavedRuleSet) error {
|
||||
func (c *CacheFile) SaveRuleSet(tag string, set *adapter.SavedBinary) error {
|
||||
return c.DB.Batch(func(t *bbolt.Tx) error {
|
||||
bucket, err := c.createBucket(t, bucketRuleSet)
|
||||
if err != nil {
|
||||
|
||||
@@ -7,11 +7,13 @@ var (
|
||||
|
||||
type Locale struct {
|
||||
// deprecated messages for graphical clients
|
||||
Locale string
|
||||
DeprecatedMessage string
|
||||
DeprecatedMessageNoLink string
|
||||
}
|
||||
|
||||
var defaultLocal = &Locale{
|
||||
Locale: "en_US",
|
||||
DeprecatedMessage: "%s is deprecated in sing-box %s and will be removed in sing-box %s please checkout documentation for migration.",
|
||||
DeprecatedMessageNoLink: "%s is deprecated in sing-box %s and will be removed in sing-box %s.",
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ var warningMessageForEndUsers = "\n\n如果您不明白此消息意味着什么
|
||||
|
||||
func init() {
|
||||
localeRegistry["zh_CN"] = &Locale{
|
||||
Locale: "zh_CN",
|
||||
DeprecatedMessage: "%s 已在 sing-box %s 中被弃用,且将在 sing-box %s 中被移除,请参阅迁移指南。" + warningMessageForEndUsers,
|
||||
DeprecatedMessageNoLink: "%s 已在 sing-box %s 中被弃用,且将在 sing-box %s 中被移除。" + warningMessageForEndUsers,
|
||||
}
|
||||
|
||||
+30
-14
@@ -128,18 +128,34 @@ func (o *NewDNSServerOptions) Upgrade(ctx context.Context) error {
|
||||
} else {
|
||||
serverType = C.DNSTypeUDP
|
||||
}
|
||||
remoteOptions := RemoteDNSServerOptions{
|
||||
LocalDNSServerOptions: LocalDNSServerOptions{
|
||||
DialerOptions: DialerOptions{
|
||||
Detour: options.Detour,
|
||||
var remoteOptions RemoteDNSServerOptions
|
||||
if options.Detour == "" {
|
||||
remoteOptions = RemoteDNSServerOptions{
|
||||
LocalDNSServerOptions: LocalDNSServerOptions{
|
||||
LegacyStrategy: options.Strategy,
|
||||
LegacyDefaultDialer: options.Detour == "",
|
||||
LegacyClientSubnet: options.ClientSubnet.Build(netip.Prefix{}),
|
||||
},
|
||||
LegacyStrategy: options.Strategy,
|
||||
LegacyDefaultDialer: options.Detour == "",
|
||||
LegacyClientSubnet: options.ClientSubnet.Build(netip.Prefix{}),
|
||||
},
|
||||
AddressResolver: options.AddressResolver,
|
||||
AddressStrategy: options.AddressStrategy,
|
||||
AddressFallbackDelay: options.AddressFallbackDelay,
|
||||
LegacyAddressResolver: options.AddressResolver,
|
||||
LegacyAddressStrategy: options.AddressStrategy,
|
||||
LegacyAddressFallbackDelay: options.AddressFallbackDelay,
|
||||
}
|
||||
} else {
|
||||
remoteOptions = RemoteDNSServerOptions{
|
||||
LocalDNSServerOptions: LocalDNSServerOptions{
|
||||
DialerOptions: DialerOptions{
|
||||
Detour: options.Detour,
|
||||
DomainResolver: &DomainResolveOptions{
|
||||
Server: options.AddressResolver,
|
||||
Strategy: options.AddressStrategy,
|
||||
},
|
||||
FallbackDelay: options.AddressFallbackDelay,
|
||||
},
|
||||
LegacyStrategy: options.Strategy,
|
||||
LegacyDefaultDialer: options.Detour == "",
|
||||
LegacyClientSubnet: options.ClientSubnet.Build(netip.Prefix{}),
|
||||
},
|
||||
}
|
||||
}
|
||||
switch serverType {
|
||||
case C.DNSTypeUDP:
|
||||
@@ -274,9 +290,9 @@ type LocalDNSServerOptions struct {
|
||||
type RemoteDNSServerOptions struct {
|
||||
LocalDNSServerOptions
|
||||
ServerOptions
|
||||
AddressResolver string `json:"address_resolver,omitempty"`
|
||||
AddressStrategy DomainStrategy `json:"address_strategy,omitempty"`
|
||||
AddressFallbackDelay badoption.Duration `json:"address_fallback_delay,omitempty"`
|
||||
LegacyAddressResolver string `json:"-"`
|
||||
LegacyAddressStrategy DomainStrategy `json:"-"`
|
||||
LegacyAddressFallbackDelay badoption.Duration `json:"-"`
|
||||
}
|
||||
|
||||
type RemoteTLSDNSServerOptions struct {
|
||||
|
||||
@@ -53,7 +53,14 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||
if options.Detour == "" {
|
||||
options.IsWireGuardListener = true
|
||||
}
|
||||
outboundDialer, err := dialer.New(ctx, options.DialerOptions, false)
|
||||
outboundDialer, err := dialer.NewWithOptions(dialer.Options{
|
||||
Context: ctx,
|
||||
Options: options.DialerOptions,
|
||||
RemoteIsDomain: common.Any(options.Peers, func(it option.WireGuardPeer) bool {
|
||||
return !M.ParseAddr(it.Address).IsValid()
|
||||
}),
|
||||
ResolverOnDetour: true,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -56,7 +56,14 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||
} else if options.GSO {
|
||||
return nil, E.New("gso is conflict with detour")
|
||||
}
|
||||
outboundDialer, err := dialer.New(ctx, options.DialerOptions, options.ServerIsDomain())
|
||||
outboundDialer, err := dialer.NewWithOptions(dialer.Options{
|
||||
Context: ctx,
|
||||
Options: options.DialerOptions,
|
||||
RemoteIsDomain: options.ServerIsDomain() || common.Any(options.Peers, func(it option.LegacyWireGuardPeer) bool {
|
||||
return it.ServerIsDomain()
|
||||
}),
|
||||
ResolverOnDetour: true,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+8
-1
@@ -162,7 +162,7 @@ func (r *Router) Start(stage adapter.StartStage) error {
|
||||
r.started = true
|
||||
return nil
|
||||
case adapter.StartStateStarted:
|
||||
for _, ruleSet := range r.ruleSetMap {
|
||||
for _, ruleSet := range r.ruleSets {
|
||||
ruleSet.Cleanup()
|
||||
}
|
||||
runtime.GC()
|
||||
@@ -180,6 +180,13 @@ func (r *Router) Close() error {
|
||||
})
|
||||
monitor.Finish()
|
||||
}
|
||||
for i, ruleSet := range r.ruleSets {
|
||||
monitor.Start("close rule-set[", i, "]")
|
||||
err = E.Append(err, ruleSet.Close(), func(err error) error {
|
||||
return E.Cause(err, "close rule-set[", i, "]")
|
||||
})
|
||||
monitor.Finish()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/fswatch"
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
@@ -26,14 +27,16 @@ import (
|
||||
var _ adapter.RuleSet = (*LocalRuleSet)(nil)
|
||||
|
||||
type LocalRuleSet struct {
|
||||
ctx context.Context
|
||||
logger logger.Logger
|
||||
tag string
|
||||
rules []adapter.HeadlessRule
|
||||
metadata adapter.RuleSetMetadata
|
||||
fileFormat string
|
||||
watcher *fswatch.Watcher
|
||||
refs atomic.Int32
|
||||
ctx context.Context
|
||||
logger logger.Logger
|
||||
tag string
|
||||
rules []adapter.HeadlessRule
|
||||
metadata adapter.RuleSetMetadata
|
||||
fileFormat string
|
||||
watcher *fswatch.Watcher
|
||||
callbackAccess sync.Mutex
|
||||
callbacks list.List[adapter.RuleSetUpdateCallback]
|
||||
refs atomic.Int32
|
||||
}
|
||||
|
||||
func NewLocalRuleSet(ctx context.Context, logger logger.Logger, options option.RuleSet) (*LocalRuleSet, error) {
|
||||
@@ -52,13 +55,12 @@ func NewLocalRuleSet(ctx context.Context, logger logger.Logger, options option.R
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
err := ruleSet.reloadFile(filemanager.BasePath(ctx, options.LocalOptions.Path))
|
||||
filePath := filemanager.BasePath(ctx, options.LocalOptions.Path)
|
||||
filePath, _ = filepath.Abs(filePath)
|
||||
err := ruleSet.reloadFile(filePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if options.Type == C.RuleSetTypeLocal {
|
||||
filePath, _ := filepath.Abs(options.LocalOptions.Path)
|
||||
watcher, err := fswatch.NewWatcher(fswatch.Options{
|
||||
Path: []string{filePath},
|
||||
Callback: func(path string) {
|
||||
@@ -141,6 +143,12 @@ func (s *LocalRuleSet) reloadRules(headlessRules []option.HeadlessRule) error {
|
||||
metadata.ContainsIPCIDRRule = hasHeadlessRule(headlessRules, isIPCIDRHeadlessRule)
|
||||
s.rules = rules
|
||||
s.metadata = metadata
|
||||
s.callbackAccess.Lock()
|
||||
callbacks := s.callbacks.Array()
|
||||
s.callbackAccess.Unlock()
|
||||
for _, callback := range callbacks {
|
||||
callback(s)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -173,10 +181,15 @@ func (s *LocalRuleSet) Cleanup() {
|
||||
}
|
||||
|
||||
func (s *LocalRuleSet) RegisterCallback(callback adapter.RuleSetUpdateCallback) *list.Element[adapter.RuleSetUpdateCallback] {
|
||||
return nil
|
||||
s.callbackAccess.Lock()
|
||||
defer s.callbackAccess.Unlock()
|
||||
return s.callbacks.PushBack(callback)
|
||||
}
|
||||
|
||||
func (s *LocalRuleSet) UnregisterCallback(element *list.Element[adapter.RuleSetUpdateCallback]) {
|
||||
s.callbackAccess.Lock()
|
||||
defer s.callbackAccess.Unlock()
|
||||
s.callbacks.Remove(element)
|
||||
}
|
||||
|
||||
func (s *LocalRuleSet) Close() error {
|
||||
|
||||
@@ -35,23 +35,23 @@ import (
|
||||
var _ adapter.RuleSet = (*RemoteRuleSet)(nil)
|
||||
|
||||
type RemoteRuleSet struct {
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
outboundManager adapter.OutboundManager
|
||||
logger logger.ContextLogger
|
||||
options option.RuleSet
|
||||
metadata adapter.RuleSetMetadata
|
||||
updateInterval time.Duration
|
||||
dialer N.Dialer
|
||||
rules []adapter.HeadlessRule
|
||||
lastUpdated time.Time
|
||||
lastEtag string
|
||||
updateTicker *time.Ticker
|
||||
cacheFile adapter.CacheFile
|
||||
pauseManager pause.Manager
|
||||
callbackAccess sync.Mutex
|
||||
callbacks list.List[adapter.RuleSetUpdateCallback]
|
||||
refs atomic.Int32
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
logger logger.ContextLogger
|
||||
outbound adapter.OutboundManager
|
||||
options option.RuleSet
|
||||
metadata adapter.RuleSetMetadata
|
||||
updateInterval time.Duration
|
||||
dialer N.Dialer
|
||||
rules []adapter.HeadlessRule
|
||||
lastUpdated time.Time
|
||||
lastEtag string
|
||||
updateTicker *time.Ticker
|
||||
cacheFile adapter.CacheFile
|
||||
pauseManager pause.Manager
|
||||
callbackAccess sync.Mutex
|
||||
callbacks list.List[adapter.RuleSetUpdateCallback]
|
||||
refs atomic.Int32
|
||||
}
|
||||
|
||||
func NewRemoteRuleSet(ctx context.Context, logger logger.ContextLogger, options option.RuleSet) *RemoteRuleSet {
|
||||
@@ -63,13 +63,13 @@ func NewRemoteRuleSet(ctx context.Context, logger logger.ContextLogger, options
|
||||
updateInterval = 24 * time.Hour
|
||||
}
|
||||
return &RemoteRuleSet{
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
outboundManager: service.FromContext[adapter.OutboundManager](ctx),
|
||||
logger: logger,
|
||||
options: options,
|
||||
updateInterval: updateInterval,
|
||||
pauseManager: service.FromContext[pause.Manager](ctx),
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
outbound: service.FromContext[adapter.OutboundManager](ctx),
|
||||
logger: logger,
|
||||
options: options,
|
||||
updateInterval: updateInterval,
|
||||
pauseManager: service.FromContext[pause.Manager](ctx),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,13 +85,13 @@ func (s *RemoteRuleSet) StartContext(ctx context.Context, startContext *adapter.
|
||||
s.cacheFile = service.FromContext[adapter.CacheFile](s.ctx)
|
||||
var dialer N.Dialer
|
||||
if s.options.RemoteOptions.DownloadDetour != "" {
|
||||
outbound, loaded := s.outboundManager.Outbound(s.options.RemoteOptions.DownloadDetour)
|
||||
outbound, loaded := s.outbound.Outbound(s.options.RemoteOptions.DownloadDetour)
|
||||
if !loaded {
|
||||
return E.New("download_detour not found: ", s.options.RemoteOptions.DownloadDetour)
|
||||
return E.New("download detour not found: ", s.options.RemoteOptions.DownloadDetour)
|
||||
}
|
||||
dialer = outbound
|
||||
} else {
|
||||
dialer = s.outboundManager.Default()
|
||||
dialer = s.outbound.Default()
|
||||
}
|
||||
s.dialer = dialer
|
||||
if s.cacheFile != nil {
|
||||
@@ -292,7 +292,7 @@ func (s *RemoteRuleSet) fetchOnce(ctx context.Context, startContext *adapter.HTT
|
||||
}
|
||||
s.lastUpdated = time.Now()
|
||||
if s.cacheFile != nil {
|
||||
err = s.cacheFile.SaveRuleSet(s.options.Tag, &adapter.SavedRuleSet{
|
||||
err = s.cacheFile.SaveRuleSet(s.options.Tag, &adapter.SavedBinary{
|
||||
LastUpdated: s.lastUpdated,
|
||||
Content: content,
|
||||
LastEtag: s.lastEtag,
|
||||
@@ -307,8 +307,10 @@ func (s *RemoteRuleSet) fetchOnce(ctx context.Context, startContext *adapter.HTT
|
||||
|
||||
func (s *RemoteRuleSet) Close() error {
|
||||
s.rules = nil
|
||||
s.updateTicker.Stop()
|
||||
s.cancel()
|
||||
if s.updateTicker != nil {
|
||||
s.updateTicker.Stop()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user