Add user rule item

This commit is contained in:
世界 2022-07-17 15:11:26 +08:00
parent cf845d946e
commit d1e83882e5
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
31 changed files with 212 additions and 177 deletions

View file

@ -21,6 +21,7 @@ type InboundContext struct {
Destination M.Socksaddr
Domain string
Protocol string
User string
Outbound string
// cache

View file

@ -1,111 +1,14 @@
package settings
import (
"os"
"syscall"
"unsafe"
F "github.com/sagernet/sing/common/format"
"golang.org/x/sys/windows"
"github.com/sagernet/sing/common/wininet"
)
var (
modwininet = windows.NewLazySystemDLL("settings.dll")
procInternetSetOptionW = modwininet.NewProc("InternetSetOptionW")
)
const (
internetOptionPerConnectionOption = 75
internetOptionSettingsChanged = 39
internetOptionRefresh = 37
internetOptionProxySettingsChanged = 95
)
const (
internetPerConnFlags = 1
internetPerConnProxyServer = 2
internetPerConnProxyBypass = 3
internetPerConnAutoconfigUrl = 4
internetPerConnAutodiscoveryFlags = 5
internetPerConnAutoconfigSecondaryUrl = 6
internetPerConnAutoconfigReloadDelayMins = 7
internetPerConnAutoconfigLastDetectTime = 8
internetPerConnAutoconfigLastDetectUrl = 9
internetPerConnFlagsUi = 10
internetOptionProxyUsername = 43
internetOptionProxyPassword = 44
)
const (
proxyTypeDirect = 1
proxyTypeProxy = 2
proxyTypeAutoProxyUrl = 4
proxyTypeAutoDetect = 8
)
type internetPerConnOptionList struct {
dwSize uint32
pszConnection uintptr
dwOptionCount uint32
dwOptionError uint32
pOptions uintptr
}
type internetPerConnOption struct {
dwOption uint32
value [8]byte
}
func internetSetOption(option uintptr, lpBuffer uintptr, dwBufferSize uintptr) error {
r0, _, err := syscall.SyscallN(procInternetSetOptionW.Addr(), 0, option, lpBuffer, dwBufferSize)
if r0 != 1 {
return err
}
return nil
}
func setOptions(options ...internetPerConnOption) error {
var optionList internetPerConnOptionList
optionList.dwSize = uint32(unsafe.Sizeof(optionList))
optionList.dwOptionCount = uint32(len(options))
optionList.dwOptionError = 0
optionList.pOptions = uintptr(unsafe.Pointer(&options[0]))
err := internetSetOption(internetOptionPerConnectionOption, uintptr(unsafe.Pointer(&optionList)), uintptr(optionList.dwSize))
if err != nil {
return os.NewSyscallError("InternetSetOption(Direct)", err)
}
err = internetSetOption(internetOptionSettingsChanged, 0, 0)
if err != nil {
return os.NewSyscallError("InternetSetOption(SettingsChanged)", err)
}
err = internetSetOption(internetOptionProxySettingsChanged, 0, 0)
if err != nil {
return os.NewSyscallError("InternetSetOption(ProxySettingsChanged)", err)
}
err = internetSetOption(internetOptionRefresh, 0, 0)
if err != nil {
return os.NewSyscallError("InternetSetOption(Refresh)", err)
}
return nil
}
func ClearSystemProxy() error {
var flagsOption internetPerConnOption
flagsOption.dwOption = internetPerConnFlags
*((*uint32)(unsafe.Pointer(&flagsOption.value))) = proxyTypeDirect | proxyTypeAutoDetect
return setOptions(flagsOption)
return wininet.ClearSystemProxy()
}
func SetSystemProxy(port uint16, mixed bool) error {
var flagsOption internetPerConnOption
flagsOption.dwOption = internetPerConnFlags
*((*uint32)(unsafe.Pointer(&flagsOption.value))) = proxyTypeProxy | proxyTypeDirect
var proxyOption internetPerConnOption
proxyOption.dwOption = internetPerConnProxyServer
*((*uintptr)(unsafe.Pointer(&proxyOption.value))) = uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(F.ToString("http://127.0.0.1:", port))))
var bypassOption internetPerConnOption
bypassOption.dwOption = internetPerConnProxyBypass
*((*uintptr)(unsafe.Pointer(&bypassOption.value))) = uintptr(unsafe.Pointer(windows.StringToUTF16Ptr("local")))
return setOptions(flagsOption, proxyOption, bypassOption)
return wininet.SetSystemProxy(F.ToString("http://127.0.0.1:", port), "local")
}

