From cd5e7055d2265d43685ac29c80b357b60687e15e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Mon, 15 Aug 2022 11:40:49 +0800 Subject: [PATCH] Add android package rules support in tun routing --- adapter/router.go | 1 + common/process/searcher.go | 7 ++ common/process/searcher_android.go | 147 ++--------------------------- common/process/searcher_darwin.go | 3 +- common/process/searcher_linux.go | 4 +- common/process/searcher_stub.go | 4 +- common/process/searcher_windows.go | 3 +- docs/changelog.md | 2 +- docs/configuration/inbound/tun.md | 50 ++++++---- go.mod | 2 +- go.sum | 4 +- inbound/tun.go | 7 +- option/tun.go | 2 + route/router.go | 40 ++++++-- test/go.mod | 4 +- test/go.sum | 8 +- 16 files changed, 100 insertions(+), 188 deletions(-) diff --git a/adapter/router.go b/adapter/router.go index 91cf7023..4e32513d 100644 --- a/adapter/router.go +++ b/adapter/router.go @@ -37,6 +37,7 @@ type Router interface { DefaultMark() int NetworkMonitor() tun.NetworkUpdateMonitor InterfaceMonitor() tun.DefaultInterfaceMonitor + PackageManager() tun.PackageManager Rules() []Rule SetTrafficController(controller TrafficController) } diff --git a/common/process/searcher.go b/common/process/searcher.go index cdecd333..fa93807f 100644 --- a/common/process/searcher.go +++ b/common/process/searcher.go @@ -4,6 +4,8 @@ import ( "context" "net/netip" + "github.com/sagernet/sing-box/log" + "github.com/sagernet/sing-tun" E "github.com/sagernet/sing/common/exceptions" ) @@ -13,6 +15,11 @@ type Searcher interface { var ErrNotFound = E.New("process not found") +type Config struct { + Logger log.ContextLogger + PackageManager tun.PackageManager +} + type Info struct { ProcessPath string PackageName string diff --git a/common/process/searcher_android.go b/common/process/searcher_android.go index 5e070209..444084e1 100644 --- a/common/process/searcher_android.go +++ b/common/process/searcher_android.go @@ -2,81 +2,19 @@ package process import ( "context" - "encoding/xml" - "io" "net/netip" - "os" - "strconv" - "github.com/sagernet/sing-box/log" - "github.com/sagernet/sing/common" - E "github.com/sagernet/sing/common/exceptions" - - "github.com/fsnotify/fsnotify" + tun "github.com/sagernet/sing-tun" ) var _ Searcher = (*androidSearcher)(nil) type androidSearcher struct { - logger log.ContextLogger - watcher *fsnotify.Watcher - userMap map[string]int32 - packageMap map[int32]string - sharedUserMap map[int32]string + packageManager tun.PackageManager } -func NewSearcher(logger log.ContextLogger) (Searcher, error) { - return &androidSearcher{logger: logger}, nil -} - -func (s *androidSearcher) Start() error { - err := s.updatePackages() - if err != nil { - return E.Cause(err, "read packages list") - } - err = s.startWatcher() - if err != nil { - s.logger.Warn("create fsnotify watcher: ", err) - } - return nil -} - -func (s *androidSearcher) startWatcher() error { - watcher, err := fsnotify.NewWatcher() - if err != nil { - return err - } - err = watcher.Add("/data/system/packages.xml") - if err != nil { - return err - } - s.watcher = watcher - go s.loopUpdate() - return nil -} - -func (s *androidSearcher) loopUpdate() { - for { - select { - case _, ok := <-s.watcher.Events: - if !ok { - return - } - err := s.updatePackages() - if err != nil { - s.logger.Error(E.Cause(err, "update packages list")) - } - case err, ok := <-s.watcher.Errors: - if !ok { - return - } - s.logger.Error(E.Cause(err, "fsnotify error")) - } - } -} - -func (s *androidSearcher) Close() error { - return common.Close(common.PtrOrNil(s.watcher)) +func NewSearcher(config Config) (Searcher, error) { + return &androidSearcher{config.PackageManager}, nil } func (s *androidSearcher) FindProcessInfo(ctx context.Context, network string, srcIP netip.Addr, srcPort int) (*Info, error) { @@ -84,13 +22,13 @@ func (s *androidSearcher) FindProcessInfo(ctx context.Context, network string, s if err != nil { return nil, err } - if sharedUser, loaded := s.sharedUserMap[uid]; loaded { + if sharedPackage, loaded := s.packageManager.SharedPackageByID(uint32(uid)); loaded { return &Info{ UserId: uid, - PackageName: sharedUser, + PackageName: sharedPackage, }, nil } - if packageName, loaded := s.packageMap[uid]; loaded { + if packageName, loaded := s.packageManager.PackageByID(uint32(uid)); loaded { return &Info{ UserId: uid, PackageName: packageName, @@ -98,74 +36,3 @@ func (s *androidSearcher) FindProcessInfo(ctx context.Context, network string, s } return &Info{UserId: uid}, nil } - -func (s *androidSearcher) updatePackages() error { - userMap := make(map[string]int32) - packageMap := make(map[int32]string) - sharedUserMap := make(map[int32]string) - packagesData, err := os.Open("/data/system/packages.xml") - if err != nil { - return err - } - decoder := xml.NewDecoder(packagesData) - var token xml.Token - for { - token, err = decoder.Token() - if err == io.EOF { - break - } else if err != nil { - return err - } - - element, isStart := token.(xml.StartElement) - if !isStart { - continue - } - - switch element.Name.Local { - case "package": - var name string - var userID int64 - for _, attr := range element.Attr { - switch attr.Name.Local { - case "name": - name = attr.Value - case "userId", "sharedUserId": - userID, err = strconv.ParseInt(attr.Value, 10, 32) - if err != nil { - return err - } - } - } - if userID == 0 && name == "" { - continue - } - userMap[name] = int32(userID) - packageMap[int32(userID)] = name - case "shared-user": - var name string - var userID int64 - for _, attr := range element.Attr { - switch attr.Name.Local { - case "name": - name = attr.Value - case "userId": - userID, err = strconv.ParseInt(attr.Value, 10, 32) - if err != nil { - return err - } - packageMap[int32(userID)] = name - } - } - if userID == 0 && name == "" { - continue - } - sharedUserMap[int32(userID)] = name - } - } - s.logger.Info("updated packages list: ", len(packageMap), " packages, ", len(sharedUserMap), " shared users") - s.userMap = userMap - s.packageMap = packageMap - s.sharedUserMap = sharedUserMap - return nil -} diff --git a/common/process/searcher_darwin.go b/common/process/searcher_darwin.go index d8b85ae7..2547ba89 100644 --- a/common/process/searcher_darwin.go +++ b/common/process/searcher_darwin.go @@ -8,7 +8,6 @@ import ( "syscall" "unsafe" - "github.com/sagernet/sing-box/log" N "github.com/sagernet/sing/common/network" "golang.org/x/sys/unix" @@ -18,7 +17,7 @@ var _ Searcher = (*darwinSearcher)(nil) type darwinSearcher struct{} -func NewSearcher(logger log.ContextLogger) (Searcher, error) { +func NewSearcher(_ Config) (Searcher, error) { return &darwinSearcher{}, nil } diff --git a/common/process/searcher_linux.go b/common/process/searcher_linux.go index 64295cb4..f9bed9c8 100644 --- a/common/process/searcher_linux.go +++ b/common/process/searcher_linux.go @@ -15,8 +15,8 @@ type linuxSearcher struct { logger log.ContextLogger } -func NewSearcher(logger log.ContextLogger) (Searcher, error) { - return &linuxSearcher{logger}, nil +func NewSearcher(config Config) (Searcher, error) { + return &linuxSearcher{config.Logger}, nil } func (s *linuxSearcher) FindProcessInfo(ctx context.Context, network string, srcIP netip.Addr, srcPort int) (*Info, error) { diff --git a/common/process/searcher_stub.go b/common/process/searcher_stub.go index ff128517..4665d91f 100644 --- a/common/process/searcher_stub.go +++ b/common/process/searcher_stub.go @@ -4,10 +4,8 @@ package process import ( "os" - - "github.com/sagernet/sing-box/log" ) -func NewSearcher(logger log.ContextLogger) (Searcher, error) { +func NewSearcher(_ Config) (Searcher, error) { return nil, os.ErrInvalid } diff --git a/common/process/searcher_windows.go b/common/process/searcher_windows.go index 118578ba..f76c1105 100644 --- a/common/process/searcher_windows.go +++ b/common/process/searcher_windows.go @@ -8,7 +8,6 @@ import ( "syscall" "unsafe" - "github.com/sagernet/sing-box/log" E "github.com/sagernet/sing/common/exceptions" N "github.com/sagernet/sing/common/network" @@ -19,7 +18,7 @@ var _ Searcher = (*windowsSearcher)(nil) type windowsSearcher struct{} -func NewSearcher(logger log.ContextLogger) (Searcher, error) { +func NewSearcher(_ Config) (Searcher, error) { err := initWin32API() if err != nil { return nil, E.Cause(err, "init win32 api") diff --git a/docs/changelog.md b/docs/changelog.md index 5773ae92..75177ac0 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ #### 2022/08/15 -* Add uid and android user rules support in [Tun](/configuration/inbound/tun) routing. +* Add uid, android user and package rules support in [Tun](/configuration/inbound/tun) routing. #### 2022/08/13 diff --git a/docs/configuration/inbound/tun.md b/docs/configuration/inbound/tun.md index 68766d78..6bcbf645 100644 --- a/docs/configuration/inbound/tun.md +++ b/docs/configuration/inbound/tun.md @@ -28,10 +28,6 @@ 99999 ] ], - "include_android_user": [ - 0, - 10 - ], "exclude_uid": [ 1000 ], @@ -39,6 +35,16 @@ 1000, 99999 ], + "include_android_user": [ + 0, + 10 + ], + "include_package": [ + "com.android.chrome" + ], + "exclude_package": [ + "com.android.captiveportallogin" + ], "sniff": true, "sniff_override_destination": false, @@ -111,7 +117,7 @@ TCP/IP stack. !!! error "" - UID and android user rules are only supported on Linux and require auto_route. + UID rules are only supported on Linux and require auto_route. Limit users in route. Not limited by default. @@ -119,19 +125,6 @@ Limit users in route. Not limited by default. Limit users in route, but in range. -#### include_android_user - -!!! warning "" - - Only supported on Android - -Limit android users in route. - -| Common user | ID | -|--------------|-----| -| Main | 0 | -| Work Profile | 10 | - #### exclude_uid Exclude users in route. @@ -140,6 +133,27 @@ Exclude users in route. Exclude users in route, but in range. +#### include_android_user + +!!! error "" + + Android user and package rules are only supported on Android and require auto_route. + +Limit android users in route. + +| Common user | ID | +|--------------|-----| +| Main | 0 | +| Work Profile | 10 | + +#### include_package + +Limit android packages in route. + +#### exclude_package + +Exclude android packages in route. + ### Listen Fields #### sniff diff --git a/go.mod b/go.mod index 2282e3fb..3b6efc6d 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/sagernet/sing v0.0.0-20220814164830-4f2b872a8cbf github.com/sagernet/sing-dns v0.0.0-20220813025814-e656c9dbf3ae github.com/sagernet/sing-shadowsocks v0.0.0-20220812082714-484a11603b48 - github.com/sagernet/sing-tun v0.0.0-20220815014658-b828f0164333 + github.com/sagernet/sing-tun v0.0.0-20220815033412-1407eae46bd7 github.com/sagernet/sing-vmess v0.0.0-20220811135656-4f3f07acf9c4 github.com/sagernet/smux v0.0.0-20220812084127-e2d085ee3939 github.com/spf13/cobra v1.5.0 diff --git a/go.sum b/go.sum index 2c2bfd23..53c01e91 100644 --- a/go.sum +++ b/go.sum @@ -159,8 +159,8 @@ github.com/sagernet/sing-dns v0.0.0-20220813025814-e656c9dbf3ae h1:xOpbvgizvIbKK github.com/sagernet/sing-dns v0.0.0-20220813025814-e656c9dbf3ae/go.mod h1:T77zZdE2Cm6VqnFumrpwsq+kxYsbq+vWDhmjtdSl/oM= github.com/sagernet/sing-shadowsocks v0.0.0-20220812082714-484a11603b48 h1:NlcTFKldteZvYBDyr+V9MjZEI0rAWCSFCyLgPvc5n/Y= github.com/sagernet/sing-shadowsocks v0.0.0-20220812082714-484a11603b48/go.mod h1:EX3RbZvrwAkPI2nuGa78T2iQXmrkT+/VQtskjou42xM= -github.com/sagernet/sing-tun v0.0.0-20220815014658-b828f0164333 h1:fJj7jCPkGkbhY/UNwebi7kKq8Yxc6qeD3Jzh9Wk9tPw= -github.com/sagernet/sing-tun v0.0.0-20220815014658-b828f0164333/go.mod h1:+JztVFWrBR8bbf1fWPCyc4KJ/a1bPejmmoEBj9rI6HQ= +github.com/sagernet/sing-tun v0.0.0-20220815033412-1407eae46bd7 h1:KwUTQUPvdcJtrZR3WImygB0fINaGIr4X42TnDIDJ9sU= +github.com/sagernet/sing-tun v0.0.0-20220815033412-1407eae46bd7/go.mod h1:+mJ/s6hO3CZyD7CpHbEuZmIVyRkTYLRl4iTr5a57mG0= github.com/sagernet/sing-vmess v0.0.0-20220811135656-4f3f07acf9c4 h1:2hLETh97+S4WnfMR27XyC7QVU1SH7FTNoCznP229YJU= github.com/sagernet/sing-vmess v0.0.0-20220811135656-4f3f07acf9c4/go.mod h1:82O6gzbxLha/W/jxSVQbsqf2lVdRTjMIgyLug0lpJps= github.com/sagernet/smux v0.0.0-20220812084127-e2d085ee3939 h1:pB1Dh1NbwVrLhQhotr4O4Hs3yhiBzmg3AvnUyYjL4x4= diff --git a/inbound/tun.go b/inbound/tun.go index bdddd9b3..70dfb2c8 100644 --- a/inbound/tun.go +++ b/inbound/tun.go @@ -81,8 +81,10 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger Inet6Address: options.Inet6Address.Build(), AutoRoute: options.AutoRoute, IncludeUID: includeUID, - IncludeAndroidUser: options.IncludeAndroidUser, ExcludeUID: excludeUID, + IncludeAndroidUser: options.IncludeAndroidUser, + IncludePackage: options.IncludePackage, + ExcludePackage: options.ExcludePackage, }, endpointIndependentNat: options.EndpointIndependentNat, udpTimeout: udpTimeout, @@ -131,6 +133,9 @@ func (t *Tun) Tag() string { } func (t *Tun) Start() error { + if C.IsAndroid { + t.tunOptions.BuildAndroidRules(t.router.PackageManager(), t) + } tunIf, err := tun.Open(t.tunOptions) if err != nil { return E.Cause(err, "configure tun interface") diff --git a/option/tun.go b/option/tun.go index 9267ec36..9bf65b7c 100644 --- a/option/tun.go +++ b/option/tun.go @@ -11,6 +11,8 @@ type TunInboundOptions struct { ExcludeUID Listable[uint32] `json:"exclude_uid,omitempty"` ExcludeUIDRange Listable[string] `json:"exclude_uid_range,omitempty"` IncludeAndroidUser Listable[int] `json:"android_user,omitempty"` + IncludePackage Listable[string] `json:"include_package,omitempty"` + ExcludePackage Listable[string] `json:"exclude_package,omitempty"` EndpointIndependentNat bool `json:"endpoint_independent_nat,omitempty"` UDPTimeout int64 `json:"udp_timeout,omitempty"` Stack string `json:"stack,omitempty"` diff --git a/route/router.go b/route/router.go index 5edebc31..35ca3bd1 100644 --- a/route/router.go +++ b/route/router.go @@ -90,6 +90,7 @@ type Router struct { defaultMark int networkMonitor tun.NetworkUpdateMonitor interfaceMonitor tun.DefaultInterfaceMonitor + packageManager tun.PackageManager trafficController adapter.TrafficController processSearcher process.Searcher } @@ -260,8 +261,22 @@ func NewRouter(ctx context.Context, logger log.ContextLogger, dnsLogger log.Cont router.interfaceMonitor = interfaceMonitor } - if hasRule(options.Rules, isProcessRule) || hasDNSRule(dnsOptions.Rules, isProcessDNSRule) || options.FindProcess { - searcher, err := process.NewSearcher(logger) + needFindProcess := hasRule(options.Rules, isProcessRule) || hasDNSRule(dnsOptions.Rules, isProcessDNSRule) || options.FindProcess + needPackageManager := C.IsAndroid && (needFindProcess || common.Any(inbounds, func(inbound option.Inbound) bool { + return len(inbound.TunOptions.IncludePackage) > 0 || len(inbound.TunOptions.ExcludePackage) > 0 + })) + if needPackageManager { + packageManager, err := tun.NewPackageManager(router) + if err != nil { + return nil, E.Cause(err, "create package manager") + } + router.packageManager = packageManager + } + if needFindProcess { + searcher, err := process.NewSearcher(process.Config{ + Logger: logger, + PackageManager: router.packageManager, + }) if err != nil { if err != os.ErrInvalid { logger.Warn(E.Cause(err, "create process searcher")) @@ -425,13 +440,10 @@ func (r *Router) Start() error { return err } } - if r.processSearcher != nil { - if starter, isStarter := r.processSearcher.(common.Starter); isStarter { - err := starter.Start() - if err != nil { - r.logger.Error(E.Cause(err, "initialize process searcher")) - r.processSearcher = nil - } + if r.packageManager != nil { + err := r.packageManager.Start() + if err != nil { + return err } } return nil @@ -454,7 +466,7 @@ func (r *Router) Close() error { common.PtrOrNil(r.geoIPReader), r.interfaceMonitor, r.networkMonitor, - r.processSearcher, + r.packageManager, ) } @@ -679,6 +691,10 @@ func (r *Router) InterfaceMonitor() tun.DefaultInterfaceMonitor { return r.interfaceMonitor } +func (r *Router) PackageManager() tun.PackageManager { + return r.packageManager +} + func (r *Router) SetTrafficController(controller adapter.TrafficController) { r.trafficController = controller } @@ -912,6 +928,10 @@ func (r *Router) downloadGeositeDatabase(savePath string) error { return err } +func (r *Router) OnPackagesUpdated(packages int, sharedUsers int) { + r.logger.Info("updated packages list: ", packages, " packages, ", sharedUsers, " shared users") +} + func (r *Router) NewError(ctx context.Context, err error) { common.Close(err) if E.IsClosedOrCanceled(err) { diff --git a/test/go.mod b/test/go.mod index 01d7671f..ae056e5c 100644 --- a/test/go.mod +++ b/test/go.mod @@ -14,7 +14,7 @@ require ( github.com/sagernet/sing-shadowsocks v0.0.0-20220812082714-484a11603b48 github.com/spyzhov/ajson v0.7.1 github.com/stretchr/testify v1.8.0 - golang.org/x/net v0.0.0-20220811182439-13a9a731de15 + golang.org/x/net v0.0.0-20220812174116-3211cb980234 ) require ( @@ -54,7 +54,7 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/sagernet/netlink v0.0.0-20220803045538-bdac49abf805 // indirect github.com/sagernet/sing-dns v0.0.0-20220813025814-e656c9dbf3ae // indirect - github.com/sagernet/sing-tun v0.0.0-20220815014658-b828f0164333 // indirect + github.com/sagernet/sing-tun v0.0.0-20220815033412-1407eae46bd7 // indirect github.com/sagernet/sing-vmess v0.0.0-20220811135656-4f3f07acf9c4 // indirect github.com/sagernet/smux v0.0.0-20220812084127-e2d085ee3939 // indirect github.com/sirupsen/logrus v1.8.1 // indirect diff --git a/test/go.sum b/test/go.sum index e5f909e1..109b6aff 100644 --- a/test/go.sum +++ b/test/go.sum @@ -181,8 +181,8 @@ github.com/sagernet/sing-dns v0.0.0-20220813025814-e656c9dbf3ae h1:xOpbvgizvIbKK github.com/sagernet/sing-dns v0.0.0-20220813025814-e656c9dbf3ae/go.mod h1:T77zZdE2Cm6VqnFumrpwsq+kxYsbq+vWDhmjtdSl/oM= github.com/sagernet/sing-shadowsocks v0.0.0-20220812082714-484a11603b48 h1:NlcTFKldteZvYBDyr+V9MjZEI0rAWCSFCyLgPvc5n/Y= github.com/sagernet/sing-shadowsocks v0.0.0-20220812082714-484a11603b48/go.mod h1:EX3RbZvrwAkPI2nuGa78T2iQXmrkT+/VQtskjou42xM= -github.com/sagernet/sing-tun v0.0.0-20220815014658-b828f0164333 h1:fJj7jCPkGkbhY/UNwebi7kKq8Yxc6qeD3Jzh9Wk9tPw= -github.com/sagernet/sing-tun v0.0.0-20220815014658-b828f0164333/go.mod h1:+JztVFWrBR8bbf1fWPCyc4KJ/a1bPejmmoEBj9rI6HQ= +github.com/sagernet/sing-tun v0.0.0-20220815033412-1407eae46bd7 h1:KwUTQUPvdcJtrZR3WImygB0fINaGIr4X42TnDIDJ9sU= +github.com/sagernet/sing-tun v0.0.0-20220815033412-1407eae46bd7/go.mod h1:+mJ/s6hO3CZyD7CpHbEuZmIVyRkTYLRl4iTr5a57mG0= github.com/sagernet/sing-vmess v0.0.0-20220811135656-4f3f07acf9c4 h1:2hLETh97+S4WnfMR27XyC7QVU1SH7FTNoCznP229YJU= github.com/sagernet/sing-vmess v0.0.0-20220811135656-4f3f07acf9c4/go.mod h1:82O6gzbxLha/W/jxSVQbsqf2lVdRTjMIgyLug0lpJps= github.com/sagernet/smux v0.0.0-20220812084127-e2d085ee3939 h1:pB1Dh1NbwVrLhQhotr4O4Hs3yhiBzmg3AvnUyYjL4x4= @@ -275,8 +275,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220811182439-13a9a731de15 h1:cik0bxZUSJVDyaHf1hZPSDsU8SZHGQZQMeueXCE7yBQ= -golang.org/x/net v0.0.0-20220811182439-13a9a731de15/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20220812174116-3211cb980234 h1:RDqmgfe7SvlMWoqC3xwQ2blLO3fcWcxMa3eBLRdRW7E= +golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=