Add wifi_ssid and wifi_bssid route and DNS rules

This commit is contained in:
世界 2023-11-18 13:51:32 +08:00
parent 96604c5681
commit c39b52cd80
No known key found for this signature in database
GPG Key ID: CD109927C34A63C4
17 changed files with 172 additions and 6 deletions

View File

@ -41,6 +41,7 @@ type Router interface {
NetworkMonitor() tun.NetworkUpdateMonitor NetworkMonitor() tun.NetworkUpdateMonitor
InterfaceMonitor() tun.DefaultInterfaceMonitor InterfaceMonitor() tun.DefaultInterfaceMonitor
PackageManager() tun.PackageManager PackageManager() tun.PackageManager
WIFIState() WIFIState
Rules() []Rule Rules() []Rule
ClashServer() ClashServer ClashServer() ClashServer
@ -78,3 +79,8 @@ type DNSRule interface {
type InterfaceUpdateListener interface { type InterfaceUpdateListener interface {
InterfaceUpdated() InterfaceUpdated()
} }
type WIFIState struct {
SSID string
BSSID string
}

View File

@ -1,6 +1,7 @@
package cachefile package cachefile
import ( import (
"context"
"errors" "errors"
"net/netip" "net/netip"
"os" "os"
@ -13,6 +14,7 @@ import (
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/service/filemanager"
) )
var ( var (
@ -41,7 +43,7 @@ type CacheFile struct {
saveMetadataTimer *time.Timer saveMetadataTimer *time.Timer
} }
func Open(path string, cacheID string) (*CacheFile, error) { func Open(ctx context.Context, path string, cacheID string) (*CacheFile, error) {
const fileMode = 0o666 const fileMode = 0o666
options := bbolt.Options{Timeout: time.Second} options := bbolt.Options{Timeout: time.Second}
var ( var (
@ -67,6 +69,10 @@ func Open(path string, cacheID string) (*CacheFile, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = filemanager.Chown(ctx, path)
if err != nil {
return nil, E.Cause(err, "platform chown")
}
var cacheIDBytes []byte var cacheIDBytes []byte
if cacheID != "" { if cacheID != "" {
cacheIDBytes = append([]byte{0}, []byte(cacheID)...) cacheIDBytes = append([]byte{0}, []byte(cacheID)...)

View File

@ -148,7 +148,7 @@ func NewServer(ctx context.Context, router adapter.Router, logFactory log.Observ
func (s *Server) PreStart() error { func (s *Server) PreStart() error {
if s.cacheFilePath != "" { if s.cacheFilePath != "" {
cacheFile, err := cachefile.Open(s.cacheFilePath, s.cacheID) cacheFile, err := cachefile.Open(s.ctx, s.cacheFilePath, s.cacheID)
if err != nil { if err != nil {
return E.Cause(err, "open cache file") return E.Cause(err, "open cache file")
} }

View File

@ -60,11 +60,11 @@ func (s *CommandServer) handleLogConn(conn net.Conn) error {
for element := s.savedLines.Front(); element != nil; element = element.Next() { for element := s.savedLines.Front(); element != nil; element = element.Next() {
savedLines = append(savedLines, element.Value) savedLines = append(savedLines, element.Value)
} }
s.access.Unlock()
subscription, done, err := s.observer.Subscribe() subscription, done, err := s.observer.Subscribe()
if err != nil { if err != nil {
return err return err
} }
s.access.Unlock()
defer s.observer.UnSubscribe(subscription) defer s.observer.UnSubscribe(subscription)
for _, line := range savedLines { for _, line := range savedLines {
err = writeLog(conn, []byte(line)) err = writeLog(conn, []byte(line))

View File

@ -19,6 +19,7 @@ type PlatformInterface interface {
UsePlatformInterfaceGetter() bool UsePlatformInterfaceGetter() bool
GetInterfaces() (NetworkInterfaceIterator, error) GetInterfaces() (NetworkInterfaceIterator, error)
UnderNetworkExtension() bool UnderNetworkExtension() bool
ReadWIFIState() *WIFIState
ClearDNSCache() ClearDNSCache()
} }
@ -38,6 +39,15 @@ type NetworkInterface struct {
Addresses StringIterator Addresses StringIterator
} }
type WIFIState struct {
SSID string
BSSID string
}
func NewWIFIState(wifiSSID string, wifiBSSID string) *WIFIState {
return &WIFIState{wifiSSID, wifiBSSID}
}
type NetworkInterfaceIterator interface { type NetworkInterfaceIterator interface {
Next() *NetworkInterface Next() *NetworkInterface
HasNext() bool HasNext() bool

View File

@ -23,6 +23,7 @@ type Interface interface {
Interfaces() ([]NetworkInterface, error) Interfaces() ([]NetworkInterface, error)
UnderNetworkExtension() bool UnderNetworkExtension() bool
ClearDNSCache() ClearDNSCache()
ReadWIFIState() adapter.WIFIState
process.Searcher process.Searcher
} }

View File

@ -210,6 +210,14 @@ func (w *platformInterfaceWrapper) ClearDNSCache() {
w.iif.ClearDNSCache() w.iif.ClearDNSCache()
} }
func (w *platformInterfaceWrapper) ReadWIFIState() adapter.WIFIState {
wifiState := w.iif.ReadWIFIState()
if wifiState == nil {
return adapter.WIFIState{}
}
return (adapter.WIFIState)(*wifiState)
}
func (w *platformInterfaceWrapper) DisableColors() bool { func (w *platformInterfaceWrapper) DisableColors() bool {
return runtime.GOOS != "android" return runtime.GOOS != "android"
} }

2
go.mod
View File

@ -26,7 +26,7 @@ require (
github.com/sagernet/gvisor v0.0.0-20230930141345-5fef6f2e17ab github.com/sagernet/gvisor v0.0.0-20230930141345-5fef6f2e17ab
github.com/sagernet/quic-go v0.40.0 github.com/sagernet/quic-go v0.40.0
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
github.com/sagernet/sing v0.2.18-0.20231108041402-4fbbd193203c github.com/sagernet/sing v0.2.18-0.20231117150934-256fafcd99b6
github.com/sagernet/sing-dns v0.1.11-0.20231116102430-5a2133f5d358 github.com/sagernet/sing-dns v0.1.11-0.20231116102430-5a2133f5d358
github.com/sagernet/sing-mux v0.1.5-0.20231109075101-6b086ed6bb07 github.com/sagernet/sing-mux v0.1.5-0.20231109075101-6b086ed6bb07
github.com/sagernet/sing-quic v0.1.4-0.20231114135334-e2a6aab55cca github.com/sagernet/sing-quic v0.1.4-0.20231114135334-e2a6aab55cca

4
go.sum
View File

@ -110,8 +110,8 @@ github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byL
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU= github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk= github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
github.com/sagernet/sing v0.2.18-0.20231108041402-4fbbd193203c h1:uask61Pxc3nGqsOSjqnBKrwfODWRoEa80lXm04LNk0E= github.com/sagernet/sing v0.2.18-0.20231117150934-256fafcd99b6 h1:v6QQn8FPEwF2Wi4jQ0xWg7+NUWMIsnP8uoAG7lua1Qo=
github.com/sagernet/sing v0.2.18-0.20231108041402-4fbbd193203c/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo= github.com/sagernet/sing v0.2.18-0.20231117150934-256fafcd99b6/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
github.com/sagernet/sing-dns v0.1.11-0.20231116102430-5a2133f5d358 h1:psJQg/KXVQTupFFR1liaCAucW+NwCDhe1oYfkmz8EJ8= github.com/sagernet/sing-dns v0.1.11-0.20231116102430-5a2133f5d358 h1:psJQg/KXVQTupFFR1liaCAucW+NwCDhe1oYfkmz8EJ8=
github.com/sagernet/sing-dns v0.1.11-0.20231116102430-5a2133f5d358/go.mod h1:vtUimtf7Nq9EdvD5WTpfCr69KL1M7bcgOVKiYBiAY/c= github.com/sagernet/sing-dns v0.1.11-0.20231116102430-5a2133f5d358/go.mod h1:vtUimtf7Nq9EdvD5WTpfCr69KL1M7bcgOVKiYBiAY/c=
github.com/sagernet/sing-mux v0.1.5-0.20231109075101-6b086ed6bb07 h1:ncKb5tVOsCQgCsv6UpsA0jinbNb5OQ5GMPJlyQP3EHM= github.com/sagernet/sing-mux v0.1.5-0.20231109075101-6b086ed6bb07 h1:ncKb5tVOsCQgCsv6UpsA0jinbNb5OQ5GMPJlyQP3EHM=

View File

@ -78,6 +78,8 @@ type DefaultRule struct {
User Listable[string] `json:"user,omitempty"` User Listable[string] `json:"user,omitempty"`
UserID Listable[int32] `json:"user_id,omitempty"` UserID Listable[int32] `json:"user_id,omitempty"`
ClashMode string `json:"clash_mode,omitempty"` ClashMode string `json:"clash_mode,omitempty"`
WIFISSID Listable[string] `json:"wifi_ssid,omitempty"`
WIFIBSSID Listable[string] `json:"wifi_bssid,omitempty"`
Invert bool `json:"invert,omitempty"` Invert bool `json:"invert,omitempty"`
Outbound string `json:"outbound,omitempty"` Outbound string `json:"outbound,omitempty"`
} }

View File

@ -78,6 +78,8 @@ type DefaultDNSRule struct {
UserID Listable[int32] `json:"user_id,omitempty"` UserID Listable[int32] `json:"user_id,omitempty"`
Outbound Listable[string] `json:"outbound,omitempty"` Outbound Listable[string] `json:"outbound,omitempty"`
ClashMode string `json:"clash_mode,omitempty"` ClashMode string `json:"clash_mode,omitempty"`
WIFISSID Listable[string] `json:"wifi_ssid,omitempty"`
WIFIBSSID Listable[string] `json:"wifi_bssid,omitempty"`
Invert bool `json:"invert,omitempty"` Invert bool `json:"invert,omitempty"`
Server string `json:"server,omitempty"` Server string `json:"server,omitempty"`
DisableCache bool `json:"disable_cache,omitempty"` DisableCache bool `json:"disable_cache,omitempty"`

View File

@ -86,6 +86,8 @@ type Router struct {
clashServer adapter.ClashServer clashServer adapter.ClashServer
v2rayServer adapter.V2RayServer v2rayServer adapter.V2RayServer
platformInterface platform.Interface platformInterface platform.Interface
needWIFIState bool
wifiState adapter.WIFIState
} }
func NewRouter( func NewRouter(
@ -116,6 +118,7 @@ func NewRouter(
defaultMark: options.DefaultMark, defaultMark: options.DefaultMark,
pauseManager: pause.ManagerFromContext(ctx), pauseManager: pause.ManagerFromContext(ctx),
platformInterface: platformInterface, platformInterface: platformInterface,
needWIFIState: true, // hasRule(options.Rules, isWIFIRule) || hasDNSRule(dnsOptions.Rules, isWIFIDNSRule),
} }
router.dnsClient = dns.NewClient(dns.ClientOptions{ router.dnsClient = dns.NewClient(dns.ClientOptions{
DisableCache: dnsOptions.DNSClientOptions.DisableCache, DisableCache: dnsOptions.DNSClientOptions.DisableCache,
@ -328,6 +331,9 @@ func NewRouter(
service.ContextWith[serviceNTP.TimeService](ctx, timeService) service.ContextWith[serviceNTP.TimeService](ctx, timeService)
router.timeService = timeService router.timeService = timeService
} }
if platformInterface != nil && router.interfaceMonitor != nil && router.needWIFIState {
router.interfaceMonitor.RegisterCallback(router.updateWIFIState)
}
return router, nil return router, nil
} }
@ -468,6 +474,9 @@ func (r *Router) Start() error {
r.geositeCache = nil r.geositeCache = nil
r.geositeReader = nil r.geositeReader = nil
} }
if r.needWIFIState {
r.updateWIFIState(0)
}
for i, rule := range r.rules { for i, rule := range r.rules {
err := rule.Start() err := rule.Start()
if err != nil { if err != nil {
@ -940,6 +949,10 @@ func (r *Router) Rules() []adapter.Rule {
return r.rules return r.rules
} }
func (r *Router) WIFIState() adapter.WIFIState {
return r.wifiState
}
func (r *Router) NetworkMonitor() tun.NetworkUpdateMonitor { func (r *Router) NetworkMonitor() tun.NetworkUpdateMonitor {
return r.networkMonitor return r.networkMonitor
} }
@ -1019,3 +1032,15 @@ func (r *Router) ResetNetwork() error {
} }
return nil return nil
} }
func (r *Router) updateWIFIState(_ int) {
state /*, err */ := r.platformInterface.ReadWIFIState()
/*if err != nil {
r.logger.Error("read WIFI state: ", err)
return
}*/
if state != r.wifiState {
r.wifiState = state
r.logger.Info("updated WIFI state: SSID=", state.SSID, ", BSSID=", state.BSSID)
}
}

View File

@ -307,3 +307,11 @@ func isProcessDNSRule(rule option.DefaultDNSRule) bool {
func notPrivateNode(code string) bool { func notPrivateNode(code string) bool {
return code != "private" return code != "private"
} }
func isWIFIRule(rule option.DefaultRule) bool {
return len(rule.WIFISSID) > 0 || len(rule.WIFIBSSID) > 0
}
func isWIFIDNSRule(rule option.DefaultDNSRule) bool {
return len(rule.WIFISSID) > 0 || len(rule.WIFIBSSID) > 0
}

View File

@ -184,6 +184,16 @@ func NewDefaultRule(router adapter.Router, logger log.ContextLogger, options opt
rule.items = append(rule.items, item) rule.items = append(rule.items, item)
rule.allItems = append(rule.allItems, item) rule.allItems = append(rule.allItems, item)
} }
if len(options.WIFISSID) > 0 {
item := NewWIFISSIDItem(router, options.WIFISSID)
rule.items = append(rule.items, item)
rule.allItems = append(rule.allItems, item)
}
if len(options.WIFIBSSID) > 0 {
item := NewWIFIBSSIDItem(router, options.WIFIBSSID)
rule.items = append(rule.items, item)
rule.allItems = append(rule.allItems, item)
}
return rule, nil return rule, nil
} }

View File

@ -180,6 +180,16 @@ func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options
rule.items = append(rule.items, item) rule.items = append(rule.items, item)
rule.allItems = append(rule.allItems, item) rule.allItems = append(rule.allItems, item)
} }
if len(options.WIFISSID) > 0 {
item := NewWIFISSIDItem(router, options.WIFISSID)
rule.items = append(rule.items, item)
rule.allItems = append(rule.allItems, item)
}
if len(options.WIFIBSSID) > 0 {
item := NewWIFIBSSIDItem(router, options.WIFIBSSID)
rule.items = append(rule.items, item)
rule.allItems = append(rule.allItems, item)
}
return rule, nil return rule, nil
} }

View File

@ -0,0 +1,39 @@
package route
import (
"strings"
"github.com/sagernet/sing-box/adapter"
F "github.com/sagernet/sing/common/format"
)
var _ RuleItem = (*WIFIBSSIDItem)(nil)
type WIFIBSSIDItem struct {
bssidList []string
bssidMap map[string]bool
router adapter.Router
}
func NewWIFIBSSIDItem(router adapter.Router, bssidList []string) *WIFIBSSIDItem {
bssidMap := make(map[string]bool)
for _, bssid := range bssidList {
bssidMap[bssid] = true
}
return &WIFIBSSIDItem{
bssidList,
bssidMap,
router,
}
}
func (r *WIFIBSSIDItem) Match(metadata *adapter.InboundContext) bool {
return r.bssidMap[r.router.WIFIState().BSSID]
}
func (r *WIFIBSSIDItem) String() string {
if len(r.bssidList) == 1 {
return F.ToString("wifi_bssid=", r.bssidList[0])
}
return F.ToString("wifi_bssid=[", strings.Join(r.bssidList, " "), "]")
}

View File

@ -0,0 +1,39 @@
package route
import (
"strings"
"github.com/sagernet/sing-box/adapter"
F "github.com/sagernet/sing/common/format"
)
var _ RuleItem = (*WIFISSIDItem)(nil)
type WIFISSIDItem struct {
ssidList []string
ssidMap map[string]bool
router adapter.Router
}
func NewWIFISSIDItem(router adapter.Router, ssidList []string) *WIFISSIDItem {
ssidMap := make(map[string]bool)
for _, ssid := range ssidList {
ssidMap[ssid] = true
}
return &WIFISSIDItem{
ssidList,
ssidMap,
router,
}
}
func (r *WIFISSIDItem) Match(metadata *adapter.InboundContext) bool {
return r.ssidMap[r.router.WIFIState().SSID]
}
func (r *WIFISSIDItem) String() string {
if len(r.ssidList) == 1 {
return F.ToString("wifi_ssid=", r.ssidList[0])
}
return F.ToString("wifi_ssid=[", strings.Join(r.ssidList, " "), "]")
}