View file

@ -9,6 +9,10 @@
"mixed-in"
],
"network": "tcp",
"user": [
"usera",
"userb"
],
"protocol": [
"tls",
"http",
@ -80,6 +84,14 @@ Tags of [inbound](../inbound).
`tcp` or `udp`.
#### user
Username, see each inbound for details.
#### protocol
Sniffed protocol, see [Sniff](/configuration/route/sniff/) for details.
#### domain
Match full domain.

View file

@ -47,9 +47,7 @@ Enable tcp fast open for listener.
Enable sniffing.
Reads domain names for routing, supports HTTP TLS for TCP, QUIC for UDP.
This does not break zero copy, like splice.
See [Sniff](/configuration/route/sniff/) for details.
#### sniff_override_destination

View file

@ -51,9 +51,7 @@ Enable tcp fast open for listener.
Enable sniffing.
Reads domain names for routing, supports HTTP TLS for TCP, QUIC for UDP.
This does not break zero copy, like splice.
See [Sniff](/configuration/route/sniff/) for details.
#### sniff_override_destination

View file

@ -51,9 +51,7 @@ Enable tcp fast open for listener.
Enable sniffing.
Reads domain names for routing, supports HTTP TLS for TCP, QUIC for UDP.
This does not break zero copy, like splice.
See [Sniff](/configuration/route/sniff/) for details.
#### sniff_override_destination

View file

@ -8,6 +8,7 @@
{
"type": "redirect",
"tag": "redirect-in",
"listen": "::",
"listen_port": 5353,
"sniff": false,
@ -36,9 +37,7 @@ Listen port.
Enable sniffing.
Reads domain names for routing, supports HTTP TLS for TCP, QUIC for UDP.
This does not break zero copy, like splice.
See [Sniff](/configuration/route/sniff/) for details.
#### sniff_override_destination

View file

@ -47,9 +47,7 @@ Enable tcp fast open for listener.
Enable sniffing.
Reads domain names for routing, supports HTTP TLS for TCP, QUIC for UDP.
This does not break zero copy, like splice.
See [Sniff](/configuration/route/sniff/) for details.
#### sniff_override_destination

View file

@ -49,9 +49,7 @@ Enable tcp fast open for listener.
Enable sniffing.
Reads domain names for routing, supports HTTP TLS for TCP, QUIC for UDP.
This does not break zero copy, like splice.
See [Sniff](/configuration/route/sniff/) for details.
#### sniff_override_destination

View file

@ -40,9 +40,7 @@ Listen port.
Enable sniffing.
Reads domain names for routing, supports HTTP TLS for TCP, QUIC for UDP.
This does not break zero copy, like splice.
See [Sniff](/configuration/route/sniff/) for details.
#### sniff_override_destination

View file

@ -59,9 +59,7 @@ Hijack TCP/UDP DNS requests to the built-in DNS adapter.
Enable sniffing.
Reads domain names for routing, supports HTTP TLS for TCP, QUIC for UDP.
This does not break zero copy, like splice.
See [Sniff](/configuration/route/sniff/) for details.
#### sniff_override_destination

View file

@ -9,6 +9,10 @@
"mixed-in"
],
"network": "tcp",
"user": [
"usera",
"userb"
],
"protocol": [
"tls",
"http",
@ -79,6 +83,14 @@
Tags of [inbound](../inbound).
#### user
Username, see each inbound for details.
#### protocol
Sniffed protocol, see [Sniff](/configuration/route/sniff/) for details.
#### network
`tcp` or `udp`.

View file

@ -0,0 +1,10 @@
If enabled in the inbound, the protocol and domain name (if present) of by the connection can be sniffed.
#### Supported Protocols
| Network | Protocol | Domain Name |
|:---------:|:----------:|:-------------:|
| TCP | HTTP | Host |
| TCP | TLS | Server Name |
| UDP | QUIC | Server Name |
| UDP | STUN | / |

4
go.mod
View file

@ -7,9 +7,9 @@ require (
github.com/goccy/go-json v0.9.10
github.com/logrusorgru/aurora v2.0.3+incompatible
github.com/oschwald/maxminddb-golang v1.9.0
github.com/sagernet/sing v0.0.0-20220716021830-bd79d31e3b10
github.com/sagernet/sing v0.0.0-20220717063925-00f98eb6bc34
github.com/sagernet/sing-dns v0.0.0-20220711062726-c64e938e4619
github.com/sagernet/sing-shadowsocks v0.0.0-20220716012931-952ae62e05d7
github.com/sagernet/sing-shadowsocks v0.0.0-20220717063942-45a2ad9cd41f
github.com/sagernet/sing-tun v0.0.0-20220717030718-f53aabff275f
github.com/spf13/cobra v1.5.0
github.com/stretchr/testify v1.8.0

8
go.sum
View file

@ -25,12 +25,12 @@ github.com/oschwald/maxminddb-golang v1.9.0/go.mod h1:TK+s/Z2oZq0rSl4PSeAEoP0bgm
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sagernet/sing v0.0.0-20220716021830-bd79d31e3b10 h1:CQSsVgvVT6KcYNQASP4jnPTg7epSxHGI3MS011LIXkA=
github.com/sagernet/sing v0.0.0-20220716021830-bd79d31e3b10/go.mod h1:3ZmoGNg/nNJTyHAZFNRSPaXpNIwpDvyIiAUd0KIWV5c=
github.com/sagernet/sing v0.0.0-20220717063925-00f98eb6bc34 h1:1kFruA2QzuH2R6txJXEDSasfdxzsjNyzC4Z1kZjMkHg=
github.com/sagernet/sing v0.0.0-20220717063925-00f98eb6bc34/go.mod h1:GbtQfZSpmtD3cXeD1qX2LCMwY8dH+bnnInDTqd92IsM=
github.com/sagernet/sing-dns v0.0.0-20220711062726-c64e938e4619 h1:oHbOmq1WS0XaZmXp6WpxzyB2xeyRIA1/L8EJKuNntfY=
github.com/sagernet/sing-dns v0.0.0-20220711062726-c64e938e4619/go.mod h1:y2fpvoxukw3G7eApIZwkcpcG/NE4AB8pCQI0Qd8rMqk=
github.com/sagernet/sing-shadowsocks v0.0.0-20220716012931-952ae62e05d7 h1:7xQvlMSxNWphQ4t+7fHfR4OnkH23GukLIjImnM1CMLA=
github.com/sagernet/sing-shadowsocks v0.0.0-20220716012931-952ae62e05d7/go.mod h1:NtHwPOk1wEOPdjjsjtrYoaQuXtlDCrx0mrcWBrNE0sA=
github.com/sagernet/sing-shadowsocks v0.0.0-20220717063942-45a2ad9cd41f h1:F6yiuKbBoXgWiuoP7R0YA14pDEl3emxA1mL7M16Q7gc=
github.com/sagernet/sing-shadowsocks v0.0.0-20220717063942-45a2ad9cd41f/go.mod h1:cDrLwa3zwY8AaW6a4sjipn4xgdIr3CT8TPqSW6iFOi0=
github.com/sagernet/sing-tun v0.0.0-20220717030718-f53aabff275f h1:o3YN4sFC7lQznAwutagPqBb23hal7MkgVq/VEvd7Vug=
github.com/sagernet/sing-tun v0.0.0-20220717030718-f53aabff275f/go.mod h1:p7QbUBs2ejf6UQsiHyy1xGAWOk9JWQjZTHy8pOmkWmo=
github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=

View file

@ -11,6 +11,7 @@ import (
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common/auth"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/protocol/http"
)
@ -40,5 +41,25 @@ func NewHTTP(ctx context.Context, router adapter.Router, logger log.ContextLogge
}
func (h *HTTP) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
return http.HandleConnection(ctx, conn, std_bufio.NewReader(conn), h.authenticator, h.upstreamHandler(metadata), M.Metadata{})
return http.HandleConnection(ctx, conn, std_bufio.NewReader(conn), h.authenticator, h.upstreamUserHandler(metadata), M.Metadata{})
}
func (a *myInboundAdapter) upstreamUserHandler(metadata adapter.InboundContext) adapter.UpstreamHandlerAdapter {
return adapter.NewUpstreamHandler(metadata, a.newUserConnection, a.streamUserPacketConnection, a)
}
func (a *myInboundAdapter) newUserConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
user, loaded := auth.UserFromContext[string](ctx)
if !loaded {
a.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
return a.router.RouteConnection(ctx, conn, metadata)
}
metadata.User = user
a.logger.InfoContext(ctx, "[", user, "] inbound connection to ", metadata.Destination)
return a.router.RouteConnection(ctx, conn, metadata)
}
func (a *myInboundAdapter) streamUserPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
a.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
return a.router.RoutePacketConnection(ctx, conn, metadata)
}

