diff --git a/.golangci.yml b/.golangci.yml index 88628eca..366767d9 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -3,18 +3,15 @@ linters: enable: - gofumpt - govet - - gci +# - gci - staticcheck - paralleltest -issues: - fix: true - linters-settings: - gci: - sections: - - standard - - prefix(github.com/sagernet/) - - default +# gci: +# sections: +# - standard +# - prefix(github.com/sagernet/) +# - default staticcheck: go: '1.19' diff --git a/Makefile b/Makefile index 58598385..d721510d 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ NAME = sing-box COMMIT = $(shell git rev-parse --short HEAD) TAGS ?= with_quic,with_clash_api -PARAMS = -trimpath -tags '$(TAGS)' -ldflags \ +PARAMS = -v -trimpath -tags '$(TAGS)' -ldflags \ '-X "github.com/sagernet/sing-box/constant.Commit=$(COMMIT)" \ -w -s -buildid=' MAIN = ./cmd/sing-box @@ -11,26 +11,17 @@ MAIN = ./cmd/sing-box build: go build $(PARAMS) $(MAIN) -action_version: build - echo "::set-output name=VERSION::`./sing-box version -n`" - install: go install $(PARAMS) $(MAIN) -release: - goreleaser release --snapshot --rm-dist - -fmt_install: - go install -v mvdan.cc/gofumpt@latest - go install -v github.com/daixiang0/gci@v0.4.0 - fmt: @gofumpt -l -w . @gofmt -s -w . @gci write -s "standard,prefix(github.com/sagernet/),default" . -lint_install: - go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest +fmt_install: + go install -v mvdan.cc/gofumpt@latest + go install -v github.com/daixiang0/gci@v0.4.0 lint: GOOS=linux golangci-lint run ./... @@ -38,6 +29,9 @@ lint: GOOS=darwin golangci-lint run ./... GOOS=freebsd golangci-lint run ./... +lint_install: + go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest + test: @go test -v . && \ @pushd test && \ diff --git a/docs/changelog.md b/docs/changelog.md index 0299de61..5773ae92 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,3 +1,11 @@ +#### 2022/08/15 + +* Add uid and android user rules support in [Tun](/configuration/inbound/tun) routing. + +#### 2022/08/13 + +* Fix dns concurrent write + #### 2022/08/12 * Performance improvements diff --git a/docs/configuration/inbound/tun.md b/docs/configuration/inbound/tun.md index 6a040143..68766d78 100644 --- a/docs/configuration/inbound/tun.md +++ b/docs/configuration/inbound/tun.md @@ -19,6 +19,26 @@ "endpoint_independent_nat": false, "udp_timeout": 300, "stack": "gvisor", + "include_uid": [ + 0 + ], + "include_uid_range": [ + [ + 1000, + 99999 + ] + ], + "include_android_user": [ + 0, + 10 + ], + "exclude_uid": [ + 1000 + ], + "exclude_uid_range": [ + 1000, + 99999 + ], "sniff": true, "sniff_override_destination": false, @@ -28,9 +48,13 @@ } ``` +!!! note "" + + You can ignore the JSON Array [] tag when the content is only one item + !!! warning "" - If tun is running in non-privileged mode, the address and MTU will not be configured automatically, please make sure the settings are accurate. + If tun is running in non-privileged mode, addresses and MTU will not be configured automatically, please make sure the settings are accurate. ### Tun Fields @@ -83,6 +107,39 @@ TCP/IP stack. The LWIP stack is not included by default, see [Installation](/#Installation). +#### include_uid + +!!! error "" + + UID and android user rules are only supported on Linux and require auto_route. + +Limit users in route. Not limited by default. + +#### include_uid_range + +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. + +#### exclude_uid_range + +Exclude users in route, but in range. + ### Listen Fields #### sniff diff --git a/go.mod b/go.mod index bcb5dd22..2282e3fb 100644 --- a/go.mod +++ b/go.mod @@ -15,17 +15,17 @@ require ( github.com/logrusorgru/aurora v2.0.3+incompatible github.com/lucas-clemente/quic-go v0.28.1 github.com/oschwald/maxminddb-golang v1.10.0 - github.com/sagernet/sing v0.0.0-20220813024838-eb2fad956aa8 + 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-20220808133432-d378b6ca536d + github.com/sagernet/sing-tun v0.0.0-20220815014658-b828f0164333 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 github.com/stretchr/testify v1.8.0 go.uber.org/atomic v1.10.0 golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa - golang.org/x/net v0.0.0-20220811182439-13a9a731de15 + golang.org/x/net v0.0.0-20220812174116-3211cb980234 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab ) diff --git a/go.sum b/go.sum index cd958b1c..2c2bfd23 100644 --- a/go.sum +++ b/go.sum @@ -153,14 +153,14 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD github.com/sagernet/netlink v0.0.0-20220803045538-bdac49abf805 h1:hE+vtsjBCCPmxkRz9jZA+CicHgVkDT6H+Av5ZzskVxs= github.com/sagernet/netlink v0.0.0-20220803045538-bdac49abf805/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= github.com/sagernet/sing v0.0.0-20220812082120-05f9836bff8f/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= -github.com/sagernet/sing v0.0.0-20220813024838-eb2fad956aa8 h1:zTuJbqzZgBh+iGPV41d8ZarKxXPgTq/+PGk1kMhPV8I= -github.com/sagernet/sing v0.0.0-20220813024838-eb2fad956aa8/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= +github.com/sagernet/sing v0.0.0-20220814164830-4f2b872a8cbf h1:JYnx6N1H2KeFKM7YSH5+qfhvvcy6gpqSpewkp1RLRSg= +github.com/sagernet/sing v0.0.0-20220814164830-4f2b872a8cbf/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= github.com/sagernet/sing-dns v0.0.0-20220813025814-e656c9dbf3ae h1:xOpbvgizvIbKKrrcl/CK3RjGY2u7rC+SBXlgqzEZOU4= 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-20220808133432-d378b6ca536d h1:AQpkoUiF8FxYVI1lf2W9Rbkk914eHjVH9M8y+F/0+Nw= -github.com/sagernet/sing-tun v0.0.0-20220808133432-d378b6ca536d/go.mod h1:gWwYd53AqXl+Y+q6WlXUc6PkqU28sfu5VTQhyeEIFbw= +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-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= @@ -248,8 +248,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= diff --git a/inbound/tun.go b/inbound/tun.go index 3a85222b..bdddd9b3 100644 --- a/inbound/tun.go +++ b/inbound/tun.go @@ -3,7 +3,6 @@ package inbound import ( "context" "net" - "net/netip" "strconv" "strings" "time" @@ -17,9 +16,9 @@ import ( "github.com/sagernet/sing-tun" "github.com/sagernet/sing/common" E "github.com/sagernet/sing/common/exceptions" - F "github.com/sagernet/sing/common/format" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" + "github.com/sagernet/sing/common/ranges" ) var _ adapter.Inbound = (*Tun)(nil) @@ -30,11 +29,7 @@ type Tun struct { router adapter.Router logger log.ContextLogger inboundOptions option.InboundOptions - tunName string - tunMTU uint32 - inet4Address netip.Prefix - inet6Address netip.Prefix - autoRoute bool + tunOptions tun.Options endpointIndependentNat bool udpTimeout int64 stack string @@ -45,7 +40,7 @@ type Tun struct { func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TunInboundOptions) (*Tun, error) { tunName := options.InterfaceName if tunName == "" { - tunName = mkInterfaceName() + tunName = tun.DefaultInterfaceName() } tunMTU := options.MTU if tunMTU == 0 { @@ -57,23 +52,76 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger } else { udpTimeout = int64(C.UDPTimeout.Seconds()) } + includeUID := uidToRange(options.IncludeUID) + if len(options.IncludeUIDRange) > 0 { + var err error + includeUID, err = parseRange(includeUID, options.IncludeUIDRange) + if err != nil { + return nil, E.Cause(err, "parse include_uid_range") + } + } + excludeUID := uidToRange(options.ExcludeUID) + if len(options.ExcludeUIDRange) > 0 { + var err error + excludeUID, err = parseRange(excludeUID, options.ExcludeUIDRange) + if err != nil { + return nil, E.Cause(err, "parse exclude_uid_range") + } + } return &Tun{ - tag: tag, - ctx: ctx, - router: router, - logger: logger, - inboundOptions: options.InboundOptions, - tunName: tunName, - tunMTU: tunMTU, - inet4Address: options.Inet4Address.Build(), - inet6Address: options.Inet6Address.Build(), - autoRoute: options.AutoRoute, + tag: tag, + ctx: ctx, + router: router, + logger: logger, + inboundOptions: options.InboundOptions, + tunOptions: tun.Options{ + Name: tunName, + MTU: tunMTU, + Inet4Address: options.Inet4Address.Build(), + Inet6Address: options.Inet6Address.Build(), + AutoRoute: options.AutoRoute, + IncludeUID: includeUID, + IncludeAndroidUser: options.IncludeAndroidUser, + ExcludeUID: excludeUID, + }, endpointIndependentNat: options.EndpointIndependentNat, udpTimeout: udpTimeout, stack: options.Stack, }, nil } +func uidToRange(uidList option.Listable[uint32]) []ranges.Range[uint32] { + return common.Map(uidList, func(uid uint32) ranges.Range[uint32] { + return ranges.NewSingle(uid) + }) +} + +func parseRange(uidRanges []ranges.Range[uint32], rangeList []string) ([]ranges.Range[uint32], error) { + for _, uidRange := range rangeList { + if !strings.Contains(uidRange, ":") { + return nil, E.New("missing ':' in range: ", uidRange) + } + subIndex := strings.Index(uidRange, ":") + if subIndex == 0 { + return nil, E.New("missing range start: ", uidRange) + } else if subIndex == len(uidRange)-1 { + return nil, E.New("missing range end: ", uidRange) + } + var start, end uint64 + var err error + start, err = strconv.ParseUint(uidRange[:subIndex], 10, 32) + if err != nil { + return nil, E.Cause(err, "parse range start") + } + end, err = strconv.ParseUint(uidRange[subIndex+1:], 10, 32) + if err != nil { + return nil, E.Cause(err, "parse range end") + } + uidRanges = append(uidRanges, ranges.New(uint32(start), uint32(end))) + } + return uidRanges, nil +} + func (t *Tun) Type() string { return C.TypeTun } @@ -83,12 +131,12 @@ func (t *Tun) Tag() string { } func (t *Tun) Start() error { - tunIf, err := tun.Open(t.tunName, t.inet4Address, t.inet6Address, t.tunMTU, t.autoRoute) + tunIf, err := tun.Open(t.tunOptions) if err != nil { return E.Cause(err, "configure tun interface") } t.tunIf = tunIf - t.tunStack, err = tun.NewStack(t.ctx, t.stack, tunIf, t.tunMTU, t.endpointIndependentNat, t.udpTimeout, t) + t.tunStack, err = tun.NewStack(t.ctx, t.stack, tunIf, t.tunOptions.MTU, t.endpointIndependentNat, t.udpTimeout, t) if err != nil { return err } @@ -96,7 +144,7 @@ func (t *Tun) Start() error { if err != nil { return err } - t.logger.Info("started at ", t.tunName) + t.logger.Info("started at ", t.tunOptions.Name) return nil } @@ -153,26 +201,3 @@ func (t *Tun) NewPacketConnection(ctx context.Context, conn N.PacketConn, upstre func (t *Tun) NewError(ctx context.Context, err error) { NewError(t.logger, ctx, err) } - -func mkInterfaceName() (tunName string) { - if C.IsDarwin { - tunName = "utun" - } else { - tunName = "tun" - } - interfaces, err := net.Interfaces() - if err != nil { - return - } - var tunIndex int - for _, netInterface := range interfaces { - if strings.HasPrefix(netInterface.Name, tunName) { - index, parseErr := strconv.ParseInt(netInterface.Name[len(tunName):], 10, 16) - if parseErr == nil { - tunIndex = int(index) + 1 - } - } - } - tunName = F.ToString(tunName, tunIndex) - return -} diff --git a/option/tun.go b/option/tun.go index 07ebad09..9267ec36 100644 --- a/option/tun.go +++ b/option/tun.go @@ -1,13 +1,18 @@ package option type TunInboundOptions struct { - InterfaceName string `json:"interface_name,omitempty"` - MTU uint32 `json:"mtu,omitempty"` - Inet4Address *ListenPrefix `json:"inet4_address,omitempty"` - Inet6Address *ListenPrefix `json:"inet6_address,omitempty"` - AutoRoute bool `json:"auto_route,omitempty"` - EndpointIndependentNat bool `json:"endpoint_independent_nat,omitempty"` - UDPTimeout int64 `json:"udp_timeout,omitempty"` - Stack string `json:"stack,omitempty"` + InterfaceName string `json:"interface_name,omitempty"` + MTU uint32 `json:"mtu,omitempty"` + Inet4Address *ListenPrefix `json:"inet4_address,omitempty"` + Inet6Address *ListenPrefix `json:"inet6_address,omitempty"` + AutoRoute bool `json:"auto_route,omitempty"` + IncludeUID Listable[uint32] `json:"include_uid,omitempty"` + IncludeUIDRange Listable[string] `json:"include_uid_range,omitempty"` + ExcludeUID Listable[uint32] `json:"exclude_uid,omitempty"` + ExcludeUIDRange Listable[string] `json:"exclude_uid_range,omitempty"` + IncludeAndroidUser Listable[int] `json:"android_user,omitempty"` + EndpointIndependentNat bool `json:"endpoint_independent_nat,omitempty"` + UDPTimeout int64 `json:"udp_timeout,omitempty"` + Stack string `json:"stack,omitempty"` InboundOptions } diff --git a/test/go.mod b/test/go.mod index 1fc47204..01d7671f 100644 --- a/test/go.mod +++ b/test/go.mod @@ -10,7 +10,7 @@ require ( github.com/docker/docker v20.10.17+incompatible github.com/docker/go-connections v0.4.0 github.com/gofrs/uuid v4.2.0+incompatible - github.com/sagernet/sing v0.0.0-20220813024838-eb2fad956aa8 + github.com/sagernet/sing v0.0.0-20220814164830-4f2b872a8cbf github.com/sagernet/sing-shadowsocks v0.0.0-20220812082714-484a11603b48 github.com/spyzhov/ajson v0.7.1 github.com/stretchr/testify v1.8.0 @@ -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-20220808133432-d378b6ca536d // indirect + github.com/sagernet/sing-tun v0.0.0-20220815014658-b828f0164333 // 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 f74e9312..e5f909e1 100644 --- a/test/go.sum +++ b/test/go.sum @@ -175,14 +175,14 @@ github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR github.com/sagernet/netlink v0.0.0-20220803045538-bdac49abf805 h1:hE+vtsjBCCPmxkRz9jZA+CicHgVkDT6H+Av5ZzskVxs= github.com/sagernet/netlink v0.0.0-20220803045538-bdac49abf805/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= github.com/sagernet/sing v0.0.0-20220812082120-05f9836bff8f/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= -github.com/sagernet/sing v0.0.0-20220813024838-eb2fad956aa8 h1:zTuJbqzZgBh+iGPV41d8ZarKxXPgTq/+PGk1kMhPV8I= -github.com/sagernet/sing v0.0.0-20220813024838-eb2fad956aa8/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= +github.com/sagernet/sing v0.0.0-20220814164830-4f2b872a8cbf h1:JYnx6N1H2KeFKM7YSH5+qfhvvcy6gpqSpewkp1RLRSg= +github.com/sagernet/sing v0.0.0-20220814164830-4f2b872a8cbf/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= github.com/sagernet/sing-dns v0.0.0-20220813025814-e656c9dbf3ae h1:xOpbvgizvIbKKrrcl/CK3RjGY2u7rC+SBXlgqzEZOU4= 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-20220808133432-d378b6ca536d h1:AQpkoUiF8FxYVI1lf2W9Rbkk914eHjVH9M8y+F/0+Nw= -github.com/sagernet/sing-tun v0.0.0-20220808133432-d378b6ca536d/go.mod h1:gWwYd53AqXl+Y+q6WlXUc6PkqU28sfu5VTQhyeEIFbw= +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-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=