mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-14 04:38:28 +10:00
Compare commits
9 Commits
v1.11.0-al
...
v1.11.0-al
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
26064a9fdc | ||
|
|
d8e66b9180 | ||
|
|
c59f282b7d | ||
|
|
aa35ae1736 | ||
|
|
ef2a2fdd52 | ||
|
|
9988144868 | ||
|
|
412701d4c5 | ||
|
|
b6c940af61 | ||
|
|
1edb80adcc |
@@ -44,7 +44,7 @@ func (m *Manager) Start(stage adapter.StartStage) error {
|
||||
for _, inbound := range m.inbounds {
|
||||
err := adapter.LegacyStart(inbound, stage)
|
||||
if err != nil {
|
||||
return E.Cause(err, stage.Action(), " inbound/", inbound.Type(), "[", inbound.Tag(), "]")
|
||||
return E.Cause(err, stage, " inbound/", inbound.Type(), "[", inbound.Tag(), "]")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -118,7 +118,7 @@ func (m *Manager) Create(ctx context.Context, router adapter.Router, logger log.
|
||||
for _, stage := range adapter.ListStartStages {
|
||||
err = adapter.LegacyStart(inbound, stage)
|
||||
if err != nil {
|
||||
return E.Cause(err, stage.Action(), " inbound/", inbound.Type(), "[", inbound.Tag(), "]")
|
||||
return E.Cause(err, stage, " inbound/", inbound.Type(), "[", inbound.Tag(), "]")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package adapter
|
||||
|
||||
import E "github.com/sagernet/sing/common/exceptions"
|
||||
|
||||
type StartStage uint8
|
||||
|
||||
const (
|
||||
@@ -16,7 +18,7 @@ var ListStartStages = []StartStage{
|
||||
StartStateStarted,
|
||||
}
|
||||
|
||||
func (s StartStage) Action() string {
|
||||
func (s StartStage) String() string {
|
||||
switch s {
|
||||
case StartStateInitialize:
|
||||
return "initialize"
|
||||
@@ -25,7 +27,7 @@ func (s StartStage) Action() string {
|
||||
case StartStatePostStart:
|
||||
return "post-start"
|
||||
case StartStateStarted:
|
||||
return "start-after-started"
|
||||
return "finish-start"
|
||||
default:
|
||||
panic("unknown stage")
|
||||
}
|
||||
@@ -40,3 +42,23 @@ type LifecycleService interface {
|
||||
Name() string
|
||||
Lifecycle
|
||||
}
|
||||
|
||||
func Start(stage StartStage, services ...Lifecycle) error {
|
||||
for _, service := range services {
|
||||
err := service.Start(stage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func StartNamed(stage StartStage, services []LifecycleService) error {
|
||||
for _, service := range services {
|
||||
err := service.Start(stage)
|
||||
if err != nil {
|
||||
return E.Cause(err, stage.String(), " ", service.Name())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ func LegacyStart(starter any, stage StartStage) error {
|
||||
}); isStarter {
|
||||
return starter.Start()
|
||||
}
|
||||
case StartStatePostStart:
|
||||
case StartStateStarted:
|
||||
if postStarter, isPostStarter := starter.(interface {
|
||||
PostStart() error
|
||||
}); isPostStarter {
|
||||
|
||||
@@ -9,6 +9,8 @@ type NetworkManager interface {
|
||||
Lifecycle
|
||||
InterfaceFinder() control.InterfaceFinder
|
||||
UpdateInterfaces() error
|
||||
DefaultNetworkInterface() *NetworkInterface
|
||||
NetworkInterfaces() []NetworkInterface
|
||||
DefaultInterface() string
|
||||
AutoDetectInterface() bool
|
||||
AutoDetectInterfaceFunc() control.Func
|
||||
@@ -21,3 +23,20 @@ type NetworkManager interface {
|
||||
WIFIState() WIFIState
|
||||
ResetNetwork()
|
||||
}
|
||||
|
||||
type InterfaceUpdateListener interface {
|
||||
InterfaceUpdated()
|
||||
}
|
||||
|
||||
type WIFIState struct {
|
||||
SSID string
|
||||
BSSID string
|
||||
}
|
||||
|
||||
type NetworkInterface struct {
|
||||
control.Interface
|
||||
Type string
|
||||
DNSServers []string
|
||||
Expensive bool
|
||||
Constrained bool
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ func (m *Manager) Start(stage adapter.StartStage) error {
|
||||
for _, outbound := range outbounds {
|
||||
err := adapter.LegacyStart(outbound, stage)
|
||||
if err != nil {
|
||||
return E.Cause(err, stage.Action(), " outbound/", outbound.Type(), "[", outbound.Tag(), "]")
|
||||
return E.Cause(err, stage, " outbound/", outbound.Type(), "[", outbound.Tag(), "]")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -234,7 +234,7 @@ func (m *Manager) Create(ctx context.Context, router adapter.Router, logger log.
|
||||
for _, stage := range adapter.ListStartStages {
|
||||
err = adapter.LegacyStart(outbound, stage)
|
||||
if err != nil {
|
||||
return E.Cause(err, stage.Action(), " outbound/", outbound.Type(), "[", outbound.Tag(), "]")
|
||||
return E.Cause(err, stage, " outbound/", outbound.Type(), "[", outbound.Tag(), "]")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,12 +119,3 @@ func (c *HTTPStartContext) Close() {
|
||||
client.CloseIdleConnections()
|
||||
}
|
||||
}
|
||||
|
||||
type InterfaceUpdateListener interface {
|
||||
InterfaceUpdated()
|
||||
}
|
||||
|
||||
type WIFIState struct {
|
||||
SSID string
|
||||
BSSID string
|
||||
}
|
||||
|
||||
64
box.go
64
box.go
@@ -315,27 +315,17 @@ func (s *Box) preStart() error {
|
||||
if err != nil {
|
||||
return E.Cause(err, "start logger")
|
||||
}
|
||||
for _, lifecycleService := range s.services {
|
||||
err = lifecycleService.Start(adapter.StartStateInitialize) // cache-file
|
||||
if err != nil {
|
||||
return E.Cause(err, "initialize ", lifecycleService.Name())
|
||||
}
|
||||
err = adapter.StartNamed(adapter.StartStateInitialize, s.services) // cache-file clash-api v2ray-api
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, lifecycle := range []adapter.Lifecycle{
|
||||
s.network, s.router, s.outbound, s.inbound,
|
||||
} {
|
||||
err = lifecycle.Start(adapter.StartStateInitialize)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = adapter.Start(adapter.StartStateInitialize, s.network, s.router, s.outbound, s.inbound)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, lifecycle := range []adapter.Lifecycle{
|
||||
s.outbound, s.network, s.router,
|
||||
} {
|
||||
err = lifecycle.Start(adapter.StartStateStart)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = adapter.Start(adapter.StartStateStart, s.outbound, s.network, s.router)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -345,31 +335,29 @@ func (s *Box) start() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, lifecycleService := range s.services {
|
||||
err = lifecycleService.Start(adapter.StartStateStart)
|
||||
if err != nil {
|
||||
return E.Cause(err, "initialize ", lifecycleService.Name())
|
||||
}
|
||||
err = adapter.StartNamed(adapter.StartStateStart, s.services)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.inbound.Start(adapter.StartStateStart)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, lifecycleService := range []adapter.Lifecycle{
|
||||
s.outbound, s.network, s.router, s.inbound,
|
||||
} {
|
||||
err = lifecycleService.Start(adapter.StartStatePostStart)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = adapter.Start(adapter.StartStatePostStart, s.outbound, s.network, s.router, s.inbound)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, lifecycleService := range []adapter.Lifecycle{
|
||||
s.network, s.router, s.outbound, s.inbound,
|
||||
} {
|
||||
err = lifecycleService.Start(adapter.StartStateStarted)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = adapter.StartNamed(adapter.StartStatePostStart, s.services)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = adapter.Start(adapter.StartStateStarted, s.network, s.router, s.outbound, s.inbound)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = adapter.StartNamed(adapter.StartStateStarted, s.services)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/common/srs"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common/json"
|
||||
@@ -56,10 +55,6 @@ func compileRuleSet(sourcePath string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ruleSet, err := plainRuleSet.Upgrade()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var outputPath string
|
||||
if flagRuleSetCompileOutput == flagRuleSetCompileDefaultOutput {
|
||||
if strings.HasSuffix(sourcePath, ".json") {
|
||||
@@ -74,7 +69,7 @@ func compileRuleSet(sourcePath string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = srs.Write(outputFile, ruleSet, plainRuleSet.Version == C.RuleSetVersion2)
|
||||
err = srs.Write(outputFile, plainRuleSet.Options, plainRuleSet.Version)
|
||||
if err != nil {
|
||||
outputFile.Close()
|
||||
os.Remove(outputPath)
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/sagernet/sing-box/cmd/sing-box/internal/convertor/adguard"
|
||||
"github.com/sagernet/sing-box/common/srs"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
@@ -77,7 +78,7 @@ func convertRuleSet(sourcePath string) error {
|
||||
return err
|
||||
}
|
||||
defer outputFile.Close()
|
||||
err = srs.Write(outputFile, option.PlainRuleSet{Rules: rules}, true)
|
||||
err = srs.Write(outputFile, option.PlainRuleSet{Rules: rules}, C.RuleSetVersion2)
|
||||
if err != nil {
|
||||
outputFile.Close()
|
||||
os.Remove(outputPath)
|
||||
|
||||
@@ -6,9 +6,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/common/srs"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common/json"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
@@ -48,14 +46,10 @@ func decompileRuleSet(sourcePath string) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
plainRuleSet, err := srs.Read(reader, true)
|
||||
ruleSet, err := srs.Read(reader, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ruleSet := option.PlainRuleSetCompat{
|
||||
Version: C.RuleSetVersion1,
|
||||
Options: plainRuleSet,
|
||||
}
|
||||
var outputPath string
|
||||
if flagRuleSetDecompileOutput == flagRuleSetDecompileDefaultOutput {
|
||||
if strings.HasSuffix(sourcePath, ".srs") {
|
||||
|
||||
@@ -56,26 +56,25 @@ func ruleSetMatch(sourcePath string, domain string) error {
|
||||
if err != nil {
|
||||
return E.Cause(err, "read rule-set")
|
||||
}
|
||||
var plainRuleSet option.PlainRuleSet
|
||||
var ruleSet option.PlainRuleSetCompat
|
||||
switch flagRuleSetMatchFormat {
|
||||
case C.RuleSetFormatSource:
|
||||
var compat option.PlainRuleSetCompat
|
||||
compat, err = json.UnmarshalExtended[option.PlainRuleSetCompat](content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
plainRuleSet, err = compat.Upgrade()
|
||||
ruleSet, err = json.UnmarshalExtended[option.PlainRuleSetCompat](content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case C.RuleSetFormatBinary:
|
||||
plainRuleSet, err = srs.Read(bytes.NewReader(content), false)
|
||||
ruleSet, err = srs.Read(bytes.NewReader(content), false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return E.New("unknown rule-set format: ", flagRuleSetMatchFormat)
|
||||
}
|
||||
plainRuleSet, err := ruleSet.Upgrade()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ipAddress := M.ParseAddr(domain)
|
||||
var metadata adapter.InboundContext
|
||||
if ipAddress.IsValid() {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package adguard
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -26,7 +27,7 @@ example.arpa
|
||||
`))
|
||||
require.NoError(t, err)
|
||||
require.Len(t, rules, 1)
|
||||
rule, err := rule.NewHeadlessRule(nil, rules[0])
|
||||
rule, err := rule.NewHeadlessRule(context.Background(), rules[0])
|
||||
require.NoError(t, err)
|
||||
matchDomain := []string{
|
||||
"example.org",
|
||||
@@ -85,7 +86,7 @@ func TestHosts(t *testing.T) {
|
||||
`))
|
||||
require.NoError(t, err)
|
||||
require.Len(t, rules, 1)
|
||||
rule, err := rule.NewHeadlessRule(nil, rules[0])
|
||||
rule, err := rule.NewHeadlessRule(context.Background(), rules[0])
|
||||
require.NoError(t, err)
|
||||
matchDomain := []string{
|
||||
"google.com",
|
||||
@@ -115,7 +116,7 @@ www.example.org
|
||||
`))
|
||||
require.NoError(t, err)
|
||||
require.Len(t, rules, 1)
|
||||
rule, err := rule.NewHeadlessRule(nil, rules[0])
|
||||
rule, err := rule.NewHeadlessRule(context.Background(), rules[0])
|
||||
require.NoError(t, err)
|
||||
matchDomain := []string{
|
||||
"example.com",
|
||||
|
||||
@@ -2,7 +2,6 @@ package settings
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/netip"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -77,14 +76,14 @@ func (p *DarwinSystemProxy) update(event int) {
|
||||
}
|
||||
|
||||
func (p *DarwinSystemProxy) update0() error {
|
||||
newInterfaceName := p.monitor.DefaultInterfaceName(netip.IPv4Unspecified())
|
||||
if p.interfaceName == newInterfaceName {
|
||||
newInterface := p.monitor.DefaultInterface()
|
||||
if p.interfaceName == newInterface.Name {
|
||||
return nil
|
||||
}
|
||||
if p.interfaceName != "" {
|
||||
_ = p.Disable()
|
||||
}
|
||||
p.interfaceName = newInterfaceName
|
||||
p.interfaceName = newInterface.Name
|
||||
interfaceDisplayName, err := getInterfaceDisplayName(p.interfaceName)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -38,10 +38,13 @@ const (
|
||||
ruleItemWIFIBSSID
|
||||
ruleItemAdGuardDomain
|
||||
ruleItemProcessPathRegex
|
||||
ruleItemNetworkType
|
||||
ruleItemNetworkIsExpensive
|
||||
ruleItemNetworkIsConstrained
|
||||
ruleItemFinal uint8 = 0xFF
|
||||
)
|
||||
|
||||
func Read(reader io.Reader, recover bool) (ruleSet option.PlainRuleSet, err error) {
|
||||
func Read(reader io.Reader, recover bool) (ruleSetCompat option.PlainRuleSetCompat, err error) {
|
||||
var magicBytes [3]byte
|
||||
_, err = io.ReadFull(reader, magicBytes[:])
|
||||
if err != nil {
|
||||
@@ -54,10 +57,10 @@ func Read(reader io.Reader, recover bool) (ruleSet option.PlainRuleSet, err erro
|
||||
var version uint8
|
||||
err = binary.Read(reader, binary.BigEndian, &version)
|
||||
if err != nil {
|
||||
return ruleSet, err
|
||||
return ruleSetCompat, err
|
||||
}
|
||||
if version > C.RuleSetVersion2 {
|
||||
return ruleSet, E.New("unsupported version: ", version)
|
||||
if version > C.RuleSetVersionCurrent {
|
||||
return ruleSetCompat, E.New("unsupported version: ", version)
|
||||
}
|
||||
compressReader, err := zlib.NewReader(reader)
|
||||
if err != nil {
|
||||
@@ -68,9 +71,10 @@ func Read(reader io.Reader, recover bool) (ruleSet option.PlainRuleSet, err erro
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ruleSet.Rules = make([]option.HeadlessRule, length)
|
||||
ruleSetCompat.Version = version
|
||||
ruleSetCompat.Options.Rules = make([]option.HeadlessRule, length)
|
||||
for i := uint64(0); i < length; i++ {
|
||||
ruleSet.Rules[i], err = readRule(bReader, recover)
|
||||
ruleSetCompat.Options.Rules[i], err = readRule(bReader, recover)
|
||||
if err != nil {
|
||||
err = E.Cause(err, "read rule[", i, "]")
|
||||
return
|
||||
@@ -79,18 +83,12 @@ func Read(reader io.Reader, recover bool) (ruleSet option.PlainRuleSet, err erro
|
||||
return
|
||||
}
|
||||
|
||||
func Write(writer io.Writer, ruleSet option.PlainRuleSet, generateUnstable bool) error {
|
||||
func Write(writer io.Writer, ruleSet option.PlainRuleSet, generateVersion uint8) error {
|
||||
_, err := writer.Write(MagicBytes[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var version uint8
|
||||
if generateUnstable {
|
||||
version = C.RuleSetVersion2
|
||||
} else {
|
||||
version = C.RuleSetVersion1
|
||||
}
|
||||
err = binary.Write(writer, binary.BigEndian, version)
|
||||
err = binary.Write(writer, binary.BigEndian, generateVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -104,7 +102,7 @@ func Write(writer io.Writer, ruleSet option.PlainRuleSet, generateUnstable bool)
|
||||
return err
|
||||
}
|
||||
for _, rule := range ruleSet.Rules {
|
||||
err = writeRule(bWriter, rule, generateUnstable)
|
||||
err = writeRule(bWriter, rule, generateVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -135,12 +133,12 @@ func readRule(reader varbin.Reader, recover bool) (rule option.HeadlessRule, err
|
||||
return
|
||||
}
|
||||
|
||||
func writeRule(writer varbin.Writer, rule option.HeadlessRule, generateUnstable bool) error {
|
||||
func writeRule(writer varbin.Writer, rule option.HeadlessRule, generateVersion uint8) error {
|
||||
switch rule.Type {
|
||||
case C.RuleTypeDefault:
|
||||
return writeDefaultRule(writer, rule.DefaultOptions, generateUnstable)
|
||||
return writeDefaultRule(writer, rule.DefaultOptions, generateVersion)
|
||||
case C.RuleTypeLogical:
|
||||
return writeLogicalRule(writer, rule.LogicalOptions, generateUnstable)
|
||||
return writeLogicalRule(writer, rule.LogicalOptions, generateVersion)
|
||||
default:
|
||||
panic("unknown rule type: " + rule.Type)
|
||||
}
|
||||
@@ -227,6 +225,12 @@ func readDefaultRule(reader varbin.Reader, recover bool) (rule option.DefaultHea
|
||||
return
|
||||
}
|
||||
rule.AdGuardDomainMatcher = matcher
|
||||
case ruleItemNetworkType:
|
||||
rule.NetworkType, err = readRuleItemString(reader)
|
||||
case ruleItemNetworkIsExpensive:
|
||||
rule.NetworkIsExpensive = true
|
||||
case ruleItemNetworkIsConstrained:
|
||||
rule.NetworkIsConstrained = true
|
||||
case ruleItemFinal:
|
||||
err = binary.Read(reader, binary.BigEndian, &rule.Invert)
|
||||
return
|
||||
@@ -240,7 +244,7 @@ func readDefaultRule(reader varbin.Reader, recover bool) (rule option.DefaultHea
|
||||
}
|
||||
}
|
||||
|
||||
func writeDefaultRule(writer varbin.Writer, rule option.DefaultHeadlessRule, generateUnstable bool) error {
|
||||
func writeDefaultRule(writer varbin.Writer, rule option.DefaultHeadlessRule, generateVersion uint8) error {
|
||||
err := binary.Write(writer, binary.BigEndian, uint8(0))
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -264,7 +268,7 @@ func writeDefaultRule(writer varbin.Writer, rule option.DefaultHeadlessRule, gen
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = domain.NewMatcher(rule.Domain, rule.DomainSuffix, !generateUnstable).Write(writer)
|
||||
err = domain.NewMatcher(rule.Domain, rule.DomainSuffix, generateVersion == C.RuleSetVersion1).Write(writer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -341,6 +345,27 @@ func writeDefaultRule(writer varbin.Writer, rule option.DefaultHeadlessRule, gen
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(rule.NetworkType) > 0 {
|
||||
if generateVersion < C.RuleSetVersion3 {
|
||||
return E.New("network_type rule item is only supported in version 3 or later")
|
||||
}
|
||||
err = writeRuleItemString(writer, ruleItemNetworkType, rule.NetworkType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if rule.NetworkIsExpensive {
|
||||
err = binary.Write(writer, binary.BigEndian, ruleItemNetworkIsExpensive)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if rule.NetworkIsConstrained {
|
||||
err = binary.Write(writer, binary.BigEndian, ruleItemNetworkIsConstrained)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(rule.WIFISSID) > 0 {
|
||||
err = writeRuleItemString(writer, ruleItemWIFISSID, rule.WIFISSID)
|
||||
if err != nil {
|
||||
@@ -354,6 +379,9 @@ func writeDefaultRule(writer varbin.Writer, rule option.DefaultHeadlessRule, gen
|
||||
}
|
||||
}
|
||||
if len(rule.AdGuardDomain) > 0 {
|
||||
if generateVersion < C.RuleSetVersion2 {
|
||||
return E.New("AdGuard rule items is only supported in version 2 or later")
|
||||
}
|
||||
err = binary.Write(writer, binary.BigEndian, ruleItemAdGuardDomain)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -457,7 +485,7 @@ func readLogicalRule(reader varbin.Reader, recovery bool) (logicalRule option.Lo
|
||||
return
|
||||
}
|
||||
|
||||
func writeLogicalRule(writer varbin.Writer, logicalRule option.LogicalHeadlessRule, generateUnstable bool) error {
|
||||
func writeLogicalRule(writer varbin.Writer, logicalRule option.LogicalHeadlessRule, generateVersion uint8) error {
|
||||
err := binary.Write(writer, binary.BigEndian, uint8(1))
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -478,7 +506,7 @@ func writeLogicalRule(writer varbin.Writer, logicalRule option.LogicalHeadlessRu
|
||||
return err
|
||||
}
|
||||
for _, rule := range logicalRule.Rules {
|
||||
err = writeRule(writer, rule, generateUnstable)
|
||||
err = writeRule(writer, rule, generateVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
8
constant/network.go
Normal file
8
constant/network.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package constant
|
||||
|
||||
const (
|
||||
InterfaceTypeWIFI = "wifi"
|
||||
InterfaceTypeCellular = "cellular"
|
||||
InterfaceTypeEthernet = "ethernet"
|
||||
InterfaceTypeOther = "other"
|
||||
)
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
|
||||
const IsAndroid = goos.IsAndroid == 1
|
||||
|
||||
const IsDarwin = goos.IsDarwin == 1
|
||||
const IsDarwin = goos.IsDarwin == 1 || goos.IsIos == 1
|
||||
|
||||
const IsDragonfly = goos.IsDragonfly == 1
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@ const (
|
||||
const (
|
||||
RuleSetVersion1 = 1 + iota
|
||||
RuleSetVersion2
|
||||
RuleSetVersion3
|
||||
RuleSetVersionCurrent = RuleSetVersion3
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -2,10 +2,28 @@
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
#### 1.11.0-alpha.11
|
||||
#### 1.11.0-alpha.12
|
||||
|
||||
* Merge route options to route actions **1**
|
||||
* Add `network_type`, `network_is_expensive` and `network_is_constrainted` rule items **2**
|
||||
* Fixes and improvements
|
||||
|
||||
**1**:
|
||||
|
||||
Route options in DNS route actions will no longer be considered deprecated,
|
||||
see [DNS Route Action](/configuration/dns/rule_action/).
|
||||
|
||||
Also, now `udp_disable_domain_unmapping` and `udp_connect` can also be configured in route action,
|
||||
see [Route Action](/configuration/route/rule_action/).
|
||||
|
||||
**2**:
|
||||
|
||||
When using in graphical clients, new routing rule items allow you to match on
|
||||
network type (WIFI, cellular, etc.), whether the network is expensive, and whether Low Data Mode is enabled.
|
||||
|
||||
See [Route Rule](/configuration/route/rule/), [DNS Route Rule](/configuration/dns/rule/)
|
||||
and [Headless Rule](/configuration/rule-set/headless-rule/).
|
||||
|
||||
#### 1.11.0-alpha.9
|
||||
|
||||
* Improve tun compatibility **1**
|
||||
@@ -32,7 +50,7 @@ See [Rule](/configuration/route/rule/),
|
||||
[DNS Rule Action](/configuration/dns/rule_action/).
|
||||
|
||||
For migration, see
|
||||
[Migrate legacy special outbounds to rule actions](/migration/#migrate-legacy-special-outbounds-to-rule-actions),
|
||||
[Migrate legacy special outbounds to rule actions](/migration/#migrate-legacy-special-outbounds-to-rule-actions),
|
||||
[Migrate legacy inbound fields to rule actions](/migration/#migrate-legacy-inbound-fields-to-rule-actions)
|
||||
and [Migrate legacy DNS route options to rule actions](/migration/#migrate-legacy-dns-route-options-to-rule-actions).
|
||||
|
||||
|
||||
@@ -8,7 +8,10 @@ icon: material/new-box
|
||||
:material-alert: [server](#server)
|
||||
:material-alert: [disable_cache](#disable_cache)
|
||||
:material-alert: [rewrite_ttl](#rewrite_ttl)
|
||||
:material-alert: [client_subnet](#client_subnet)
|
||||
:material-alert: [client_subnet](#client_subnet)
|
||||
:material-plus: [network_type](#network_type)
|
||||
:material-plus: [network_is_expensive](#network_is_expensive)
|
||||
:material-plus: [network_is_constrained](#network_is_constrained)
|
||||
|
||||
!!! quote "Changes in sing-box 1.10.0"
|
||||
|
||||
@@ -125,6 +128,11 @@ icon: material/new-box
|
||||
1000
|
||||
],
|
||||
"clash_mode": "direct",
|
||||
"network_type": [
|
||||
"wifi"
|
||||
],
|
||||
"network_is_expensive": false,
|
||||
"network_is_constrained": false,
|
||||
"wifi_ssid": [
|
||||
"My WIFI"
|
||||
],
|
||||
@@ -310,6 +318,39 @@ Match user id.
|
||||
|
||||
Match Clash mode.
|
||||
|
||||
#### network_type
|
||||
|
||||
!!! question "Since sing-box 1.11.0"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
Only supported in graphical clients on Android and Apple platforms.
|
||||
|
||||
Match network type.
|
||||
|
||||
Available values: `wifi`, `cellular`, `ethernet` and `other`.
|
||||
|
||||
#### network_is_expensive
|
||||
|
||||
!!! question "Since sing-box 1.11.0"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
Only supported in graphical clients on Android and Apple platforms.
|
||||
|
||||
Match if network is considered Metered (on Android) or considered expensive,
|
||||
such as Cellular or a Personal Hotspot (on Apple platforms).
|
||||
|
||||
#### network_is_constrained
|
||||
|
||||
!!! question "Since sing-box 1.11.0"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
Only supported in graphical clients on Apple platforms.
|
||||
|
||||
Match if network is in Low Data Mode.
|
||||
|
||||
#### wifi_ssid
|
||||
|
||||
!!! quote ""
|
||||
|
||||
@@ -2,6 +2,17 @@
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "sing-box 1.11.0 中的更改"
|
||||
|
||||
:material-plus: [action](#action)
|
||||
:material-alert: [server](#server)
|
||||
:material-alert: [disable_cache](#disable_cache)
|
||||
:material-alert: [rewrite_ttl](#rewrite_ttl)
|
||||
:material-alert: [client_subnet](#client_subnet)
|
||||
:material-plus: [network_type](#network_type)
|
||||
:material-plus: [network_is_expensive](#network_is_expensive)
|
||||
:material-plus: [network_is_constrained](#network_is_constrained)
|
||||
|
||||
!!! quote "sing-box 1.10.0 中的更改"
|
||||
|
||||
:material-delete-clock: [rule_set_ipcidr_match_source](#rule_set_ipcidr_match_source)
|
||||
@@ -117,6 +128,11 @@ icon: material/new-box
|
||||
1000
|
||||
],
|
||||
"clash_mode": "direct",
|
||||
"network_type": [
|
||||
"wifi"
|
||||
],
|
||||
"network_is_expensive": false,
|
||||
"network_is_constrained": false,
|
||||
"wifi_ssid": [
|
||||
"My WIFI"
|
||||
],
|
||||
@@ -135,17 +151,15 @@ icon: material/new-box
|
||||
"outbound": [
|
||||
"direct"
|
||||
],
|
||||
"server": "local",
|
||||
"disable_cache": false,
|
||||
"client_subnet": "127.0.0.1/24"
|
||||
"action": "route",
|
||||
"server": "local"
|
||||
},
|
||||
{
|
||||
"type": "logical",
|
||||
"mode": "and",
|
||||
"rules": [],
|
||||
"server": "local",
|
||||
"disable_cache": false,
|
||||
"client_subnet": "127.0.0.1/24"
|
||||
"action": "route",
|
||||
"server": "local"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -304,6 +318,39 @@ DNS 查询类型。值可以为整数或者类型名称字符串。
|
||||
|
||||
匹配 Clash 模式。
|
||||
|
||||
#### network_type
|
||||
|
||||
!!! question "自 sing-box 1.11.0 起"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
仅在 Android 与 Apple 平台图形客户端中支持。
|
||||
|
||||
匹配网络类型。
|
||||
|
||||
Available values: `wifi`, `cellular`, `ethernet` and `other`.
|
||||
|
||||
#### network_is_expensive
|
||||
|
||||
!!! question "自 sing-box 1.11.0 起"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
仅在 Android 与 Apple 平台图形客户端中支持。
|
||||
|
||||
匹配如果网络被视为计费 (在 Android) 或被视为昂贵,
|
||||
像蜂窝网络或个人热点 (在 Apple 平台)。
|
||||
|
||||
#### network_is_constrained
|
||||
|
||||
!!! question "自 sing-box 1.11.0 起"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
仅在 Apple 平台图形客户端中支持。
|
||||
|
||||
匹配如果网络在低数据模式下。
|
||||
|
||||
#### wifi_ssid
|
||||
|
||||
!!! quote ""
|
||||
@@ -352,29 +399,35 @@ DNS 查询类型。值可以为整数或者类型名称字符串。
|
||||
|
||||
`any` 可作为值用于匹配任意出站。
|
||||
|
||||
#### server
|
||||
#### action
|
||||
|
||||
==必填==
|
||||
|
||||
目标 DNS 服务器的标签。
|
||||
参阅 [规则动作](../rule_action/)。
|
||||
|
||||
#### server
|
||||
|
||||
!!! failure "已在 sing-box 1.11.0 废弃"
|
||||
|
||||
已移动到 [DNS 规则动作](../rule_action#route).
|
||||
|
||||
#### disable_cache
|
||||
|
||||
在此查询中禁用缓存。
|
||||
!!! failure "已在 sing-box 1.11.0 废弃"
|
||||
|
||||
已移动到 [DNS 规则动作](../rule_action#route).
|
||||
|
||||
#### rewrite_ttl
|
||||
|
||||
重写 DNS 回应中的 TTL。
|
||||
!!! failure "已在 sing-box 1.11.0 废弃"
|
||||
|
||||
已移动到 [DNS 规则动作](../rule_action#route).
|
||||
|
||||
#### client_subnet
|
||||
|
||||
!!! question "自 sing-box 1.9.0 起"
|
||||
!!! failure "已在 sing-box 1.11.0 废弃"
|
||||
|
||||
默认情况下,将带有指定 IP 前缀的 `edns0-subnet` OPT 附加记录附加到每个查询。
|
||||
|
||||
如果值是 IP 地址而不是前缀,则会自动附加 `/32` 或 `/128`。
|
||||
|
||||
将覆盖 `dns.client_subnet` 与 `servers.[].client_subnet`。
|
||||
已移动到 [DNS 规则动作](../rule_action#route).
|
||||
|
||||
### 地址筛选字段
|
||||
|
||||
@@ -420,8 +473,12 @@ DNS 查询类型。值可以为整数或者类型名称字符串。
|
||||
|
||||
#### mode
|
||||
|
||||
==必填==
|
||||
|
||||
`and` 或 `or`
|
||||
|
||||
#### rules
|
||||
|
||||
包括的规则。
|
||||
==必填==
|
||||
|
||||
包括的规则。
|
||||
@@ -12,8 +12,6 @@ icon: material/new-box
|
||||
{
|
||||
"action": "route", // default
|
||||
"server": "",
|
||||
|
||||
// for compatibility
|
||||
"disable_cache": false,
|
||||
"rewrite_ttl": 0,
|
||||
"client_subnet": null
|
||||
@@ -28,23 +26,6 @@ icon: material/new-box
|
||||
|
||||
Tag of target server.
|
||||
|
||||
#### disable_cache/rewrite_ttl/client_subnet
|
||||
|
||||
!!! failure "Deprecated in sing-box 1.11.0"
|
||||
|
||||
Legacy route options is deprecated and will be removed in sing-box 1.12.0, check [Migration](/migration/#migrate-legacy-dns-route-options-to-rule-actions).
|
||||
|
||||
### route-options
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "route-options",
|
||||
"disable_cache": false,
|
||||
"rewrite_ttl": null,
|
||||
"client_subnet": null
|
||||
}
|
||||
```
|
||||
|
||||
#### disable_cache
|
||||
|
||||
Disable cache and save cache in this query.
|
||||
@@ -61,6 +42,19 @@ If value is an IP address instead of prefix, `/32` or `/128` will be appended au
|
||||
|
||||
Will overrides `dns.client_subnet` and `servers.[].client_subnet`.
|
||||
|
||||
### route-options
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "route-options",
|
||||
"disable_cache": false,
|
||||
"rewrite_ttl": null,
|
||||
"client_subnet": null
|
||||
}
|
||||
```
|
||||
|
||||
`route-options` set options for routing.
|
||||
|
||||
### reject
|
||||
|
||||
```json
|
||||
|
||||
@@ -28,24 +28,6 @@ icon: material/new-box
|
||||
|
||||
目标 DNS 服务器的标签。
|
||||
|
||||
#### disable_cache/rewrite_ttl/client_subnet
|
||||
|
||||
!!! failure "自 sing-box 1.11.0 起"
|
||||
|
||||
旧的路由选项已弃用,且将在 sing-box 1.12.0 中移除,参阅 [迁移指南](/migration/#migrate-legacy-dns-route-options-to-rule-actions).
|
||||
|
||||
### route-options
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "route-options",
|
||||
"disable_cache": false,
|
||||
"rewrite_ttl": null,
|
||||
"client_subnet": null
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
#### disable_cache
|
||||
|
||||
在此查询中禁用缓存。
|
||||
@@ -62,6 +44,19 @@ icon: material/new-box
|
||||
|
||||
将覆盖 `dns.client_subnet` 与 `servers.[].client_subnet`。
|
||||
|
||||
### route-options
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "route-options",
|
||||
"disable_cache": false,
|
||||
"rewrite_ttl": null,
|
||||
"client_subnet": null
|
||||
}
|
||||
```
|
||||
|
||||
`route-options` 为路由设置选项。
|
||||
|
||||
### reject
|
||||
|
||||
```json
|
||||
|
||||
@@ -5,7 +5,10 @@ icon: material/new-box
|
||||
!!! quote "Changes in sing-box 1.11.0"
|
||||
|
||||
:material-plus: [action](#action)
|
||||
:material-alert: [outbound](#outbound)
|
||||
:material-alert: [outbound](#outbound)
|
||||
:material-plus: [network_type](#network_type)
|
||||
:material-plus: [network_is_expensive](#network_is_expensive)
|
||||
:material-plus: [network_is_constrained](#network_is_constrained)
|
||||
|
||||
!!! quote "Changes in sing-box 1.10.0"
|
||||
|
||||
@@ -120,6 +123,11 @@ icon: material/new-box
|
||||
1000
|
||||
],
|
||||
"clash_mode": "direct",
|
||||
"network_type": [
|
||||
"wifi"
|
||||
],
|
||||
"network_is_expensive": false,
|
||||
"network_is_constrained": false,
|
||||
"wifi_ssid": [
|
||||
"My WIFI"
|
||||
],
|
||||
@@ -322,6 +330,39 @@ Match user id.
|
||||
|
||||
Match Clash mode.
|
||||
|
||||
#### network_type
|
||||
|
||||
!!! question "Since sing-box 1.11.0"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
Only supported in graphical clients on Android and Apple platforms.
|
||||
|
||||
Match network type.
|
||||
|
||||
Available values: `wifi`, `cellular`, `ethernet` and `other`.
|
||||
|
||||
#### network_is_expensive
|
||||
|
||||
!!! question "Since sing-box 1.11.0"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
Only supported in graphical clients on Android and Apple platforms.
|
||||
|
||||
Match if network is considered Metered (on Android) or considered expensive,
|
||||
such as Cellular or a Personal Hotspot (on Apple platforms).
|
||||
|
||||
#### network_is_constrained
|
||||
|
||||
!!! question "Since sing-box 1.11.0"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
Only supported in graphical clients on Apple platforms.
|
||||
|
||||
Match if network is in Low Data Mode.
|
||||
|
||||
#### wifi_ssid
|
||||
|
||||
!!! quote ""
|
||||
|
||||
@@ -5,7 +5,10 @@ icon: material/new-box
|
||||
!!! quote "sing-box 1.11.0 中的更改"
|
||||
|
||||
:material-plus: [action](#action)
|
||||
:material-alert: [outbound](#outbound)
|
||||
:material-alert: [outbound](#outbound)
|
||||
:material-plus: [network_type](#network_type)
|
||||
:material-plus: [network_is_expensive](#network_is_expensive)
|
||||
:material-plus: [network_is_constrained](#network_is_constrained)
|
||||
|
||||
!!! quote "sing-box 1.10.0 中的更改"
|
||||
|
||||
@@ -13,7 +16,6 @@ icon: material/new-box
|
||||
:material-delete-clock: [rule_set_ipcidr_match_source](#rule_set_ipcidr_match_source)
|
||||
:material-plus: [process_path_regex](#process_path_regex)
|
||||
|
||||
|
||||
!!! quote "sing-box 1.8.0 中的更改"
|
||||
|
||||
:material-plus: [rule_set](#rule_set)
|
||||
@@ -118,6 +120,11 @@ icon: material/new-box
|
||||
1000
|
||||
],
|
||||
"clash_mode": "direct",
|
||||
"network_type": [
|
||||
"wifi"
|
||||
],
|
||||
"network_is_expensive": false,
|
||||
"network_is_constrained": false,
|
||||
"wifi_ssid": [
|
||||
"My WIFI"
|
||||
],
|
||||
@@ -153,7 +160,7 @@ icon: material/new-box
|
||||
|
||||
当内容只有一项时,可以忽略 JSON 数组 [] 标签。
|
||||
|
||||
### Default Fields
|
||||
### 默认字段
|
||||
|
||||
!!! note ""
|
||||
|
||||
@@ -320,6 +327,39 @@ icon: material/new-box
|
||||
|
||||
匹配 Clash 模式。
|
||||
|
||||
#### network_type
|
||||
|
||||
!!! question "自 sing-box 1.11.0 起"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
仅在 Android 与 Apple 平台图形客户端中支持。
|
||||
|
||||
匹配网络类型。
|
||||
|
||||
Available values: `wifi`, `cellular`, `ethernet` and `other`.
|
||||
|
||||
#### network_is_expensive
|
||||
|
||||
!!! question "自 sing-box 1.11.0 起"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
仅在 Android 与 Apple 平台图形客户端中支持。
|
||||
|
||||
匹配如果网络被视为计费 (在 Android) 或被视为昂贵,
|
||||
像蜂窝网络或个人热点 (在 Apple 平台)。
|
||||
|
||||
#### network_is_constrained
|
||||
|
||||
!!! question "自 sing-box 1.11.0 起"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
仅在 Apple 平台图形客户端中支持。
|
||||
|
||||
匹配如果网络在低数据模式下。
|
||||
|
||||
#### wifi_ssid
|
||||
|
||||
!!! quote ""
|
||||
@@ -366,13 +406,13 @@ icon: material/new-box
|
||||
|
||||
==必填==
|
||||
|
||||
参阅 [规则行动](../rule_action/)。
|
||||
参阅 [规则动作](../rule_action/)。
|
||||
|
||||
#### outbound
|
||||
|
||||
!!! failure "已在 sing-box 1.11.0 废弃"
|
||||
|
||||
已移动到 [规则行动](../rule_action#route).
|
||||
已移动到 [规则动作](../rule_action#route).
|
||||
|
||||
### 逻辑字段
|
||||
|
||||
|
||||
@@ -13,7 +13,9 @@ icon: material/new-box
|
||||
```json
|
||||
{
|
||||
"action": "route", // default
|
||||
"outbound": ""
|
||||
"outbound": "",
|
||||
"udp_disable_domain_unmapping": false,
|
||||
"udp_connect": false
|
||||
}
|
||||
```
|
||||
|
||||
@@ -25,18 +27,6 @@ icon: material/new-box
|
||||
|
||||
Tag of target outbound.
|
||||
|
||||
### route-options
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "route-options",
|
||||
"udp_disable_domain_unmapping": false,
|
||||
"udp_connect": false
|
||||
}
|
||||
```
|
||||
|
||||
`route-options` set options for routing.
|
||||
|
||||
#### udp_disable_domain_unmapping
|
||||
|
||||
If enabled, for UDP proxy requests addressed to a domain,
|
||||
@@ -49,6 +39,18 @@ do not support receiving UDP packets with domain addresses, such as Surge.
|
||||
|
||||
If enabled, attempts to connect UDP connection to the destination instead of listen.
|
||||
|
||||
### route-options
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "route-options",
|
||||
"udp_disable_domain_unmapping": false,
|
||||
"udp_connect": false
|
||||
}
|
||||
```
|
||||
|
||||
`route-options` set options for routing.
|
||||
|
||||
### reject
|
||||
|
||||
```json
|
||||
|
||||
@@ -14,7 +14,8 @@ icon: material/new-box
|
||||
{
|
||||
"action": "route", // 默认
|
||||
"outbound": "",
|
||||
"udp_disable_domain_unmapping": false
|
||||
"udp_disable_domain_unmapping": false,
|
||||
"udp_connect": false
|
||||
}
|
||||
```
|
||||
|
||||
@@ -26,16 +27,6 @@ icon: material/new-box
|
||||
|
||||
目标出站的标签。
|
||||
|
||||
### route-options
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "route-options",
|
||||
"udp_disable_domain_unmapping": false,
|
||||
"udp_connect": false
|
||||
}
|
||||
```
|
||||
|
||||
#### udp_disable_domain_unmapping
|
||||
|
||||
如果启用,对于地址为域的 UDP 代理请求,将在响应中发送原始包地址而不是映射的域。
|
||||
@@ -46,6 +37,18 @@ icon: material/new-box
|
||||
|
||||
如果启用,将尝试将 UDP 连接 connect 到目标而不是 listen。
|
||||
|
||||
### route-options
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "route-options",
|
||||
"udp_disable_domain_unmapping": false,
|
||||
"udp_connect": false
|
||||
}
|
||||
```
|
||||
|
||||
`route-options` 为路由设置选项。
|
||||
|
||||
### reject
|
||||
|
||||
```json
|
||||
|
||||
70
docs/configuration/rule-set/adguard.zh.md
Normal file
70
docs/configuration/rule-set/adguard.zh.md
Normal file
@@ -0,0 +1,70 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
# AdGuard DNS Filter
|
||||
|
||||
!!! question "自 sing-box 1.10.0 起"
|
||||
|
||||
sing-box 支持其他项目的一些规则集格式,这些格式无法完全转换为 sing-box,
|
||||
目前只有 AdGuard DNS Filter。
|
||||
|
||||
这些格式不直接作为源格式支持,
|
||||
而是需要将它们转换为二进制规则集。
|
||||
|
||||
## 转换
|
||||
|
||||
使用 `sing-box rule-set convert --type adguard [--output <file-name>.srs] <file-name>.txt` 以转换为二进制规则集。
|
||||
|
||||
## 性能
|
||||
|
||||
AdGuard 将所有规则保存在内存中并按顺序匹配,
|
||||
而 sing-box 选择高性能和较小的内存使用量。
|
||||
作为权衡,您无法知道匹配了哪个规则项。
|
||||
|
||||
## 兼容性
|
||||
|
||||
[AdGuardSDNSFilter](https://github.com/AdguardTeam/AdGuardSDNSFilter)
|
||||
中的几乎所有规则以及 [adguard-filter-list](https://github.com/ppfeufer/adguard-filter-list)
|
||||
中列出的规则集中的规则均受支持。
|
||||
|
||||
## 支持的格式
|
||||
|
||||
### AdGuard Filter
|
||||
|
||||
#### 基本规则语法
|
||||
|
||||
| 语法 | 支持 |
|
||||
|--------|------------------|
|
||||
| `@@` | :material-check: |
|
||||
| `\|\|` | :material-check: |
|
||||
| `\|` | :material-check: |
|
||||
| `^` | :material-check: |
|
||||
| `*` | :material-check: |
|
||||
|
||||
#### 主机语法
|
||||
|
||||
| 语法 | 示例 | 支持 |
|
||||
|-------------|--------------------------|--------------------------|
|
||||
| Scheme | `https://` | :material-alert: Ignored |
|
||||
| Domain Host | `example.org` | :material-check: |
|
||||
| IP Host | `1.1.1.1`, `10.0.0.` | :material-close: |
|
||||
| Regexp | `/regexp/` | :material-check: |
|
||||
| Port | `example.org:80` | :material-close: |
|
||||
| Path | `example.org/path/ad.js` | :material-close: |
|
||||
|
||||
#### 描述符语法
|
||||
|
||||
| 描述符 | 支持 |
|
||||
|-----------------------|--------------------------|
|
||||
| `$important` | :material-check: |
|
||||
| `$dnsrewrite=0.0.0.0` | :material-alert: Ignored |
|
||||
| 任何其他描述符 | :material-close: |
|
||||
|
||||
### Hosts
|
||||
|
||||
只有 IP 地址为 `0.0.0.0` 的条目将被接受。
|
||||
|
||||
### 简易
|
||||
|
||||
当所有行都是有效域时,它们被视为简单的逐行域规则, 与 hosts 一样,只匹配完全相同的域。
|
||||
@@ -1,3 +1,13 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "Changes in sing-box 1.11.0"
|
||||
|
||||
:material-plus: [network_type](#network_type)
|
||||
:material-plus: [network_is_expensive](#network_is_expensive)
|
||||
:material-plus: [network_is_constrained](#network_is_constrained)
|
||||
|
||||
### Structure
|
||||
|
||||
!!! question "Since sing-box 1.8.0"
|
||||
@@ -63,6 +73,11 @@
|
||||
"package_name": [
|
||||
"com.termux"
|
||||
],
|
||||
"network_type": [
|
||||
"wifi"
|
||||
],
|
||||
"network_is_expensive": false,
|
||||
"network_is_constrained": false,
|
||||
"wifi_ssid": [
|
||||
"My WIFI"
|
||||
],
|
||||
@@ -177,6 +192,39 @@ Match process path using regular expression.
|
||||
|
||||
Match android package name.
|
||||
|
||||
#### network_type
|
||||
|
||||
!!! question "Since sing-box 1.11.0"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
Only supported in graphical clients on Android and Apple platforms.
|
||||
|
||||
Match network type.
|
||||
|
||||
Available values: `wifi`, `cellular`, `ethernet` and `other`.
|
||||
|
||||
#### network_is_expensive
|
||||
|
||||
!!! question "Since sing-box 1.11.0"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
Only supported in graphical clients on Android and Apple platforms.
|
||||
|
||||
Match if network is considered Metered (on Android) or considered expensive,
|
||||
such as Cellular or a Personal Hotspot (on Apple platforms).
|
||||
|
||||
#### network_is_constrained
|
||||
|
||||
!!! question "Since sing-box 1.11.0"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
Only supported in graphical clients on Apple platforms.
|
||||
|
||||
Match if network is in Low Data Mode.
|
||||
|
||||
#### wifi_ssid
|
||||
|
||||
!!! quote ""
|
||||
|
||||
258
docs/configuration/rule-set/headless-rule.zh.md
Normal file
258
docs/configuration/rule-set/headless-rule.zh.md
Normal file
@@ -0,0 +1,258 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "sing-box 1.11.0 中的更改"
|
||||
|
||||
:material-plus: [network_type](#network_type)
|
||||
:material-alert: [network_is_expensive](#network_is_expensive)
|
||||
:material-alert: [network_is_constrained](#network_is_constrained)
|
||||
|
||||
### 结构
|
||||
|
||||
!!! question "自 sing-box 1.8.0 起"
|
||||
|
||||
```json
|
||||
{
|
||||
"rules": [
|
||||
{
|
||||
"query_type": [
|
||||
"A",
|
||||
"HTTPS",
|
||||
32768
|
||||
],
|
||||
"network": [
|
||||
"tcp"
|
||||
],
|
||||
"domain": [
|
||||
"test.com"
|
||||
],
|
||||
"domain_suffix": [
|
||||
".cn"
|
||||
],
|
||||
"domain_keyword": [
|
||||
"test"
|
||||
],
|
||||
"domain_regex": [
|
||||
"^stun\\..+"
|
||||
],
|
||||
"source_ip_cidr": [
|
||||
"10.0.0.0/24",
|
||||
"192.168.0.1"
|
||||
],
|
||||
"ip_cidr": [
|
||||
"10.0.0.0/24",
|
||||
"192.168.0.1"
|
||||
],
|
||||
"source_port": [
|
||||
12345
|
||||
],
|
||||
"source_port_range": [
|
||||
"1000:2000",
|
||||
":3000",
|
||||
"4000:"
|
||||
],
|
||||
"port": [
|
||||
80,
|
||||
443
|
||||
],
|
||||
"port_range": [
|
||||
"1000:2000",
|
||||
":3000",
|
||||
"4000:"
|
||||
],
|
||||
"process_name": [
|
||||
"curl"
|
||||
],
|
||||
"process_path": [
|
||||
"/usr/bin/curl"
|
||||
],
|
||||
"process_path_regex": [
|
||||
"^/usr/bin/.+"
|
||||
],
|
||||
"package_name": [
|
||||
"com.termux"
|
||||
],
|
||||
"network_type": [
|
||||
"wifi"
|
||||
],
|
||||
"network_is_expensive": false,
|
||||
"network_is_constrained": false,
|
||||
"wifi_ssid": [
|
||||
"My WIFI"
|
||||
],
|
||||
"wifi_bssid": [
|
||||
"00:00:00:00:00:00"
|
||||
],
|
||||
"invert": false
|
||||
},
|
||||
{
|
||||
"type": "logical",
|
||||
"mode": "and",
|
||||
"rules": [],
|
||||
"invert": false
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
!!! note ""
|
||||
|
||||
当内容只有一项时,可以忽略 JSON 数组 [] 标签。
|
||||
|
||||
### Default Fields
|
||||
|
||||
!!! note ""
|
||||
|
||||
默认规则使用以下匹配逻辑:
|
||||
(`domain` || `domain_suffix` || `domain_keyword` || `domain_regex` || `ip_cidr`) &&
|
||||
(`port` || `port_range`) &&
|
||||
(`source_port` || `source_port_range`) &&
|
||||
`other fields`
|
||||
|
||||
#### query_type
|
||||
|
||||
DNS 查询类型。值可以为整数或者类型名称字符串。
|
||||
|
||||
#### network
|
||||
|
||||
`tcp` 或 `udp`。
|
||||
|
||||
#### domain
|
||||
|
||||
匹配完整域名。
|
||||
|
||||
#### domain_suffix
|
||||
|
||||
匹配域名后缀。
|
||||
|
||||
#### domain_keyword
|
||||
|
||||
匹配域名关键字。
|
||||
|
||||
#### domain_regex
|
||||
|
||||
匹配域名正则表达式。
|
||||
|
||||
#### source_ip_cidr
|
||||
|
||||
匹配源 IP CIDR。
|
||||
|
||||
#### ip_cidr
|
||||
|
||||
匹配 IP CIDR。
|
||||
|
||||
#### source_port
|
||||
|
||||
匹配源端口。
|
||||
|
||||
#### source_port_range
|
||||
|
||||
匹配源端口范围。
|
||||
|
||||
#### port
|
||||
|
||||
匹配端口。
|
||||
|
||||
#### port_range
|
||||
|
||||
匹配端口范围。
|
||||
|
||||
#### process_name
|
||||
|
||||
!!! quote ""
|
||||
|
||||
仅支持 Linux、Windows 和 macOS。
|
||||
|
||||
匹配进程名称。
|
||||
|
||||
#### process_path
|
||||
|
||||
!!! quote ""
|
||||
|
||||
仅支持 Linux、Windows 和 macOS.
|
||||
|
||||
匹配进程路径。
|
||||
|
||||
#### process_path_regex
|
||||
|
||||
!!! question "自 sing-box 1.10.0 起"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
仅支持 Linux、Windows 和 macOS.
|
||||
|
||||
使用正则表达式匹配进程路径。
|
||||
|
||||
#### package_name
|
||||
|
||||
匹配 Android 应用包名。
|
||||
|
||||
#### network_type
|
||||
|
||||
!!! question "自 sing-box 1.11.0 起"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
仅在 Android 与 Apple 平台图形客户端中支持。
|
||||
|
||||
匹配网络类型。
|
||||
|
||||
Available values: `wifi`, `cellular`, `ethernet` and `other`.
|
||||
|
||||
#### network_is_expensive
|
||||
|
||||
!!! question "自 sing-box 1.11.0 起"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
仅在 Android 与 Apple 平台图形客户端中支持。
|
||||
|
||||
匹配如果网络被视为计费 (在 Android) 或被视为昂贵,
|
||||
像蜂窝网络或个人热点 (在 Apple 平台)。
|
||||
|
||||
#### network_is_constrained
|
||||
|
||||
!!! question "自 sing-box 1.11.0 起"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
仅在 Apple 平台图形客户端中支持。
|
||||
|
||||
匹配如果网络在低数据模式下。
|
||||
|
||||
#### wifi_ssid
|
||||
|
||||
!!! quote ""
|
||||
|
||||
仅在 Android 与 Apple 平台图形客户端中支持。
|
||||
|
||||
匹配 WiFi SSID。
|
||||
|
||||
#### wifi_bssid
|
||||
|
||||
!!! quote ""
|
||||
|
||||
仅在 Android 与 Apple 平台图形客户端中支持。
|
||||
|
||||
#### invert
|
||||
|
||||
反选匹配结果。
|
||||
|
||||
### 逻辑字段
|
||||
|
||||
#### type
|
||||
|
||||
`logical`
|
||||
|
||||
#### mode
|
||||
|
||||
==必填==
|
||||
|
||||
`and` 或 `or`
|
||||
|
||||
#### rules
|
||||
|
||||
==必填==
|
||||
|
||||
包括的规则。
|
||||
@@ -74,7 +74,7 @@ Tag of rule-set.
|
||||
|
||||
==Required==
|
||||
|
||||
List of [Headless Rule](./headless-rule.md/).
|
||||
List of [Headless Rule](../headless-rule/).
|
||||
|
||||
### Local or Remote Fields
|
||||
|
||||
|
||||
117
docs/configuration/rule-set/index.zh.md
Normal file
117
docs/configuration/rule-set/index.zh.md
Normal file
@@ -0,0 +1,117 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "sing-box 1.10.0 中的更改"
|
||||
|
||||
:material-plus: `type: inline`
|
||||
|
||||
# 规则集
|
||||
|
||||
!!! question "自 sing-box 1.8.0 起"
|
||||
|
||||
### 结构
|
||||
|
||||
=== "内联"
|
||||
|
||||
!!! question "自 sing-box 1.10.0 起"
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "inline", // 可选
|
||||
"tag": "",
|
||||
"rules": []
|
||||
}
|
||||
```
|
||||
|
||||
=== "本地文件"
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "local",
|
||||
"tag": "",
|
||||
"format": "source", // or binary
|
||||
"path": ""
|
||||
}
|
||||
```
|
||||
|
||||
=== "远程文件"
|
||||
|
||||
!!! info ""
|
||||
|
||||
远程规则集将被缓存如果 `experimental.cache_file.enabled` 已启用。
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "remote",
|
||||
"tag": "",
|
||||
"format": "source", // or binary
|
||||
"url": "",
|
||||
"download_detour": "", // 可选
|
||||
"update_interval": "" // 可选
|
||||
}
|
||||
```
|
||||
|
||||
### 字段
|
||||
|
||||
#### type
|
||||
|
||||
==必填==
|
||||
|
||||
规则集类型, `local` 或 `remote`。
|
||||
|
||||
#### tag
|
||||
|
||||
==必填==
|
||||
|
||||
规则集的标签。
|
||||
|
||||
### 内联字段
|
||||
|
||||
!!! question "自 sing-box 1.10.0 起"
|
||||
|
||||
#### rules
|
||||
|
||||
==必填==
|
||||
|
||||
一组 [无头规则](../headless-rule/).
|
||||
|
||||
### 本地或远程字段
|
||||
|
||||
#### format
|
||||
|
||||
==必填==
|
||||
|
||||
规则集格式, `source` 或 `binary`。
|
||||
|
||||
### 本地字段
|
||||
|
||||
#### path
|
||||
|
||||
==必填==
|
||||
|
||||
!!! note ""
|
||||
|
||||
自 sing-box 1.10.0 起,文件更改时将自动重新加载。
|
||||
|
||||
规则集的文件路径。
|
||||
|
||||
### 远程字段
|
||||
|
||||
#### url
|
||||
|
||||
==必填==
|
||||
|
||||
规则集的下载 URL。
|
||||
|
||||
#### download_detour
|
||||
|
||||
用于下载规则集的出站的标签。
|
||||
|
||||
如果为空,将使用默认出站。
|
||||
|
||||
#### update_interval
|
||||
|
||||
规则集的更新间隔。
|
||||
|
||||
默认使用 `1d`。
|
||||
@@ -4,6 +4,10 @@ icon: material/new-box
|
||||
|
||||
# Source Format
|
||||
|
||||
!!! quote "Changes in sing-box 1.11.0"
|
||||
|
||||
:material-plus: version `3`
|
||||
|
||||
!!! quote "Changes in sing-box 1.10.0"
|
||||
|
||||
:material-plus: version `2`
|
||||
@@ -14,7 +18,7 @@ icon: material/new-box
|
||||
|
||||
```json
|
||||
{
|
||||
"version": 2,
|
||||
"version": 3,
|
||||
"rules": []
|
||||
}
|
||||
```
|
||||
@@ -29,19 +33,14 @@ Use `sing-box rule-set compile [--output <file-name>.srs] <file-name>.json` to c
|
||||
|
||||
==Required==
|
||||
|
||||
Version of rule-set, one of `1` or `2`.
|
||||
Version of rule-set.
|
||||
|
||||
* 1: Initial rule-set version, since sing-box 1.8.0.
|
||||
* 2: Optimized memory usages of `domain_suffix` rules.
|
||||
|
||||
The new rule-set version `2` does not make any changes to the format, only affecting `binary` rule-sets compiled by command `rule-set compile`
|
||||
|
||||
Since 1.10.0, the optimization is always applied to `source` rule-sets even if version is set to `1`.
|
||||
|
||||
It is recommended to upgrade to `2` after sing-box 1.10.0 becomes a stable version.
|
||||
* 1: sing-box 1.8.0: Initial rule-set version.
|
||||
* 2: sing-box 1.10.0: Optimized memory usages of `domain_suffix` rules in binary rule-sets.
|
||||
* 3: sing-box 1.11.0: Added `network_type`, `network_is_expensive` and `network_is_constrainted` rule items.
|
||||
|
||||
#### rules
|
||||
|
||||
==Required==
|
||||
|
||||
List of [Headless Rule](./headless-rule.md/).
|
||||
List of [Headless Rule](../headless-rule/).
|
||||
|
||||
46
docs/configuration/rule-set/source-format.zh.md
Normal file
46
docs/configuration/rule-set/source-format.zh.md
Normal file
@@ -0,0 +1,46 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
# 源文件格式
|
||||
|
||||
!!! quote "sing-box 1.11.0 中的更改"
|
||||
|
||||
:material-plus: version `3`
|
||||
|
||||
!!! quote "sing-box 1.10.0 中的更改"
|
||||
|
||||
:material-plus: version `2`
|
||||
|
||||
!!! question "自 sing-box 1.8.0 起"
|
||||
|
||||
### 结构
|
||||
|
||||
```json
|
||||
{
|
||||
"version": 3,
|
||||
"rules": []
|
||||
}
|
||||
```
|
||||
|
||||
### 编译
|
||||
|
||||
使用 `sing-box rule-set compile [--output <file-name>.srs] <file-name>.json` 以编译源文件为二进制规则集。
|
||||
|
||||
### 字段
|
||||
|
||||
#### version
|
||||
|
||||
==必填==
|
||||
|
||||
规则集版本。
|
||||
|
||||
* 1: sing-box 1.8.0: 初始规则集版本。
|
||||
* 2: sing-box 1.10.0: 优化了二进制规则集中 `domain_suffix` 规则的内存使用。
|
||||
* 3: sing-box 1.11.0: 添加了 `network_type`、 `network_is_expensive` 和 `network_is_constrainted` 规则项。
|
||||
|
||||
#### rules
|
||||
|
||||
==必填==
|
||||
|
||||
一组 [无头规则](../headless-rule/).
|
||||
@@ -22,14 +22,6 @@ check [Migration](../migration/#migrate-legacy-inbound-fields-to-rule-actions).
|
||||
|
||||
Old fields will be removed in sing-box 1.13.0.
|
||||
|
||||
#### Legacy DNS route options
|
||||
|
||||
Legacy DNS route options (`disable_cache`, `rewrite_ttl`, `client_subnet`) are deprecated
|
||||
and can be replaced by rule actions,
|
||||
check [Migration](../migration/#migrate-legacy-dns-route-options-to-rule-actions).
|
||||
|
||||
Old fields will be removed in sing-box 1.12.0.
|
||||
|
||||
## 1.10.0
|
||||
|
||||
#### TUN address fields are merged
|
||||
|
||||
@@ -20,13 +20,6 @@ icon: material/delete-alert
|
||||
|
||||
旧字段将在 sing-box 1.13.0 中被移除。
|
||||
|
||||
#### 旧的 DNS 路由参数
|
||||
|
||||
旧的 DNS 路由参数(`disable_cache`、`rewrite_ttl`、`client_subnet`)已废弃且可以通过规则动作替代,
|
||||
参阅 [迁移指南](/migration/#migrate-legacy-dns-route-options-to-rule-actions)。
|
||||
|
||||
旧字段将在 sing-box 1.12.0 中被移除。
|
||||
|
||||
## 1.10.0
|
||||
|
||||
#### Match source 规则项已重命名
|
||||
|
||||
@@ -156,58 +156,6 @@ Inbound fields are deprecated and can be replaced by rule actions.
|
||||
}
|
||||
```
|
||||
|
||||
### Migrate legacy DNS route options to rule actions
|
||||
|
||||
Legacy DNS route options are deprecated and can be replaced by rule actions.
|
||||
|
||||
!!! info "References"
|
||||
|
||||
[DNS Rule](/configuration/dns/rule/) /
|
||||
[DNS Rule Action](/configuration/dns/rule_action/)
|
||||
|
||||
=== ":material-card-remove: Deprecated"
|
||||
|
||||
```json
|
||||
{
|
||||
"dns": {
|
||||
"rules": [
|
||||
{
|
||||
...,
|
||||
|
||||
"server": "local",
|
||||
"disable_cache": true,
|
||||
"rewrite_ttl": 600,
|
||||
"client_subnet": "1.1.1.1/24"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
=== ":material-card-multiple: New"
|
||||
|
||||
```json
|
||||
{
|
||||
"dns": {
|
||||
"rules": [
|
||||
{
|
||||
...,
|
||||
|
||||
"action": "route-options",
|
||||
"disable_cache": true,
|
||||
"rewrite_ttl": 600,
|
||||
"client_subnet": "1.1.1.1/24"
|
||||
},
|
||||
{
|
||||
...,
|
||||
|
||||
"server": "local"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 1.10.0
|
||||
|
||||
### TUN address fields are merged
|
||||
|
||||
@@ -156,58 +156,6 @@ icon: material/arrange-bring-forward
|
||||
}
|
||||
```
|
||||
|
||||
### 迁移旧的 DNS 路由选项到规则动作
|
||||
|
||||
旧的 DNS 路由选项已被弃用,且可以被规则动作替代。
|
||||
|
||||
!!! info "参考"
|
||||
|
||||
[DNS 规则](/zh/configuration/dns/rule/) /
|
||||
[DNS 规则动作](/zh/configuration/dns/rule_action/)
|
||||
|
||||
=== ":material-card-remove: 弃用的"
|
||||
|
||||
```json
|
||||
{
|
||||
"dns": {
|
||||
"rules": [
|
||||
{
|
||||
...,
|
||||
|
||||
"server": "local",
|
||||
"disable_cache": true,
|
||||
"rewrite_ttl": 600,
|
||||
"client_subnet": "1.1.1.1/24"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
=== ":material-card-multiple: 新的"
|
||||
|
||||
```json
|
||||
{
|
||||
"dns": {
|
||||
"rules": [
|
||||
{
|
||||
...,
|
||||
|
||||
"action": "route-options",
|
||||
"disable_cache": true,
|
||||
"rewrite_ttl": 600,
|
||||
"client_subnet": "1.1.1.1/24"
|
||||
},
|
||||
{
|
||||
...,
|
||||
|
||||
"server": "local"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 1.10.0
|
||||
|
||||
### TUN 地址字段已合并
|
||||
|
||||
@@ -100,15 +100,6 @@ var OptionInboundOptions = Note{
|
||||
MigrationLink: "https://sing-box.sagernet.org/migration/#migrate-legacy-special-outbounds-to-rule-actions",
|
||||
}
|
||||
|
||||
var OptionLegacyDNSRouteOptions = Note{
|
||||
Name: "legacy-dns-route-options",
|
||||
Description: "legacy dns route options",
|
||||
DeprecatedVersion: "1.11.0",
|
||||
ScheduledVersion: "1.12.0",
|
||||
EnvName: "LEGACY_DNS_ROUTE_OPTIONS",
|
||||
MigrationLink: "https://sing-box.sagernet.org/migration/#migrate-legacy-dns-route-options-to-rule-actions",
|
||||
}
|
||||
|
||||
var Options = []Note{
|
||||
OptionBadMatchSource,
|
||||
OptionGEOIP,
|
||||
@@ -116,5 +107,4 @@ var Options = []Note{
|
||||
OptionTUNAddressX,
|
||||
OptionSpecialOutbounds,
|
||||
OptionInboundOptions,
|
||||
OptionLegacyDNSRouteOptions,
|
||||
}
|
||||
|
||||
@@ -74,11 +74,7 @@ func (s *platformInterfaceStub) CreateDefaultInterfaceMonitor(logger logger.Logg
|
||||
return (*interfaceMonitorStub)(nil)
|
||||
}
|
||||
|
||||
func (s *platformInterfaceStub) UsePlatformInterfaceGetter() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *platformInterfaceStub) Interfaces() ([]control.Interface, error) {
|
||||
func (s *platformInterfaceStub) Interfaces() ([]adapter.NetworkInterface, error) {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
@@ -111,16 +107,8 @@ func (s *interfaceMonitorStub) Close() error {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
func (s *interfaceMonitorStub) DefaultInterfaceName(destination netip.Addr) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (s *interfaceMonitorStub) DefaultInterfaceIndex(destination netip.Addr) int {
|
||||
return -1
|
||||
}
|
||||
|
||||
func (s *interfaceMonitorStub) DefaultInterface(destination netip.Addr) (string, int) {
|
||||
return "", -1
|
||||
func (s *interfaceMonitorStub) DefaultInterface() *control.Interface {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *interfaceMonitorStub) OverrideAndroidVPN() bool {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build !linux
|
||||
//go:build !unix
|
||||
|
||||
package libbox
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//go:build unix
|
||||
|
||||
package libbox
|
||||
|
||||
import (
|
||||
@@ -1,15 +1,10 @@
|
||||
package libbox
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/netip"
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/sing-tun"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
"github.com/sagernet/sing/common/x/list"
|
||||
)
|
||||
|
||||
@@ -20,19 +15,9 @@ var (
|
||||
|
||||
type platformDefaultInterfaceMonitor struct {
|
||||
*platformInterfaceWrapper
|
||||
networkAddresses []networkAddress
|
||||
defaultInterfaceName string
|
||||
defaultInterfaceIndex int
|
||||
element *list.Element[tun.NetworkUpdateCallback]
|
||||
access sync.Mutex
|
||||
callbacks list.List[tun.DefaultInterfaceUpdateCallback]
|
||||
logger logger.Logger
|
||||
}
|
||||
|
||||
type networkAddress struct {
|
||||
interfaceName string
|
||||
interfaceIndex int
|
||||
addresses []netip.Prefix
|
||||
element *list.Element[tun.NetworkUpdateCallback]
|
||||
callbacks list.List[tun.DefaultInterfaceUpdateCallback]
|
||||
logger logger.Logger
|
||||
}
|
||||
|
||||
func (m *platformDefaultInterfaceMonitor) Start() error {
|
||||
@@ -43,37 +28,10 @@ func (m *platformDefaultInterfaceMonitor) Close() error {
|
||||
return m.iif.CloseDefaultInterfaceMonitor(m)
|
||||
}
|
||||
|
||||
func (m *platformDefaultInterfaceMonitor) DefaultInterfaceName(destination netip.Addr) string {
|
||||
for _, address := range m.networkAddresses {
|
||||
for _, prefix := range address.addresses {
|
||||
if prefix.Contains(destination) {
|
||||
return address.interfaceName
|
||||
}
|
||||
}
|
||||
}
|
||||
return m.defaultInterfaceName
|
||||
}
|
||||
|
||||
func (m *platformDefaultInterfaceMonitor) DefaultInterfaceIndex(destination netip.Addr) int {
|
||||
for _, address := range m.networkAddresses {
|
||||
for _, prefix := range address.addresses {
|
||||
if prefix.Contains(destination) {
|
||||
return address.interfaceIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
return m.defaultInterfaceIndex
|
||||
}
|
||||
|
||||
func (m *platformDefaultInterfaceMonitor) DefaultInterface(destination netip.Addr) (string, int) {
|
||||
for _, address := range m.networkAddresses {
|
||||
for _, prefix := range address.addresses {
|
||||
if prefix.Contains(destination) {
|
||||
return address.interfaceName, address.interfaceIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
return m.defaultInterfaceName, m.defaultInterfaceIndex
|
||||
func (m *platformDefaultInterfaceMonitor) DefaultInterface() *control.Interface {
|
||||
m.defaultInterfaceAccess.Lock()
|
||||
defer m.defaultInterfaceAccess.Unlock()
|
||||
return m.defaultInterface
|
||||
}
|
||||
|
||||
func (m *platformDefaultInterfaceMonitor) OverrideAndroidVPN() bool {
|
||||
@@ -85,96 +43,49 @@ func (m *platformDefaultInterfaceMonitor) AndroidVPNEnabled() bool {
|
||||
}
|
||||
|
||||
func (m *platformDefaultInterfaceMonitor) RegisterCallback(callback tun.DefaultInterfaceUpdateCallback) *list.Element[tun.DefaultInterfaceUpdateCallback] {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
m.defaultInterfaceAccess.Lock()
|
||||
defer m.defaultInterfaceAccess.Unlock()
|
||||
return m.callbacks.PushBack(callback)
|
||||
}
|
||||
|
||||
func (m *platformDefaultInterfaceMonitor) UnregisterCallback(element *list.Element[tun.DefaultInterfaceUpdateCallback]) {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
m.defaultInterfaceAccess.Lock()
|
||||
defer m.defaultInterfaceAccess.Unlock()
|
||||
m.callbacks.Remove(element)
|
||||
}
|
||||
|
||||
func (m *platformDefaultInterfaceMonitor) UpdateDefaultInterface(interfaceName string, interfaceIndex32 int32) {
|
||||
if interfaceName == "" || interfaceIndex32 == -1 {
|
||||
m.defaultInterfaceName = ""
|
||||
m.defaultInterfaceIndex = -1
|
||||
m.access.Lock()
|
||||
callbacks := m.callbacks.Array()
|
||||
m.access.Unlock()
|
||||
for _, callback := range callbacks {
|
||||
callback(tun.EventNoRoute)
|
||||
}
|
||||
return
|
||||
}
|
||||
var err error
|
||||
if m.iif.UsePlatformInterfaceGetter() {
|
||||
err = m.updateInterfacesPlatform()
|
||||
} else {
|
||||
err = m.updateInterfaces()
|
||||
}
|
||||
if err == nil {
|
||||
err = m.networkManager.UpdateInterfaces()
|
||||
}
|
||||
func (m *platformDefaultInterfaceMonitor) UpdateDefaultInterface(interfaceName string, interfaceIndex32 int32, isExpensive bool, isConstrained bool) {
|
||||
m.isExpensive = isExpensive
|
||||
m.isConstrained = isConstrained
|
||||
err := m.networkManager.UpdateInterfaces()
|
||||
if err != nil {
|
||||
m.logger.Error(E.Cause(err, "update interfaces"))
|
||||
}
|
||||
interfaceIndex := int(interfaceIndex32)
|
||||
if m.defaultInterfaceName == interfaceName && m.defaultInterfaceIndex == interfaceIndex {
|
||||
m.defaultInterfaceAccess.Lock()
|
||||
if interfaceIndex32 == -1 {
|
||||
m.defaultInterface = nil
|
||||
callbacks := m.callbacks.Array()
|
||||
m.defaultInterfaceAccess.Unlock()
|
||||
for _, callback := range callbacks {
|
||||
callback(tun.EventInterfaceUpdate)
|
||||
}
|
||||
return
|
||||
}
|
||||
oldInterface := m.defaultInterface
|
||||
newInterface, err := m.networkManager.InterfaceFinder().ByIndex(int(interfaceIndex32))
|
||||
if err != nil {
|
||||
m.defaultInterfaceAccess.Unlock()
|
||||
m.logger.Error(E.Cause(err, "find updated interface: ", interfaceName))
|
||||
return
|
||||
}
|
||||
m.defaultInterface = newInterface
|
||||
if oldInterface != nil && oldInterface.Name == m.defaultInterface.Name && oldInterface.Index == m.defaultInterface.Index {
|
||||
m.defaultInterfaceAccess.Unlock()
|
||||
return
|
||||
}
|
||||
m.defaultInterfaceName = interfaceName
|
||||
m.defaultInterfaceIndex = interfaceIndex
|
||||
m.access.Lock()
|
||||
callbacks := m.callbacks.Array()
|
||||
m.access.Unlock()
|
||||
m.defaultInterfaceAccess.Unlock()
|
||||
for _, callback := range callbacks {
|
||||
callback(tun.EventInterfaceUpdate)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *platformDefaultInterfaceMonitor) updateInterfaces() error {
|
||||
interfaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var addresses []networkAddress
|
||||
for _, iif := range interfaces {
|
||||
var netAddresses []net.Addr
|
||||
netAddresses, err = iif.Addrs()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var address networkAddress
|
||||
address.interfaceName = iif.Name
|
||||
address.interfaceIndex = iif.Index
|
||||
address.addresses = common.Map(common.FilterIsInstance(netAddresses, func(it net.Addr) (*net.IPNet, bool) {
|
||||
value, loaded := it.(*net.IPNet)
|
||||
return value, loaded
|
||||
}), func(it *net.IPNet) netip.Prefix {
|
||||
bits, _ := it.Mask.Size()
|
||||
return netip.PrefixFrom(M.AddrFromIP(it.IP), bits)
|
||||
})
|
||||
addresses = append(addresses, address)
|
||||
}
|
||||
m.networkAddresses = addresses
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *platformDefaultInterfaceMonitor) updateInterfacesPlatform() error {
|
||||
interfaces, err := m.Interfaces()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var addresses []networkAddress
|
||||
for _, iif := range interfaces {
|
||||
var address networkAddress
|
||||
address.interfaceName = iif.Name
|
||||
address.interfaceIndex = iif.Index
|
||||
// address.addresses = common.Map(iif.Addresses, netip.MustParsePrefix)
|
||||
addresses = append(addresses, address)
|
||||
}
|
||||
m.networkAddresses = addresses
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package libbox
|
||||
|
||||
import (
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
)
|
||||
|
||||
@@ -13,10 +14,8 @@ type PlatformInterface interface {
|
||||
FindConnectionOwner(ipProtocol int32, sourceAddress string, sourcePort int32, destinationAddress string, destinationPort int32) (int32, error)
|
||||
PackageNameByUid(uid int32) (string, error)
|
||||
UIDByPackageName(packageName string) (int32, error)
|
||||
UsePlatformDefaultInterfaceMonitor() bool
|
||||
StartDefaultInterfaceMonitor(listener InterfaceUpdateListener) error
|
||||
CloseDefaultInterfaceMonitor(listener InterfaceUpdateListener) error
|
||||
UsePlatformInterfaceGetter() bool
|
||||
GetInterfaces() (NetworkInterfaceIterator, error)
|
||||
UnderNetworkExtension() bool
|
||||
IncludeAllNetworks() bool
|
||||
@@ -31,15 +30,26 @@ type TunInterface interface {
|
||||
}
|
||||
|
||||
type InterfaceUpdateListener interface {
|
||||
UpdateDefaultInterface(interfaceName string, interfaceIndex int32)
|
||||
UpdateDefaultInterface(interfaceName string, interfaceIndex int32, isExpensive bool, isConstrained bool)
|
||||
}
|
||||
|
||||
const (
|
||||
InterfaceTypeWIFI = C.InterfaceTypeWIFI
|
||||
InterfaceTypeCellular = C.InterfaceTypeCellular
|
||||
InterfaceTypeEthernet = C.InterfaceTypeEthernet
|
||||
InterfaceTypeOther = C.InterfaceTypeOther
|
||||
)
|
||||
|
||||
type NetworkInterface struct {
|
||||
Index int32
|
||||
MTU int32
|
||||
Name string
|
||||
Addresses StringIterator
|
||||
Flags int32
|
||||
|
||||
Type string
|
||||
DNSServer StringIterator
|
||||
Metered bool
|
||||
}
|
||||
|
||||
type WIFIState struct {
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"github.com/sagernet/sing-box/common/process"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-tun"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
)
|
||||
|
||||
@@ -14,10 +13,8 @@ type Interface interface {
|
||||
UsePlatformAutoDetectInterfaceControl() bool
|
||||
AutoDetectInterfaceControl(fd int) error
|
||||
OpenTun(options *tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error)
|
||||
UsePlatformDefaultInterfaceMonitor() bool
|
||||
CreateDefaultInterfaceMonitor(logger logger.Logger) tun.DefaultInterfaceMonitor
|
||||
UsePlatformInterfaceGetter() bool
|
||||
Interfaces() ([]control.Interface, error)
|
||||
Interfaces() ([]adapter.NetworkInterface, error)
|
||||
UnderNetworkExtension() bool
|
||||
IncludeAllNetworks() bool
|
||||
ClearDNSCache()
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"os"
|
||||
"runtime"
|
||||
runtimeDebug "runtime/debug"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
@@ -54,7 +55,10 @@ func NewService(configContent string, platformInterface PlatformInterface) (*Box
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
urlTestHistoryStorage := urltest.NewHistoryStorage()
|
||||
ctx = service.ContextWithPtr(ctx, urlTestHistoryStorage)
|
||||
platformWrapper := &platformInterfaceWrapper{iif: platformInterface, useProcFS: platformInterface.UseProcFS()}
|
||||
platformWrapper := &platformInterfaceWrapper{
|
||||
iif: platformInterface,
|
||||
useProcFS: platformInterface.UseProcFS(),
|
||||
}
|
||||
service.MustRegister[platform.Interface](ctx, platformWrapper)
|
||||
instance, err := box.New(box.Options{
|
||||
Context: ctx,
|
||||
@@ -106,9 +110,14 @@ var (
|
||||
)
|
||||
|
||||
type platformInterfaceWrapper struct {
|
||||
iif PlatformInterface
|
||||
useProcFS bool
|
||||
networkManager adapter.NetworkManager
|
||||
iif PlatformInterface
|
||||
useProcFS bool
|
||||
networkManager adapter.NetworkManager
|
||||
myTunName string
|
||||
defaultInterfaceAccess sync.Mutex
|
||||
defaultInterface *control.Interface
|
||||
isExpensive bool
|
||||
isConstrained bool
|
||||
}
|
||||
|
||||
func (w *platformInterfaceWrapper) Initialize(networkManager adapter.NetworkManager) error {
|
||||
@@ -148,38 +157,42 @@ func (w *platformInterfaceWrapper) OpenTun(options *tun.Options, platformOptions
|
||||
return nil, E.Cause(err, "dup tun file descriptor")
|
||||
}
|
||||
options.FileDescriptor = dupFd
|
||||
w.myTunName = options.Name
|
||||
return tun.New(*options)
|
||||
}
|
||||
|
||||
func (w *platformInterfaceWrapper) UsePlatformDefaultInterfaceMonitor() bool {
|
||||
return w.iif.UsePlatformDefaultInterfaceMonitor()
|
||||
}
|
||||
|
||||
func (w *platformInterfaceWrapper) CreateDefaultInterfaceMonitor(logger logger.Logger) tun.DefaultInterfaceMonitor {
|
||||
return &platformDefaultInterfaceMonitor{
|
||||
platformInterfaceWrapper: w,
|
||||
defaultInterfaceIndex: -1,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *platformInterfaceWrapper) UsePlatformInterfaceGetter() bool {
|
||||
return w.iif.UsePlatformInterfaceGetter()
|
||||
}
|
||||
|
||||
func (w *platformInterfaceWrapper) Interfaces() ([]control.Interface, error) {
|
||||
func (w *platformInterfaceWrapper) Interfaces() ([]adapter.NetworkInterface, error) {
|
||||
interfaceIterator, err := w.iif.GetInterfaces()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var interfaces []control.Interface
|
||||
var interfaces []adapter.NetworkInterface
|
||||
for _, netInterface := range iteratorToArray[*NetworkInterface](interfaceIterator) {
|
||||
interfaces = append(interfaces, control.Interface{
|
||||
Index: int(netInterface.Index),
|
||||
MTU: int(netInterface.MTU),
|
||||
Name: netInterface.Name,
|
||||
Addresses: common.Map(iteratorToArray[string](netInterface.Addresses), netip.MustParsePrefix),
|
||||
Flags: linkFlags(uint32(netInterface.Flags)),
|
||||
if netInterface.Name == w.myTunName {
|
||||
continue
|
||||
}
|
||||
w.defaultInterfaceAccess.Lock()
|
||||
isDefault := w.defaultInterface != nil && int(netInterface.Index) == w.defaultInterface.Index
|
||||
w.defaultInterfaceAccess.Unlock()
|
||||
interfaces = append(interfaces, adapter.NetworkInterface{
|
||||
Interface: control.Interface{
|
||||
Index: int(netInterface.Index),
|
||||
MTU: int(netInterface.MTU),
|
||||
Name: netInterface.Name,
|
||||
Addresses: common.Map(iteratorToArray[string](netInterface.Addresses), netip.MustParsePrefix),
|
||||
Flags: linkFlags(uint32(netInterface.Flags)),
|
||||
},
|
||||
Type: netInterface.Type,
|
||||
DNSServers: iteratorToArray[string](netInterface.DNSServer),
|
||||
Expensive: netInterface.Metered || isDefault && w.isExpensive,
|
||||
Constrained: isDefault && w.isConstrained,
|
||||
})
|
||||
}
|
||||
return interfaces, nil
|
||||
|
||||
6
go.mod
6
go.mod
@@ -25,14 +25,14 @@ require (
|
||||
github.com/sagernet/gvisor v0.0.0-20241021032506-a4324256e4a3
|
||||
github.com/sagernet/quic-go v0.48.1-beta.1
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
|
||||
github.com/sagernet/sing v0.6.0-alpha.3
|
||||
github.com/sagernet/sing v0.6.0-alpha.4
|
||||
github.com/sagernet/sing-dns v0.4.0-alpha.1
|
||||
github.com/sagernet/sing-mux v0.3.0-alpha.1
|
||||
github.com/sagernet/sing-quic v0.3.0-rc.2
|
||||
github.com/sagernet/sing-shadowsocks v0.2.7
|
||||
github.com/sagernet/sing-shadowsocks2 v0.2.0
|
||||
github.com/sagernet/sing-shadowtls v0.1.4
|
||||
github.com/sagernet/sing-tun v0.6.0-alpha.3
|
||||
github.com/sagernet/sing-tun v0.6.0-alpha.5
|
||||
github.com/sagernet/sing-vmess v0.1.12
|
||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7
|
||||
github.com/sagernet/utls v1.6.7
|
||||
@@ -53,8 +53,6 @@ require (
|
||||
howett.net/plist v1.0.1
|
||||
)
|
||||
|
||||
//replace github.com/sagernet/sing => ../sing
|
||||
|
||||
require (
|
||||
github.com/ajg/form v1.5.1 // indirect
|
||||
github.com/andybalholm/brotli v1.0.6 // indirect
|
||||
|
||||
8
go.sum
8
go.sum
@@ -110,8 +110,8 @@ github.com/sagernet/quic-go v0.48.1-beta.1/go.mod h1:1WgdDIVD1Gybp40JTWketeSfKA/
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
||||
github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
|
||||
github.com/sagernet/sing v0.6.0-alpha.3 h1:GLp9d6Gbt+Ioeplauuzojz1nY2J6moceVGYIOv/h5gA=
|
||||
github.com/sagernet/sing v0.6.0-alpha.3/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||
github.com/sagernet/sing v0.6.0-alpha.4 h1:h9oshzhaY0ESPC9HERcXtT9MhK7Oyo/IWXVu1uIiw3Y=
|
||||
github.com/sagernet/sing v0.6.0-alpha.4/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||
github.com/sagernet/sing-dns v0.4.0-alpha.1 h1:2KlP8DeqtGkULFiZtvG2r7SuoJP6orANFzJwC5vDKvg=
|
||||
github.com/sagernet/sing-dns v0.4.0-alpha.1/go.mod h1:vgHATsm4wdymwpvBZPei8RY+546iGXS6hlWv2x6YKcM=
|
||||
github.com/sagernet/sing-mux v0.3.0-alpha.1 h1:IgNX5bJBpL41gGbp05pdDOvh/b5eUQ6cv9240+Ngipg=
|
||||
@@ -124,8 +124,8 @@ github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wK
|
||||
github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
|
||||
github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k=
|
||||
github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4=
|
||||
github.com/sagernet/sing-tun v0.6.0-alpha.3 h1:KddmFF9ZYC1n+HZzpAZ1StMC5v38ir6hH0PJ7uGpAGE=
|
||||
github.com/sagernet/sing-tun v0.6.0-alpha.3/go.mod h1:R/UaKB1oFIvAeMIH2btQCaDVsfCNomEjfYBSCSA94sw=
|
||||
github.com/sagernet/sing-tun v0.6.0-alpha.5 h1:N2m+tzpVgeDxsQzc3tq3Bge2uSRhJ72KXy4Zr03U+Cg=
|
||||
github.com/sagernet/sing-tun v0.6.0-alpha.5/go.mod h1:5FKJNou4ZfW3HhLoSpRRUc8RT+nsdFTvhJc+4MlBrOo=
|
||||
github.com/sagernet/sing-vmess v0.1.12 h1:2gFD8JJb+eTFMoa8FIVMnknEi+vCSfaiTXTfEYAYAPg=
|
||||
github.com/sagernet/sing-vmess v0.1.12/go.mod h1:luTSsfyBGAc9VhtCqwjR+dt1QgqBhuYBCONB/POhF8I=
|
||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=
|
||||
|
||||
@@ -95,6 +95,9 @@ type RawDefaultRule struct {
|
||||
User badoption.Listable[string] `json:"user,omitempty"`
|
||||
UserID badoption.Listable[int32] `json:"user_id,omitempty"`
|
||||
ClashMode string `json:"clash_mode,omitempty"`
|
||||
NetworkType badoption.Listable[string] `json:"network_type,omitempty"`
|
||||
NetworkIsExpensive bool `json:"network_is_expensive,omitempty"`
|
||||
NetworkIsConstrained bool `json:"network_is_constrained,omitempty"`
|
||||
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"`
|
||||
WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"`
|
||||
RuleSet badoption.Listable[string] `json:"rule_set,omitempty"`
|
||||
|
||||
@@ -7,8 +7,7 @@ import (
|
||||
"time"
|
||||
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/experimental/deprecated"
|
||||
dns "github.com/sagernet/sing-dns"
|
||||
"github.com/sagernet/sing-dns"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/json"
|
||||
"github.com/sagernet/sing/common/json/badjson"
|
||||
@@ -137,18 +136,10 @@ func (r *DNSRuleAction) UnmarshalJSONContext(ctx context.Context, data []byte) e
|
||||
return badjson.UnmarshallExcludedContext(ctx, data, (*_DNSRuleAction)(r), v)
|
||||
}
|
||||
|
||||
type _RouteActionOptions struct {
|
||||
Outbound string `json:"outbound,omitempty"`
|
||||
}
|
||||
|
||||
type RouteActionOptions _RouteActionOptions
|
||||
|
||||
func (r *RouteActionOptions) UnmarshalJSON(data []byte) error {
|
||||
err := json.Unmarshal(data, (*_RouteActionOptions)(r))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
type RouteActionOptions struct {
|
||||
Outbound string `json:"outbound,omitempty"`
|
||||
UDPDisableDomainUnmapping bool `json:"udp_disable_domain_unmapping,omitempty"`
|
||||
UDPConnect bool `json:"udp_connect,omitempty"`
|
||||
}
|
||||
|
||||
type _RouteOptionsActionOptions struct {
|
||||
@@ -169,29 +160,13 @@ func (r *RouteOptionsActionOptions) UnmarshalJSON(data []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type _DNSRouteActionOptions struct {
|
||||
Server string `json:"server,omitempty"`
|
||||
// Deprecated: Use DNSRouteOptionsActionOptions instead.
|
||||
DisableCache bool `json:"disable_cache,omitempty"`
|
||||
// Deprecated: Use DNSRouteOptionsActionOptions instead.
|
||||
RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
|
||||
// Deprecated: Use DNSRouteOptionsActionOptions instead.
|
||||
type DNSRouteActionOptions struct {
|
||||
Server string `json:"server,omitempty"`
|
||||
DisableCache bool `json:"disable_cache,omitempty"`
|
||||
RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
|
||||
ClientSubnet *badoption.Prefixable `json:"client_subnet,omitempty"`
|
||||
}
|
||||
|
||||
type DNSRouteActionOptions _DNSRouteActionOptions
|
||||
|
||||
func (r *DNSRouteActionOptions) UnmarshalJSONContext(ctx context.Context, data []byte) error {
|
||||
err := json.Unmarshal(data, (*_DNSRouteActionOptions)(r))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if r.DisableCache || r.RewriteTTL != nil || r.ClientSubnet != nil {
|
||||
deprecated.Report(ctx, deprecated.OptionLegacyDNSRouteOptions)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type _DNSRouteOptionsActionOptions struct {
|
||||
DisableCache bool `json:"disable_cache,omitempty"`
|
||||
RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
|
||||
|
||||
@@ -97,6 +97,9 @@ type RawDefaultDNSRule struct {
|
||||
UserID badoption.Listable[int32] `json:"user_id,omitempty"`
|
||||
Outbound badoption.Listable[string] `json:"outbound,omitempty"`
|
||||
ClashMode string `json:"clash_mode,omitempty"`
|
||||
NetworkType badoption.Listable[string] `json:"network_type,omitempty"`
|
||||
NetworkIsExpensive bool `json:"network_is_expensive,omitempty"`
|
||||
NetworkIsConstrained bool `json:"network_is_constrained,omitempty"`
|
||||
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"`
|
||||
WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"`
|
||||
RuleSet badoption.Listable[string] `json:"rule_set,omitempty"`
|
||||
|
||||
@@ -146,25 +146,28 @@ func (r HeadlessRule) IsValid() bool {
|
||||
}
|
||||
|
||||
type DefaultHeadlessRule struct {
|
||||
QueryType badoption.Listable[DNSQueryType] `json:"query_type,omitempty"`
|
||||
Network badoption.Listable[string] `json:"network,omitempty"`
|
||||
Domain badoption.Listable[string] `json:"domain,omitempty"`
|
||||
DomainSuffix badoption.Listable[string] `json:"domain_suffix,omitempty"`
|
||||
DomainKeyword badoption.Listable[string] `json:"domain_keyword,omitempty"`
|
||||
DomainRegex badoption.Listable[string] `json:"domain_regex,omitempty"`
|
||||
SourceIPCIDR badoption.Listable[string] `json:"source_ip_cidr,omitempty"`
|
||||
IPCIDR badoption.Listable[string] `json:"ip_cidr,omitempty"`
|
||||
SourcePort badoption.Listable[uint16] `json:"source_port,omitempty"`
|
||||
SourcePortRange badoption.Listable[string] `json:"source_port_range,omitempty"`
|
||||
Port badoption.Listable[uint16] `json:"port,omitempty"`
|
||||
PortRange badoption.Listable[string] `json:"port_range,omitempty"`
|
||||
ProcessName badoption.Listable[string] `json:"process_name,omitempty"`
|
||||
ProcessPath badoption.Listable[string] `json:"process_path,omitempty"`
|
||||
ProcessPathRegex badoption.Listable[string] `json:"process_path_regex,omitempty"`
|
||||
PackageName badoption.Listable[string] `json:"package_name,omitempty"`
|
||||
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"`
|
||||
WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"`
|
||||
Invert bool `json:"invert,omitempty"`
|
||||
QueryType badoption.Listable[DNSQueryType] `json:"query_type,omitempty"`
|
||||
Network badoption.Listable[string] `json:"network,omitempty"`
|
||||
Domain badoption.Listable[string] `json:"domain,omitempty"`
|
||||
DomainSuffix badoption.Listable[string] `json:"domain_suffix,omitempty"`
|
||||
DomainKeyword badoption.Listable[string] `json:"domain_keyword,omitempty"`
|
||||
DomainRegex badoption.Listable[string] `json:"domain_regex,omitempty"`
|
||||
SourceIPCIDR badoption.Listable[string] `json:"source_ip_cidr,omitempty"`
|
||||
IPCIDR badoption.Listable[string] `json:"ip_cidr,omitempty"`
|
||||
SourcePort badoption.Listable[uint16] `json:"source_port,omitempty"`
|
||||
SourcePortRange badoption.Listable[string] `json:"source_port_range,omitempty"`
|
||||
Port badoption.Listable[uint16] `json:"port,omitempty"`
|
||||
PortRange badoption.Listable[string] `json:"port_range,omitempty"`
|
||||
ProcessName badoption.Listable[string] `json:"process_name,omitempty"`
|
||||
ProcessPath badoption.Listable[string] `json:"process_path,omitempty"`
|
||||
ProcessPathRegex badoption.Listable[string] `json:"process_path_regex,omitempty"`
|
||||
PackageName badoption.Listable[string] `json:"package_name,omitempty"`
|
||||
NetworkType badoption.Listable[string] `json:"network_type,omitempty"`
|
||||
NetworkIsExpensive bool `json:"network_is_expensive,omitempty"`
|
||||
NetworkIsConstrained bool `json:"network_is_constrained,omitempty"`
|
||||
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"`
|
||||
WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"`
|
||||
Invert bool `json:"invert,omitempty"`
|
||||
|
||||
DomainMatcher *domain.Matcher `json:"-"`
|
||||
SourceIPSet *netipx.IPSet `json:"-"`
|
||||
@@ -191,7 +194,7 @@ func (r LogicalHeadlessRule) IsValid() bool {
|
||||
}
|
||||
|
||||
type _PlainRuleSetCompat struct {
|
||||
Version int `json:"version"`
|
||||
Version uint8 `json:"version"`
|
||||
Options PlainRuleSet `json:"-"`
|
||||
}
|
||||
|
||||
@@ -200,7 +203,7 @@ type PlainRuleSetCompat _PlainRuleSetCompat
|
||||
func (r PlainRuleSetCompat) MarshalJSON() ([]byte, error) {
|
||||
var v any
|
||||
switch r.Version {
|
||||
case C.RuleSetVersion1, C.RuleSetVersion2:
|
||||
case C.RuleSetVersion1, C.RuleSetVersion2, C.RuleSetVersion3:
|
||||
v = r.Options
|
||||
default:
|
||||
return nil, E.New("unknown rule-set version: ", r.Version)
|
||||
@@ -215,7 +218,7 @@ func (r *PlainRuleSetCompat) UnmarshalJSON(bytes []byte) error {
|
||||
}
|
||||
var v any
|
||||
switch r.Version {
|
||||
case C.RuleSetVersion1, C.RuleSetVersion2:
|
||||
case C.RuleSetVersion1, C.RuleSetVersion2, C.RuleSetVersion3:
|
||||
v = &r.Options
|
||||
case 0:
|
||||
return E.New("missing rule-set version")
|
||||
@@ -231,7 +234,7 @@ func (r *PlainRuleSetCompat) UnmarshalJSON(bytes []byte) error {
|
||||
|
||||
func (r PlainRuleSetCompat) Upgrade() (PlainRuleSet, error) {
|
||||
switch r.Version {
|
||||
case C.RuleSetVersion1, C.RuleSetVersion2:
|
||||
case C.RuleSetVersion1, C.RuleSetVersion2, C.RuleSetVersion3:
|
||||
default:
|
||||
return PlainRuleSet{}, E.New("unknown rule-set version: " + F.ToString(r.Version))
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ func (l *loopBackDetector) NewConn(conn net.Conn) net.Conn {
|
||||
}
|
||||
if udpConn, isUDPConn := conn.(abstractUDPConn); isUDPConn {
|
||||
if !source.Addr().IsLoopback() {
|
||||
_, err := l.networkManager.InterfaceFinder().InterfaceByAddr(source.Addr())
|
||||
_, err := l.networkManager.InterfaceFinder().ByAddr(source.Addr())
|
||||
if err != nil {
|
||||
return conn
|
||||
}
|
||||
@@ -59,7 +59,7 @@ func (l *loopBackDetector) NewPacketConn(conn N.NetPacketConn, destination M.Soc
|
||||
return conn
|
||||
}
|
||||
if !source.Addr().IsLoopback() {
|
||||
_, err := l.networkManager.InterfaceFinder().InterfaceByAddr(source.Addr())
|
||||
_, err := l.networkManager.InterfaceFinder().ByAddr(source.Addr())
|
||||
if err != nil {
|
||||
return conn
|
||||
}
|
||||
@@ -82,7 +82,7 @@ func (l *loopBackDetector) CheckPacketConn(source netip.AddrPort, local netip.Ad
|
||||
return false
|
||||
}
|
||||
if !source.Addr().IsLoopback() {
|
||||
_, err := l.networkManager.InterfaceFinder().InterfaceByAddr(source.Addr())
|
||||
_, err := l.networkManager.InterfaceFinder().ByAddr(source.Addr())
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
179
route/network.go
179
route/network.go
@@ -3,9 +3,10 @@ package route
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/netip"
|
||||
"net"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
@@ -15,33 +16,41 @@ import (
|
||||
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-tun"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/atomic"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
"github.com/sagernet/sing/common/winpowrprof"
|
||||
"github.com/sagernet/sing/service"
|
||||
"github.com/sagernet/sing/service/pause"
|
||||
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
var _ adapter.NetworkManager = (*NetworkManager)(nil)
|
||||
|
||||
type NetworkManager struct {
|
||||
logger logger.ContextLogger
|
||||
interfaceFinder *control.DefaultInterfaceFinder
|
||||
logger logger.ContextLogger
|
||||
interfaceFinder *control.DefaultInterfaceFinder
|
||||
networkInterfaces atomic.TypedValue[[]adapter.NetworkInterface]
|
||||
|
||||
autoDetectInterface bool
|
||||
defaultInterface string
|
||||
defaultMark uint32
|
||||
autoRedirectOutputMark uint32
|
||||
networkMonitor tun.NetworkUpdateMonitor
|
||||
interfaceMonitor tun.DefaultInterfaceMonitor
|
||||
packageManager tun.PackageManager
|
||||
powerListener winpowrprof.EventListener
|
||||
pauseManager pause.Manager
|
||||
platformInterface platform.Interface
|
||||
outboundManager adapter.OutboundManager
|
||||
wifiState adapter.WIFIState
|
||||
started bool
|
||||
|
||||
networkMonitor tun.NetworkUpdateMonitor
|
||||
interfaceMonitor tun.DefaultInterfaceMonitor
|
||||
packageManager tun.PackageManager
|
||||
powerListener winpowrprof.EventListener
|
||||
pauseManager pause.Manager
|
||||
platformInterface platform.Interface
|
||||
outboundManager adapter.OutboundManager
|
||||
wifiState adapter.WIFIState
|
||||
started bool
|
||||
}
|
||||
|
||||
func NewNetworkManager(ctx context.Context, logger logger.ContextLogger, routeOptions option.RouteOptions) (*NetworkManager, error) {
|
||||
@@ -55,7 +64,7 @@ func NewNetworkManager(ctx context.Context, logger logger.ContextLogger, routeOp
|
||||
platformInterface: service.FromContext[platform.Interface](ctx),
|
||||
outboundManager: service.FromContext[adapter.OutboundManager](ctx),
|
||||
}
|
||||
usePlatformDefaultInterfaceMonitor := nm.platformInterface != nil && nm.platformInterface.UsePlatformDefaultInterfaceMonitor()
|
||||
usePlatformDefaultInterfaceMonitor := nm.platformInterface != nil
|
||||
enforceInterfaceMonitor := routeOptions.AutoDetectInterface
|
||||
if !usePlatformDefaultInterfaceMonitor {
|
||||
networkMonitor, err := tun.NewNetworkUpdateMonitor(logger)
|
||||
@@ -90,17 +99,17 @@ func (r *NetworkManager) Start(stage adapter.StartStage) error {
|
||||
monitor := taskmonitor.New(r.logger, C.StartTimeout)
|
||||
switch stage {
|
||||
case adapter.StartStateInitialize:
|
||||
if r.interfaceMonitor != nil {
|
||||
monitor.Start("initialize interface monitor")
|
||||
err := r.interfaceMonitor.Start()
|
||||
if r.networkMonitor != nil {
|
||||
monitor.Start("initialize network monitor")
|
||||
err := r.networkMonitor.Start()
|
||||
monitor.Finish()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if r.networkMonitor != nil {
|
||||
monitor.Start("initialize network monitor")
|
||||
err := r.networkMonitor.Start()
|
||||
if r.interfaceMonitor != nil {
|
||||
monitor.Start("initialize interface monitor")
|
||||
err := r.interfaceMonitor.Start()
|
||||
monitor.Finish()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -151,20 +160,6 @@ func (r *NetworkManager) Start(stage adapter.StartStage) error {
|
||||
func (r *NetworkManager) Close() error {
|
||||
monitor := taskmonitor.New(r.logger, C.StopTimeout)
|
||||
var err error
|
||||
if r.interfaceMonitor != nil {
|
||||
monitor.Start("close interface monitor")
|
||||
err = E.Append(err, r.interfaceMonitor.Close(), func(err error) error {
|
||||
return E.Cause(err, "close interface monitor")
|
||||
})
|
||||
monitor.Finish()
|
||||
}
|
||||
if r.networkMonitor != nil {
|
||||
monitor.Start("close network monitor")
|
||||
err = E.Append(err, r.networkMonitor.Close(), func(err error) error {
|
||||
return E.Cause(err, "close network monitor")
|
||||
})
|
||||
monitor.Finish()
|
||||
}
|
||||
if r.packageManager != nil {
|
||||
monitor.Start("close package manager")
|
||||
err = E.Append(err, r.packageManager.Close(), func(err error) error {
|
||||
@@ -179,6 +174,20 @@ func (r *NetworkManager) Close() error {
|
||||
})
|
||||
monitor.Finish()
|
||||
}
|
||||
if r.interfaceMonitor != nil {
|
||||
monitor.Start("close interface monitor")
|
||||
err = E.Append(err, r.interfaceMonitor.Close(), func(err error) error {
|
||||
return E.Cause(err, "close interface monitor")
|
||||
})
|
||||
monitor.Finish()
|
||||
}
|
||||
if r.networkMonitor != nil {
|
||||
monitor.Start("close network monitor")
|
||||
err = E.Append(err, r.networkMonitor.Close(), func(err error) error {
|
||||
return E.Cause(err, "close network monitor")
|
||||
})
|
||||
monitor.Finish()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -187,18 +196,75 @@ func (r *NetworkManager) InterfaceFinder() control.InterfaceFinder {
|
||||
}
|
||||
|
||||
func (r *NetworkManager) UpdateInterfaces() error {
|
||||
if r.platformInterface == nil || !r.platformInterface.UsePlatformInterfaceGetter() {
|
||||
if r.platformInterface == nil {
|
||||
return r.interfaceFinder.Update()
|
||||
} else {
|
||||
interfaces, err := r.platformInterface.Interfaces()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.interfaceFinder.UpdateInterfaces(interfaces)
|
||||
if C.IsDarwin {
|
||||
err = r.interfaceFinder.Update()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// NEInterface only provides name,index and type
|
||||
interfaces = common.Map(interfaces, func(it adapter.NetworkInterface) adapter.NetworkInterface {
|
||||
iif, _ := r.interfaceFinder.ByIndex(it.Index)
|
||||
if iif != nil {
|
||||
it.Interface = *iif
|
||||
}
|
||||
return it
|
||||
})
|
||||
} else {
|
||||
r.interfaceFinder.UpdateInterfaces(common.Map(interfaces, func(it adapter.NetworkInterface) control.Interface { return it.Interface }))
|
||||
}
|
||||
oldInterfaces := r.networkInterfaces.Load()
|
||||
newInterfaces := common.Filter(interfaces, func(it adapter.NetworkInterface) bool {
|
||||
return it.Flags&net.FlagUp != 0
|
||||
})
|
||||
r.networkInterfaces.Store(newInterfaces)
|
||||
if !slices.EqualFunc(oldInterfaces, newInterfaces, func(oldInterface adapter.NetworkInterface, newInterface adapter.NetworkInterface) bool {
|
||||
return oldInterface.Interface.Index == newInterface.Interface.Index &&
|
||||
oldInterface.Interface.Name == newInterface.Interface.Name &&
|
||||
oldInterface.Interface.Flags == newInterface.Interface.Flags &&
|
||||
oldInterface.Type == newInterface.Type &&
|
||||
oldInterface.Expensive == newInterface.Expensive &&
|
||||
oldInterface.Constrained == newInterface.Constrained
|
||||
}) {
|
||||
r.logger.Info("updated available networks: ", strings.Join(common.Map(newInterfaces, func(it adapter.NetworkInterface) string {
|
||||
var options []string
|
||||
options = append(options, F.ToString(it.Type))
|
||||
if it.Expensive {
|
||||
options = append(options, "expensive")
|
||||
}
|
||||
if it.Constrained {
|
||||
options = append(options, "constrained")
|
||||
}
|
||||
return F.ToString(it.Name, " (", strings.Join(options, ", "), ")")
|
||||
}), ", "))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (r *NetworkManager) DefaultNetworkInterface() *adapter.NetworkInterface {
|
||||
iif := r.interfaceMonitor.DefaultInterface()
|
||||
if iif == nil {
|
||||
return nil
|
||||
}
|
||||
for _, it := range r.networkInterfaces.Load() {
|
||||
if it.Interface.Index == iif.Index {
|
||||
return &it
|
||||
}
|
||||
}
|
||||
return &adapter.NetworkInterface{Interface: *iif}
|
||||
}
|
||||
|
||||
func (r *NetworkManager) NetworkInterfaces() []adapter.NetworkInterface {
|
||||
return r.networkInterfaces.Load()
|
||||
}
|
||||
|
||||
func (r *NetworkManager) DefaultInterface() string {
|
||||
return r.defaultInterface
|
||||
}
|
||||
@@ -220,18 +286,17 @@ func (r *NetworkManager) AutoDetectInterfaceFunc() control.Func {
|
||||
}
|
||||
return control.BindToInterfaceFunc(r.interfaceFinder, func(network string, address string) (interfaceName string, interfaceIndex int, err error) {
|
||||
remoteAddr := M.ParseSocksaddr(address).Addr
|
||||
if C.IsLinux {
|
||||
interfaceName, interfaceIndex = r.interfaceMonitor.DefaultInterface(remoteAddr)
|
||||
if interfaceIndex == -1 {
|
||||
err = tun.ErrNoRoute
|
||||
}
|
||||
} else {
|
||||
interfaceIndex = r.interfaceMonitor.DefaultInterfaceIndex(remoteAddr)
|
||||
if interfaceIndex == -1 {
|
||||
err = tun.ErrNoRoute
|
||||
if remoteAddr.IsValid() {
|
||||
iif, err := r.interfaceFinder.ByAddr(remoteAddr)
|
||||
if err == nil {
|
||||
return iif.Name, iif.Index, nil
|
||||
}
|
||||
}
|
||||
return
|
||||
defaultInterface := r.interfaceMonitor.DefaultInterface()
|
||||
if defaultInterface == nil {
|
||||
return "", -1, tun.ErrNoRoute
|
||||
}
|
||||
return defaultInterface.Name, defaultInterface.Index, nil
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -285,6 +350,12 @@ func (r *NetworkManager) notifyNetworkUpdate(event int) {
|
||||
r.logger.Error("missing default interface")
|
||||
} else {
|
||||
r.pauseManager.NetworkWake()
|
||||
defaultInterface := r.DefaultNetworkInterface()
|
||||
if defaultInterface == nil {
|
||||
panic("invalid interface context")
|
||||
}
|
||||
var options []string
|
||||
options = append(options, F.ToString("index ", defaultInterface.Index))
|
||||
if C.IsAndroid && r.platformInterface == nil {
|
||||
var vpnStatus string
|
||||
if r.interfaceMonitor.AndroidVPNEnabled() {
|
||||
@@ -292,17 +363,24 @@ func (r *NetworkManager) notifyNetworkUpdate(event int) {
|
||||
} else {
|
||||
vpnStatus = "disabled"
|
||||
}
|
||||
r.logger.Info("updated default interface ", r.interfaceMonitor.DefaultInterfaceName(netip.IPv4Unspecified()), ", index ", r.interfaceMonitor.DefaultInterfaceIndex(netip.IPv4Unspecified()), ", vpn ", vpnStatus)
|
||||
options = append(options, "vpn "+vpnStatus)
|
||||
} else {
|
||||
r.logger.Info("updated default interface ", r.interfaceMonitor.DefaultInterfaceName(netip.IPv4Unspecified()), ", index ", r.interfaceMonitor.DefaultInterfaceIndex(netip.IPv4Unspecified()))
|
||||
if defaultInterface.Type != "" {
|
||||
options = append(options, F.ToString("type ", defaultInterface.Type))
|
||||
}
|
||||
if defaultInterface.Expensive {
|
||||
options = append(options, "expensive")
|
||||
}
|
||||
if defaultInterface.Constrained {
|
||||
options = append(options, "constrained")
|
||||
}
|
||||
}
|
||||
r.logger.Info("updated default interface ", defaultInterface.Name, ", ", strings.Join(options, ", "))
|
||||
if r.platformInterface != nil {
|
||||
state := r.platformInterface.ReadWIFIState()
|
||||
if state != r.wifiState {
|
||||
r.wifiState = state
|
||||
if state.SSID == "" && state.BSSID == "" {
|
||||
r.logger.Info("updated WIFI state: disconnected")
|
||||
} else {
|
||||
if state.SSID != "" {
|
||||
r.logger.Info("updated WIFI state: SSID=", state.SSID, ", BSSID=", state.BSSID)
|
||||
}
|
||||
}
|
||||
@@ -312,7 +390,6 @@ func (r *NetworkManager) notifyNetworkUpdate(event int) {
|
||||
if !r.started {
|
||||
return
|
||||
}
|
||||
|
||||
r.ResetNetwork()
|
||||
}
|
||||
|
||||
|
||||
@@ -437,6 +437,12 @@ match:
|
||||
}
|
||||
}
|
||||
switch action := currentRule.Action().(type) {
|
||||
case *rule.RuleActionRoute:
|
||||
metadata.UDPDisableDomainUnmapping = action.UDPDisableDomainUnmapping
|
||||
metadata.UDPConnect = action.UDPConnect
|
||||
selectedRule = currentRule
|
||||
selectedRuleIndex = currentRuleIndex
|
||||
break match
|
||||
case *rule.RuleActionRouteOptions:
|
||||
metadata.UDPDisableDomainUnmapping = action.UDPDisableDomainUnmapping
|
||||
metadata.UDPConnect = action.UDPConnect
|
||||
@@ -467,7 +473,12 @@ match:
|
||||
selectedRuleIndex = currentRuleIndex
|
||||
break match
|
||||
}
|
||||
ruleIndex = currentRuleIndex
|
||||
if ruleIndex == -1 {
|
||||
ruleIndex = currentRuleIndex
|
||||
} else {
|
||||
ruleIndex += currentRuleIndex
|
||||
}
|
||||
ruleIndex++
|
||||
}
|
||||
if !preMatch && metadata.Destination.Addr.IsUnspecified() {
|
||||
newBuffer, newPacketBuffers, newErr := r.actionSniff(ctx, metadata, &rule.RuleActionSniff{}, inputConn, inputPacketConn)
|
||||
|
||||
@@ -29,6 +29,10 @@ func NewRuleAction(ctx context.Context, logger logger.ContextLogger, action opti
|
||||
case C.RuleActionTypeRoute:
|
||||
return &RuleActionRoute{
|
||||
Outbound: action.RouteOptions.Outbound,
|
||||
RuleActionRouteOptions: RuleActionRouteOptions{
|
||||
UDPDisableDomainUnmapping: action.RouteOptions.UDPDisableDomainUnmapping,
|
||||
UDPConnect: action.RouteOptions.UDPConnect,
|
||||
},
|
||||
}, nil
|
||||
case C.RuleActionTypeRouteOptions:
|
||||
return &RuleActionRouteOptions{
|
||||
@@ -85,10 +89,12 @@ func NewDNSRuleAction(logger logger.ContextLogger, action option.DNSRuleAction)
|
||||
return nil
|
||||
case C.RuleActionTypeRoute:
|
||||
return &RuleActionDNSRoute{
|
||||
Server: action.RouteOptions.Server,
|
||||
DisableCache: action.RouteOptions.DisableCache,
|
||||
RewriteTTL: action.RouteOptions.RewriteTTL,
|
||||
ClientSubnet: netip.Prefix(common.PtrValueOrDefault(action.RouteOptions.ClientSubnet)),
|
||||
Server: action.RouteOptions.Server,
|
||||
RuleActionDNSRouteOptions: RuleActionDNSRouteOptions{
|
||||
DisableCache: action.RouteOptions.DisableCache,
|
||||
RewriteTTL: action.RouteOptions.RewriteTTL,
|
||||
ClientSubnet: netip.Prefix(common.PtrValueOrDefault(action.RouteOptions.ClientSubnet)),
|
||||
},
|
||||
}
|
||||
case C.RuleActionTypeRouteOptions:
|
||||
return &RuleActionDNSRouteOptions{
|
||||
@@ -109,6 +115,7 @@ func NewDNSRuleAction(logger logger.ContextLogger, action option.DNSRuleAction)
|
||||
|
||||
type RuleActionRoute struct {
|
||||
Outbound string
|
||||
RuleActionRouteOptions
|
||||
}
|
||||
|
||||
func (r *RuleActionRoute) Type() string {
|
||||
@@ -116,7 +123,15 @@ func (r *RuleActionRoute) Type() string {
|
||||
}
|
||||
|
||||
func (r *RuleActionRoute) String() string {
|
||||
return F.ToString("route(", r.Outbound, ")")
|
||||
var descriptions []string
|
||||
descriptions = append(descriptions, r.Outbound)
|
||||
if r.UDPDisableDomainUnmapping {
|
||||
descriptions = append(descriptions, "udp-disable-domain-unmapping")
|
||||
}
|
||||
if r.UDPConnect {
|
||||
descriptions = append(descriptions, "udp-connect")
|
||||
}
|
||||
return F.ToString("route(", strings.Join(descriptions, ","), ")")
|
||||
}
|
||||
|
||||
type RuleActionRouteOptions struct {
|
||||
@@ -140,10 +155,8 @@ func (r *RuleActionRouteOptions) String() string {
|
||||
}
|
||||
|
||||
type RuleActionDNSRoute struct {
|
||||
Server string
|
||||
DisableCache bool
|
||||
RewriteTTL *uint32
|
||||
ClientSubnet netip.Prefix
|
||||
Server string
|
||||
RuleActionDNSRouteOptions
|
||||
}
|
||||
|
||||
func (r *RuleActionDNSRoute) Type() string {
|
||||
@@ -151,7 +164,18 @@ func (r *RuleActionDNSRoute) Type() string {
|
||||
}
|
||||
|
||||
func (r *RuleActionDNSRoute) String() string {
|
||||
return F.ToString("route(", r.Server, ")")
|
||||
var descriptions []string
|
||||
descriptions = append(descriptions, r.Server)
|
||||
if r.DisableCache {
|
||||
descriptions = append(descriptions, "disable-cache")
|
||||
}
|
||||
if r.RewriteTTL != nil {
|
||||
descriptions = append(descriptions, F.ToString("rewrite-ttl=", *r.RewriteTTL))
|
||||
}
|
||||
if r.ClientSubnet.IsValid() {
|
||||
descriptions = append(descriptions, F.ToString("client-subnet=", r.ClientSubnet))
|
||||
}
|
||||
return F.ToString("route(", strings.Join(descriptions, ","), ")")
|
||||
}
|
||||
|
||||
type RuleActionDNSRouteOptions struct {
|
||||
@@ -170,10 +194,10 @@ func (r *RuleActionDNSRouteOptions) String() string {
|
||||
descriptions = append(descriptions, "disable-cache")
|
||||
}
|
||||
if r.RewriteTTL != nil {
|
||||
descriptions = append(descriptions, F.ToString("rewrite-ttl(", *r.RewriteTTL, ")"))
|
||||
descriptions = append(descriptions, F.ToString("rewrite-ttl=", *r.RewriteTTL))
|
||||
}
|
||||
if r.ClientSubnet.IsValid() {
|
||||
descriptions = append(descriptions, F.ToString("client-subnet(", r.ClientSubnet, ")"))
|
||||
descriptions = append(descriptions, F.ToString("client-subnet=", r.ClientSubnet))
|
||||
}
|
||||
return F.ToString("route-options(", strings.Join(descriptions, ","), ")")
|
||||
}
|
||||
|
||||
@@ -224,6 +224,21 @@ func NewDefaultRule(ctx context.Context, logger log.ContextLogger, options optio
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.NetworkType) > 0 {
|
||||
item := NewNetworkTypeItem(networkManager, options.NetworkType)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if options.NetworkIsExpensive {
|
||||
item := NewNetworkIsExpensiveItem(networkManager)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if options.NetworkIsConstrained {
|
||||
item := NewNetworkIsConstrainedItem(networkManager)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.WIFISSID) > 0 {
|
||||
item := NewWIFISSIDItem(networkManager, options.WIFISSID)
|
||||
rule.items = append(rule.items, item)
|
||||
|
||||
@@ -221,6 +221,21 @@ func NewDefaultDNSRule(ctx context.Context, logger log.ContextLogger, options op
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.NetworkType) > 0 {
|
||||
item := NewNetworkTypeItem(networkManager, options.NetworkType)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if options.NetworkIsExpensive {
|
||||
item := NewNetworkIsExpensiveItem(networkManager)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if options.NetworkIsConstrained {
|
||||
item := NewNetworkIsConstrainedItem(networkManager)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.WIFISSID) > 0 {
|
||||
item := NewWIFISSIDItem(networkManager, options.WIFISSID)
|
||||
rule.items = append(rule.items, item)
|
||||
|
||||
@@ -140,18 +140,33 @@ func NewDefaultHeadlessRule(ctx context.Context, options option.DefaultHeadlessR
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.WIFISSID) > 0 {
|
||||
if networkManager != nil {
|
||||
item := NewWIFISSIDItem(networkManager, options.WIFISSID)
|
||||
if networkManager != nil {
|
||||
if len(options.NetworkType) > 0 {
|
||||
item := NewNetworkTypeItem(networkManager, options.NetworkType)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
}
|
||||
if len(options.WIFIBSSID) > 0 {
|
||||
if networkManager != nil {
|
||||
if options.NetworkIsExpensive {
|
||||
item := NewNetworkIsExpensiveItem(networkManager)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if options.NetworkIsConstrained {
|
||||
item := NewNetworkIsConstrainedItem(networkManager)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.WIFISSID) > 0 {
|
||||
item := NewWIFISSIDItem(networkManager, options.WIFISSID)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
|
||||
}
|
||||
if len(options.WIFIBSSID) > 0 {
|
||||
item := NewWIFIBSSIDItem(networkManager, options.WIFIBSSID)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
|
||||
}
|
||||
}
|
||||
if len(options.AdGuardDomain) > 0 {
|
||||
|
||||
29
route/rule/rule_item_network_is_constrained.go
Normal file
29
route/rule/rule_item_network_is_constrained.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package rule
|
||||
|
||||
import (
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
)
|
||||
|
||||
var _ RuleItem = (*NetworkIsConstrainedItem)(nil)
|
||||
|
||||
type NetworkIsConstrainedItem struct {
|
||||
networkManager adapter.NetworkManager
|
||||
}
|
||||
|
||||
func NewNetworkIsConstrainedItem(networkManager adapter.NetworkManager) *NetworkIsConstrainedItem {
|
||||
return &NetworkIsConstrainedItem{
|
||||
networkManager: networkManager,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *NetworkIsConstrainedItem) Match(metadata *adapter.InboundContext) bool {
|
||||
networkInterface := r.networkManager.DefaultNetworkInterface()
|
||||
if networkInterface == nil {
|
||||
return false
|
||||
}
|
||||
return networkInterface.Constrained
|
||||
}
|
||||
|
||||
func (r *NetworkIsConstrainedItem) String() string {
|
||||
return "network_is_expensive=true"
|
||||
}
|
||||
29
route/rule/rule_item_network_is_expensive.go
Normal file
29
route/rule/rule_item_network_is_expensive.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package rule
|
||||
|
||||
import (
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
)
|
||||
|
||||
var _ RuleItem = (*NetworkIsExpensiveItem)(nil)
|
||||
|
||||
type NetworkIsExpensiveItem struct {
|
||||
networkManager adapter.NetworkManager
|
||||
}
|
||||
|
||||
func NewNetworkIsExpensiveItem(networkManager adapter.NetworkManager) *NetworkIsExpensiveItem {
|
||||
return &NetworkIsExpensiveItem{
|
||||
networkManager: networkManager,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *NetworkIsExpensiveItem) Match(metadata *adapter.InboundContext) bool {
|
||||
networkInterface := r.networkManager.DefaultNetworkInterface()
|
||||
if networkInterface == nil {
|
||||
return false
|
||||
}
|
||||
return networkInterface.Expensive
|
||||
}
|
||||
|
||||
func (r *NetworkIsExpensiveItem) String() string {
|
||||
return "network_is_expensive=true"
|
||||
}
|
||||
39
route/rule/rule_item_network_type.go
Normal file
39
route/rule/rule_item_network_type.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package rule
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing/common"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
)
|
||||
|
||||
var _ RuleItem = (*NetworkTypeItem)(nil)
|
||||
|
||||
type NetworkTypeItem struct {
|
||||
networkManager adapter.NetworkManager
|
||||
networkType []string
|
||||
}
|
||||
|
||||
func NewNetworkTypeItem(networkManager adapter.NetworkManager, networkType []string) *NetworkTypeItem {
|
||||
return &NetworkTypeItem{
|
||||
networkManager: networkManager,
|
||||
networkType: networkType,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *NetworkTypeItem) Match(metadata *adapter.InboundContext) bool {
|
||||
networkInterface := r.networkManager.DefaultNetworkInterface()
|
||||
if networkInterface == nil {
|
||||
return false
|
||||
}
|
||||
return common.Contains(r.networkType, networkInterface.Type)
|
||||
}
|
||||
|
||||
func (r *NetworkTypeItem) String() string {
|
||||
if len(r.networkType) == 1 {
|
||||
return F.ToString("network_type=", r.networkType[0])
|
||||
} else {
|
||||
return F.ToString("network_type=", "["+strings.Join(F.MapToString(r.networkType), " ")+"]")
|
||||
}
|
||||
}
|
||||
@@ -95,33 +95,34 @@ func (s *LocalRuleSet) StartContext(ctx context.Context, startContext *adapter.H
|
||||
}
|
||||
|
||||
func (s *LocalRuleSet) reloadFile(path string) error {
|
||||
var plainRuleSet option.PlainRuleSet
|
||||
var ruleSet option.PlainRuleSetCompat
|
||||
switch s.fileFormat {
|
||||
case C.RuleSetFormatSource, "":
|
||||
content, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
compat, err := json.UnmarshalExtended[option.PlainRuleSetCompat](content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
plainRuleSet, err = compat.Upgrade()
|
||||
ruleSet, err = json.UnmarshalExtended[option.PlainRuleSetCompat](content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case C.RuleSetFormatBinary:
|
||||
setFile, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
plainRuleSet, err = srs.Read(setFile, false)
|
||||
ruleSet, err = srs.Read(setFile, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return E.New("unknown rule-set format: ", s.fileFormat)
|
||||
}
|
||||
plainRuleSet, err := ruleSet.Upgrade()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.reloadRules(plainRuleSet.Rules)
|
||||
}
|
||||
|
||||
|
||||
@@ -155,28 +155,27 @@ func (s *RemoteRuleSet) UnregisterCallback(element *list.Element[adapter.RuleSet
|
||||
|
||||
func (s *RemoteRuleSet) loadBytes(content []byte) error {
|
||||
var (
|
||||
plainRuleSet option.PlainRuleSet
|
||||
err error
|
||||
ruleSet option.PlainRuleSetCompat
|
||||
err error
|
||||
)
|
||||
switch s.options.Format {
|
||||
case C.RuleSetFormatSource:
|
||||
var compat option.PlainRuleSetCompat
|
||||
compat, err = json.UnmarshalExtended[option.PlainRuleSetCompat](content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
plainRuleSet, err = compat.Upgrade()
|
||||
ruleSet, err = json.UnmarshalExtended[option.PlainRuleSetCompat](content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case C.RuleSetFormatBinary:
|
||||
plainRuleSet, err = srs.Read(bytes.NewReader(content), false)
|
||||
ruleSet, err = srs.Read(bytes.NewReader(content), false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return E.New("unknown rule-set format: ", s.options.Format)
|
||||
}
|
||||
plainRuleSet, err := ruleSet.Upgrade()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rules := make([]adapter.HeadlessRule, len(plainRuleSet.Rules))
|
||||
for i, ruleOptions := range plainRuleSet.Rules {
|
||||
rules[i], err = NewHeadlessRule(s.ctx, ruleOptions)
|
||||
|
||||
@@ -119,18 +119,19 @@ func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (t *Transport) fetchInterface() (*net.Interface, error) {
|
||||
interfaceName := t.interfaceName
|
||||
func (t *Transport) fetchInterface() (*control.Interface, error) {
|
||||
if t.autoInterface {
|
||||
if t.networkManager.InterfaceMonitor() == nil {
|
||||
return nil, E.New("missing monitor for auto DHCP, set route.auto_detect_interface")
|
||||
}
|
||||
interfaceName = t.networkManager.InterfaceMonitor().DefaultInterfaceName(netip.Addr{})
|
||||
defaultInterface := t.networkManager.InterfaceMonitor().DefaultInterface()
|
||||
if defaultInterface == nil {
|
||||
return nil, E.New("missing default interface")
|
||||
}
|
||||
return defaultInterface, nil
|
||||
} else {
|
||||
return t.networkManager.InterfaceFinder().ByName(t.interfaceName)
|
||||
}
|
||||
if interfaceName == "" {
|
||||
return nil, E.New("missing default interface")
|
||||
}
|
||||
return net.InterfaceByName(interfaceName)
|
||||
}
|
||||
|
||||
func (t *Transport) fetchServers() error {
|
||||
@@ -172,7 +173,7 @@ func (t *Transport) interfaceUpdated(int) {
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Transport) fetchServers0(ctx context.Context, iface *net.Interface) error {
|
||||
func (t *Transport) fetchServers0(ctx context.Context, iface *control.Interface) error {
|
||||
var listener net.ListenConfig
|
||||
listener.Control = control.Append(listener.Control, control.BindToInterface(t.networkManager.InterfaceFinder(), iface.Name, iface.Index))
|
||||
listener.Control = control.Append(listener.Control, control.ReuseAddr())
|
||||
@@ -206,7 +207,7 @@ func (t *Transport) fetchServers0(ctx context.Context, iface *net.Interface) err
|
||||
return group.Run(ctx)
|
||||
}
|
||||
|
||||
func (t *Transport) fetchServersResponse(iface *net.Interface, packetConn net.PacketConn, transactionID dhcpv4.TransactionID) error {
|
||||
func (t *Transport) fetchServersResponse(iface *control.Interface, packetConn net.PacketConn, transactionID dhcpv4.TransactionID) error {
|
||||
buffer := buf.NewSize(dhcpv4.MaxMessageSize)
|
||||
defer buffer.Release()
|
||||
|
||||
@@ -246,7 +247,7 @@ func (t *Transport) fetchServersResponse(iface *net.Interface, packetConn net.Pa
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Transport) recreateServers(iface *net.Interface, serverAddrs []netip.Addr) error {
|
||||
func (t *Transport) recreateServers(iface *control.Interface, serverAddrs []netip.Addr) error {
|
||||
if len(serverAddrs) > 0 {
|
||||
t.options.Logger.Info("dhcp: updated DNS servers from ", iface.Name, ": [", strings.Join(common.Map(serverAddrs, func(it netip.Addr) string {
|
||||
return it.String()
|
||||
|
||||
Reference in New Issue
Block a user