View file

@ -52,8 +52,8 @@ func (h *Mixed) NewConnection(ctx context.Context, conn net.Conn, metadata adapt
}
switch headerType {
case socks4.Version, socks5.Version:
return socks.HandleConnection0(ctx, conn, headerType, h.authenticator, h.upstreamHandler(metadata), M.Metadata{})
return socks.HandleConnection0(ctx, conn, headerType, h.authenticator, h.upstreamUserHandler(metadata), M.Metadata{})
}
reader := std_bufio.NewReader(bufio.NewCachedReader(conn, buf.As([]byte{headerType})))
return http.HandleConnection(ctx, conn, reader, h.authenticator, h.upstreamHandler(metadata), M.Metadata{})
return http.HandleConnection(ctx, conn, reader, h.authenticator, h.upstreamUserHandler(metadata), M.Metadata{})
}

View file

@ -77,5 +77,17 @@ func (h *Shadowsocks) NewConnection(ctx context.Context, conn net.Conn, metadata
}
func (h *Shadowsocks) NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.Buffer, metadata adapter.InboundContext) error {
return h.service.NewPacket(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, buffer, adapter.UpstreamMetadata(metadata))
return h.service.NewPacket(adapter.WithContext(ctx, &metadata), conn, buffer, adapter.UpstreamMetadata(metadata))
}
func (h *Shadowsocks) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
return h.router.RouteConnection(ctx, conn, metadata)
}
func (h *Shadowsocks) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
ctx = log.ContextWithNewID(ctx)
h.logger.InfoContext(ctx, "inbound packet connection from ", metadata.Source)
h.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
return h.router.RoutePacketConnection(ctx, conn, metadata)
}

View file

@ -3,14 +3,15 @@ package inbound
import (
"context"
"net"
"os"
"github.com/sagernet/sing-box/adapter"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-shadowsocks"
"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/auth"
"github.com/sagernet/sing/common/buf"
F "github.com/sagernet/sing/common/format"
N "github.com/sagernet/sing/common/network"
@ -72,24 +73,34 @@ func (h *ShadowsocksMulti) NewConnection(ctx context.Context, conn net.Conn, met
}
func (h *ShadowsocksMulti) NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.Buffer, metadata adapter.InboundContext) error {
return h.service.NewPacket(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, buffer, adapter.UpstreamMetadata(metadata))
return h.service.NewPacket(adapter.WithContext(ctx, &metadata), conn, buffer, adapter.UpstreamMetadata(metadata))
}
func (h *ShadowsocksMulti) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
userCtx := ctx.(*shadowsocks.UserContext[int])
user := h.users[userCtx.User].Name
userIndex, loaded := auth.UserFromContext[int](ctx)
if !loaded {
return os.ErrInvalid
}
user := h.users[userIndex].Name
if user == "" {
user = F.ToString(userCtx.User)
user = F.ToString(userIndex)
} else {
metadata.User = user
}
h.logger.InfoContext(ctx, "[", user, "] inbound connection to ", metadata.Destination)
return h.router.RouteConnection(ctx, conn, metadata)
}
func (h *ShadowsocksMulti) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
userCtx := ctx.(*shadowsocks.UserContext[int])
user := h.users[userCtx.User].Name
userIndex, loaded := auth.UserFromContext[int](ctx)
if !loaded {
return os.ErrInvalid
}
user := h.users[userIndex].Name
if user == "" {
user = F.ToString(userCtx.User)
user = F.ToString(userIndex)
} else {
metadata.User = user
}
ctx = log.ContextWithNewID(ctx)
h.logger.InfoContext(ctx, "[", user, "] inbound packet connection from ", metadata.Source)

View file

@ -3,14 +3,15 @@ package inbound
import (
"context"
"net"
"os"
"github.com/sagernet/sing-box/adapter"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-shadowsocks"
"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/auth"
"github.com/sagernet/sing/common/buf"
F "github.com/sagernet/sing/common/format"
N "github.com/sagernet/sing/common/network"
@ -72,24 +73,34 @@ func (h *ShadowsocksRelay) NewConnection(ctx context.Context, conn net.Conn, met
}
func (h *ShadowsocksRelay) NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.Buffer, metadata adapter.InboundContext) error {
return h.service.NewPacket(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, buffer, adapter.UpstreamMetadata(metadata))
return h.service.NewPacket(adapter.WithContext(ctx, &metadata), conn, buffer, adapter.UpstreamMetadata(metadata))
}
func (h *ShadowsocksRelay) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
userCtx := ctx.(*shadowsocks.UserContext[int])
destination := h.destinations[userCtx.User].Name
destinationIndex, loaded := auth.UserFromContext[int](ctx)
if !loaded {
return os.ErrInvalid
}
destination := h.destinations[destinationIndex].Name
if destination == "" {
destination = F.ToString(userCtx.User)
destination = F.ToString(destinationIndex)
} else {
metadata.User = destination
}
h.logger.InfoContext(ctx, "[", destination, "] inbound connection to ", metadata.Destination)
return h.router.RouteConnection(ctx, conn, metadata)
}
func (h *ShadowsocksRelay) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
userCtx := ctx.(*shadowsocks.UserContext[int])
destination := h.destinations[userCtx.User].Name
destinationIndex, loaded := auth.UserFromContext[int](ctx)
if !loaded {
return os.ErrInvalid
}
destination := h.destinations[destinationIndex].Name
if destination == "" {
destination = F.ToString(userCtx.User)
destination = F.ToString(destinationIndex)
} else {
metadata.User = destination
}
ctx = log.ContextWithNewID(ctx)
h.logger.InfoContext(ctx, "[", destination, "] inbound packet connection from ", metadata.Source)

View file

@ -38,5 +38,5 @@ func NewSocks(ctx context.Context, router adapter.Router, logger log.ContextLogg
}
func (h *Socks) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
return socks.HandleConnection(ctx, conn, h.authenticator, h.upstreamHandler(metadata), M.Metadata{})
return socks.HandleConnection(ctx, conn, h.authenticator, h.upstreamUserHandler(metadata), M.Metadata{})
}

View file

@ -61,6 +61,7 @@ nav:
- GeoIP: configuration/route/geoip.md
- Geosite: configuration/route/geosite.md
- Route Rule: configuration/route/rule.md
- Protocol Sniff: configuration/route/sniff.md
- Examples:
- examples/index.md
- Shadowsocks Server: examples/ss-server.md

View file

@ -91,6 +91,7 @@ func (r *DNSRule) UnmarshalJSON(bytes []byte) error {
type DefaultDNSRule struct {
Inbound Listable[string] `json:"inbound,omitempty"`
Network string `json:"network,omitempty"`
User Listable[string] `json:"user,omitempty"`
Protocol Listable[string] `json:"protocol,omitempty"`
Domain Listable[string] `json:"domain,omitempty"`
DomainSuffix Listable[string] `json:"domain_suffix,omitempty"`
@ -114,6 +115,7 @@ func (r DefaultDNSRule) IsValid() bool {
func (r DefaultDNSRule) Equals(other DefaultDNSRule) bool {
return common.ComparableSliceEquals(r.Inbound, other.Inbound) &&
r.Network == other.Network &&
common.ComparableSliceEquals(r.User, other.User) &&
common.ComparableSliceEquals(r.Protocol, other.Protocol) &&
common.ComparableSliceEquals(r.Domain, other.Domain) &&
common.ComparableSliceEquals(r.DomainSuffix, other.DomainSuffix) &&

View file

@ -89,6 +89,7 @@ type DefaultRule struct {
Inbound Listable[string] `json:"inbound,omitempty"`
IPVersion int `json:"ip_version,omitempty"`
Network string `json:"network,omitempty"`
User Listable[string] `json:"user,omitempty"`
Protocol Listable[string] `json:"protocol,omitempty"`
Domain Listable[string] `json:"domain,omitempty"`
DomainSuffix Listable[string] `json:"domain_suffix,omitempty"`
@ -114,6 +115,7 @@ func (r DefaultRule) Equals(other DefaultRule) bool {
return common.ComparableSliceEquals(r.Inbound, other.Inbound) &&
r.IPVersion == other.IPVersion &&
r.Network == other.Network &&
common.ComparableSliceEquals(r.User, other.User) &&
common.ComparableSliceEquals(r.Protocol, other.Protocol) &&
common.ComparableSliceEquals(r.Domain, other.Domain) &&
common.ComparableSliceEquals(r.DomainSuffix, other.DomainSuffix) &&

View file

@ -82,8 +82,13 @@ func NewDefaultRule(router adapter.Router, logger log.ContextLogger, options opt
return nil, E.New("invalid network: ", options.Network)
}
}
if len(options.User) > 0 {
item := NewUserItem(options.User)
rule.items = append(rule.items, item)
rule.allItems = append(rule.allItems, item)
}
if len(options.Protocol) > 0 {
item := NewProtocolItem(options.Protocol)
item := NewUserItem(options.Protocol)
rule.items = append(rule.items, item)
rule.allItems = append(rule.allItems, item)
}

View file

@ -66,8 +66,13 @@ func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options
return nil, E.New("invalid network: ", options.Network)
}
}
if len(options.User) > 0 {
item := NewUserItem(options.User)
rule.items = append(rule.items, item)
rule.allItems = append(rule.allItems, item)
}
if len(options.Protocol) > 0 {
item := NewProtocolItem(options.Protocol)
item := NewUserItem(options.Protocol)
rule.items = append(rule.items, item)
rule.allItems = append(rule.allItems, item)
}

37
route/rule_user.go Normal file
View file

@ -0,0 +1,37 @@
package route
import (
"strings"
"github.com/sagernet/sing-box/adapter"
F "github.com/sagernet/sing/common/format"
)
var _ RuleItem = (*UserItem)(nil)
type UserItem struct {
users []string
userMap map[string]bool
}
func NewUserItem(users []string) *UserItem {
userMap := make(map[string]bool)
for _, protocol := range users {
userMap[protocol] = true
}
return &UserItem{
users: users,
userMap: userMap,
}
}
func (r *UserItem) Match(metadata *adapter.InboundContext) bool {
return r.userMap[metadata.User]
}
func (r *UserItem) String() string {
if len(r.users) == 1 {
return F.ToString("user=", r.users[0])
}
return F.ToString("user=[", strings.Join(r.users, " "), "]")
}

View file

@ -46,15 +46,15 @@ func startInstance(t *testing.T, options option.Options) {
func testSuit(t *testing.T, clientPort uint16, testPort uint16) {
dialer := socks.NewClient(N.SystemDialer, M.ParseSocksaddrHostPort("127.0.0.1", clientPort), socks.Version5, "", "")
dialTCP := func() (net.Conn, error) {
return dialer.DialContext(context.Background(), "tcp", M.ParseSocksaddrHostPort("127.0.0.1", testPort))
dialTCP := func(port uint16) (net.Conn, error) {
return dialer.DialContext(context.Background(), "tcp", M.ParseSocksaddrHostPort("127.0.0.1", port))
}
dialUDP := func() (net.PacketConn, error) {
return dialer.ListenPacket(context.Background(), M.ParseSocksaddrHostPort("127.0.0.1", testPort))
dialUDP := func(port uint16) (net.PacketConn, error) {
return dialer.ListenPacket(context.Background(), M.ParseSocksaddrHostPort("127.0.0.1", port))
}
require.NoError(t, testPingPongWithConn(t, testPort, dialTCP))
require.NoError(t, testLargeDataWithConn(t, testPort, dialTCP))
require.NoError(t, testPingPongWithPacketConn(t, testPort, dialUDP))
require.NoError(t, testLargeDataWithPacketConn(t, testPort, dialUDP))
require.NoError(t, testPingPongWithConn(t, dialTCP))
require.NoError(t, testLargeDataWithConn(t, dialTCP))
require.NoError(t, testPingPongWithPacketConn(t, dialUDP))
require.NoError(t, testLargeDataWithPacketConn(t, dialUDP))
require.NoError(t, testPacketConnTimeout(t, dialUDP))
}

View file

@ -152,14 +152,15 @@ func newLargeDataPair() (chan hashPair, chan hashPair, func(t *testing.T) error)
return pingCh, pongCh, test
}
func testPingPongWithConn(t *testing.T, port uint16, cc func() (net.Conn, error)) error {
func testPingPongWithConn(t *testing.T, cc func(port uint16) (net.Conn, error)) error {
port := mkPort(t)
l, err := listen("tcp", ":"+F.ToString(port))
if err != nil {
return err
}
defer l.Close()
c, err := cc()
c, err := cc(port)
if err != nil {
return err
}
@ -198,7 +199,9 @@ func testPingPongWithConn(t *testing.T, port uint16, cc func() (net.Conn, error)
return test(t)
}
func testPingPongWithPacketConn(t *testing.T, port uint16, pcc func() (net.PacketConn, error)) error {
func testPingPongWithPacketConn(t *testing.T, pcc func(port uint16) (net.PacketConn, error)) error {
port := mkPort(t)
l, err := listenPacket("udp", ":"+F.ToString(port))
require.NoError(t, err)
defer l.Close()
@ -219,7 +222,7 @@ func testPingPongWithPacketConn(t *testing.T, port uint16, pcc func() (net.Packe
}
}()
pc, err := pcc()
pc, err := pcc(port)
if err != nil {
return err
}
@ -246,7 +249,8 @@ type hashPair struct {
recvHash map[int][]byte
}
func testLargeDataWithConn(t *testing.T, port uint16, cc func() (net.Conn, error)) error {
func testLargeDataWithConn(t *testing.T, cc func(port uint16) (net.Conn, error)) error {
port := mkPort(t)
l, err := listen("tcp", ":"+F.ToString(port))
require.NoError(t, err)
defer l.Close()
@ -275,7 +279,7 @@ func testLargeDataWithConn(t *testing.T, port uint16, cc func() (net.Conn, error
return hashMap, nil
}
c, err := cc()
c, err := cc(port)
if err != nil {
return err
}
@ -343,7 +347,8 @@ func testLargeDataWithConn(t *testing.T, port uint16, cc func() (net.Conn, error
return test(t)
}
func testLargeDataWithPacketConn(t *testing.T, port uint16, pcc func() (net.PacketConn, error)) error {
func testLargeDataWithPacketConn(t *testing.T, pcc func(port uint16) (net.PacketConn, error)) error {
port := mkPort(t)
l, err := listenPacket("udp", ":"+F.ToString(port))
require.NoError(t, err)
defer l.Close()
@ -409,7 +414,7 @@ func testLargeDataWithPacketConn(t *testing.T, port uint16, pcc func() (net.Pack
}
}()
pc, err := pcc()
pc, err := pcc(port)
if err != nil {
return err
}
@ -444,8 +449,8 @@ func testLargeDataWithPacketConn(t *testing.T, port uint16, pcc func() (net.Pack
return test(t)
}
func testPacketConnTimeout(t *testing.T, pcc func() (net.PacketConn, error)) error {
pc, err := pcc()
func testPacketConnTimeout(t *testing.T, pcc func(port uint16) (net.PacketConn, error)) error {
pc, err := pcc(mkPort(t))
if err != nil {
return err
}

View file

@ -5,7 +5,7 @@ go 1.18
require (
github.com/docker/docker v20.10.17+incompatible
github.com/docker/go-connections v0.4.0
github.com/sagernet/sing v0.0.0-20220716021830-bd79d31e3b10
github.com/sagernet/sing v0.0.0-20220717063925-00f98eb6bc34
github.com/sagernet/sing-box v0.0.0
github.com/stretchr/testify v1.8.0
golang.org/x/net v0.0.0-20220708220712-1185a9018129
@ -33,7 +33,7 @@ require (
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/sagernet/sing-dns v0.0.0-20220711062726-c64e938e4619 // indirect
github.com/sagernet/sing-shadowsocks v0.0.0-20220716012931-952ae62e05d7 // indirect
github.com/sagernet/sing-shadowsocks v0.0.0-20220717063942-45a2ad9cd41f // indirect
github.com/sagernet/sing-tun v0.0.0-20220717030718-f53aabff275f // indirect
github.com/sirupsen/logrus v1.8.1 // indirect
github.com/vishvananda/netlink v1.1.0 // indirect

View file

@ -52,12 +52,12 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sagernet/sing v0.0.0-20220716021830-bd79d31e3b10 h1:CQSsVgvVT6KcYNQASP4jnPTg7epSxHGI3MS011LIXkA=
github.com/sagernet/sing v0.0.0-20220716021830-bd79d31e3b10/go.mod h1:3ZmoGNg/nNJTyHAZFNRSPaXpNIwpDvyIiAUd0KIWV5c=
github.com/sagernet/sing v0.0.0-20220717063925-00f98eb6bc34 h1:1kFruA2QzuH2R6txJXEDSasfdxzsjNyzC4Z1kZjMkHg=
github.com/sagernet/sing v0.0.0-20220717063925-00f98eb6bc34/go.mod h1:GbtQfZSpmtD3cXeD1qX2LCMwY8dH+bnnInDTqd92IsM=
github.com/sagernet/sing-dns v0.0.0-20220711062726-c64e938e4619 h1:oHbOmq1WS0XaZmXp6WpxzyB2xeyRIA1/L8EJKuNntfY=
github.com/sagernet/sing-dns v0.0.0-20220711062726-c64e938e4619/go.mod h1:y2fpvoxukw3G7eApIZwkcpcG/NE4AB8pCQI0Qd8rMqk=
github.com/sagernet/sing-shadowsocks v0.0.0-20220716012931-952ae62e05d7 h1:7xQvlMSxNWphQ4t+7fHfR4OnkH23GukLIjImnM1CMLA=
github.com/sagernet/sing-shadowsocks v0.0.0-20220716012931-952ae62e05d7/go.mod h1:NtHwPOk1wEOPdjjsjtrYoaQuXtlDCrx0mrcWBrNE0sA=
github.com/sagernet/sing-shadowsocks v0.0.0-20220717063942-45a2ad9cd41f h1:F6yiuKbBoXgWiuoP7R0YA14pDEl3emxA1mL7M16Q7gc=
github.com/sagernet/sing-shadowsocks v0.0.0-20220717063942-45a2ad9cd41f/go.mod h1:cDrLwa3zwY8AaW6a4sjipn4xgdIr3CT8TPqSW6iFOi0=
github.com/sagernet/sing-tun v0.0.0-20220717030718-f53aabff275f h1:o3YN4sFC7lQznAwutagPqBb23hal7MkgVq/VEvd7Vug=
github.com/sagernet/sing-tun v0.0.0-20220717030718-f53aabff275f/go.mod h1:p7QbUBs2ejf6UQsiHyy1xGAWOk9JWQjZTHy8pOmkWmo=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=