chore: add IN-USER
and IN-NAME
rules
This commit is contained in:
parent
7aae781569
commit
9c2972afb0
9 changed files with 142 additions and 18 deletions
|
@ -16,6 +16,12 @@ func WithInName(name string) Addition {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func WithInUser(user string) Addition {
|
||||||
|
return func(metadata *C.Metadata) {
|
||||||
|
metadata.InUser = user
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func WithSpecialRules(specialRules string) Addition {
|
func WithSpecialRules(specialRules string) Addition {
|
||||||
return func(metadata *C.Metadata) {
|
return func(metadata *C.Metadata) {
|
||||||
metadata.SpecialRules = specialRules
|
metadata.SpecialRules = specialRules
|
||||||
|
|
|
@ -133,6 +133,7 @@ type Metadata struct {
|
||||||
InIP netip.Addr `json:"inboundIP"`
|
InIP netip.Addr `json:"inboundIP"`
|
||||||
InPort string `json:"inboundPort"`
|
InPort string `json:"inboundPort"`
|
||||||
InName string `json:"inboundName"`
|
InName string `json:"inboundName"`
|
||||||
|
InUser string `json:"inboundUser"`
|
||||||
Host string `json:"host"`
|
Host string `json:"host"`
|
||||||
DNSMode DNSMode `json:"dnsMode"`
|
DNSMode DNSMode `json:"dnsMode"`
|
||||||
Uid uint32 `json:"uid"`
|
Uid uint32 `json:"uid"`
|
||||||
|
|
|
@ -14,12 +14,14 @@ const (
|
||||||
SrcPort
|
SrcPort
|
||||||
DstPort
|
DstPort
|
||||||
InPort
|
InPort
|
||||||
|
InUser
|
||||||
|
InName
|
||||||
|
InType
|
||||||
Process
|
Process
|
||||||
ProcessPath
|
ProcessPath
|
||||||
RuleSet
|
RuleSet
|
||||||
Network
|
Network
|
||||||
Uid
|
Uid
|
||||||
INTYPE
|
|
||||||
SubRules
|
SubRules
|
||||||
MATCH
|
MATCH
|
||||||
AND
|
AND
|
||||||
|
@ -55,6 +57,12 @@ func (rt RuleType) String() string {
|
||||||
return "DstPort"
|
return "DstPort"
|
||||||
case InPort:
|
case InPort:
|
||||||
return "InPort"
|
return "InPort"
|
||||||
|
case InUser:
|
||||||
|
return "InUser"
|
||||||
|
case InName:
|
||||||
|
return "InName"
|
||||||
|
case InType:
|
||||||
|
return "InType"
|
||||||
case Process:
|
case Process:
|
||||||
return "Process"
|
return "Process"
|
||||||
case ProcessPath:
|
case ProcessPath:
|
||||||
|
@ -67,8 +75,6 @@ func (rt RuleType) String() string {
|
||||||
return "Network"
|
return "Network"
|
||||||
case Uid:
|
case Uid:
|
||||||
return "Uid"
|
return "Uid"
|
||||||
case INTYPE:
|
|
||||||
return "InType"
|
|
||||||
case SubRules:
|
case SubRules:
|
||||||
return "SubRules"
|
return "SubRules"
|
||||||
case AND:
|
case AND:
|
||||||
|
|
|
@ -2,8 +2,11 @@ package sing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/adapter/inbound"
|
"github.com/Dreamacro/clash/adapter/inbound"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing/common/auth"
|
||||||
)
|
)
|
||||||
|
|
||||||
type contextKey string
|
type contextKey string
|
||||||
|
@ -22,3 +25,20 @@ func getAdditions(ctx context.Context) []inbound.Addition {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func combineAdditions(ctx context.Context, additions []inbound.Addition) []inbound.Addition {
|
||||||
|
additionsCloned := false
|
||||||
|
if ctxAdditions := getAdditions(ctx); len(ctxAdditions) > 0 {
|
||||||
|
additions = slices.Clone(additions)
|
||||||
|
additionsCloned = true
|
||||||
|
additions = append(additions, ctxAdditions...)
|
||||||
|
}
|
||||||
|
if user, ok := auth.UserFromContext[string](ctx); ok {
|
||||||
|
if !additionsCloned {
|
||||||
|
additions = slices.Clone(additions)
|
||||||
|
additionsCloned = true
|
||||||
|
}
|
||||||
|
additions = append(additions, inbound.WithInUser(user))
|
||||||
|
}
|
||||||
|
return additions
|
||||||
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ package sing
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"golang.org/x/exp/slices"
|
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -74,11 +73,6 @@ func UpstreamMetadata(metadata M.Metadata) M.Metadata {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||||
additions := h.Additions
|
|
||||||
if ctxAdditions := getAdditions(ctx); len(ctxAdditions) > 0 {
|
|
||||||
additions = slices.Clone(additions)
|
|
||||||
additions = append(additions, ctxAdditions...)
|
|
||||||
}
|
|
||||||
switch metadata.Destination.Fqdn {
|
switch metadata.Destination.Fqdn {
|
||||||
case mux.Destination.Fqdn:
|
case mux.Destination.Fqdn:
|
||||||
return mux.HandleConnection(ctx, h, log.SingLogger, conn, UpstreamMetadata(metadata))
|
return mux.HandleConnection(ctx, h, log.SingLogger, conn, UpstreamMetadata(metadata))
|
||||||
|
@ -103,16 +97,11 @@ func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, meta
|
||||||
if deadline.NeedAdditionalReadDeadline(conn) {
|
if deadline.NeedAdditionalReadDeadline(conn) {
|
||||||
conn = N.NewDeadlineConn(conn) // conn from sing should check NeedAdditionalReadDeadline
|
conn = N.NewDeadlineConn(conn) // conn from sing should check NeedAdditionalReadDeadline
|
||||||
}
|
}
|
||||||
h.TcpIn <- inbound.NewSocket(target, &waitCloseConn{ExtendedConn: N.NewExtendedConn(conn), wg: wg, rAddr: metadata.Source.TCPAddr()}, h.Type, additions...)
|
h.TcpIn <- inbound.NewSocket(target, &waitCloseConn{ExtendedConn: N.NewExtendedConn(conn), wg: wg, rAddr: metadata.Source.TCPAddr()}, h.Type, combineAdditions(ctx, h.Additions)...)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.PacketConn, metadata M.Metadata) error {
|
func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.PacketConn, metadata M.Metadata) error {
|
||||||
additions := h.Additions
|
|
||||||
if ctxAdditions := getAdditions(ctx); len(ctxAdditions) > 0 {
|
|
||||||
additions = slices.Clone(additions)
|
|
||||||
additions = append(additions, ctxAdditions...)
|
|
||||||
}
|
|
||||||
if deadline.NeedAdditionalReadDeadline(conn) {
|
if deadline.NeedAdditionalReadDeadline(conn) {
|
||||||
conn = deadline.NewFallbackPacketConn(bufio.NewNetPacketConn(conn)) // conn from sing should check NeedAdditionalReadDeadline
|
conn = deadline.NewFallbackPacketConn(bufio.NewNetPacketConn(conn)) // conn from sing should check NeedAdditionalReadDeadline
|
||||||
}
|
}
|
||||||
|
@ -162,7 +151,7 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.
|
||||||
buff: buff,
|
buff: buff,
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
case h.UdpIn <- inbound.NewPacket(target, packet, h.Type, additions...):
|
case h.UdpIn <- inbound.NewPacket(target, packet, h.Type, combineAdditions(ctx, h.Additions)...):
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
49
rules/common/in_name.go
Normal file
49
rules/common/in_name.go
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
C "github.com/Dreamacro/clash/constant"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type InName struct {
|
||||||
|
*Base
|
||||||
|
names []string
|
||||||
|
adapter string
|
||||||
|
payload string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *InName) Match(metadata *C.Metadata) (bool, string) {
|
||||||
|
for _, name := range u.names {
|
||||||
|
if metadata.InName == name {
|
||||||
|
return true, u.adapter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *InName) RuleType() C.RuleType {
|
||||||
|
return C.InName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *InName) Adapter() string {
|
||||||
|
return u.adapter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *InName) Payload() string {
|
||||||
|
return u.payload
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewInName(iNames, adapter string) (*InName, error) {
|
||||||
|
names := strings.Split(iNames, "/")
|
||||||
|
if len(names) == 0 {
|
||||||
|
return nil, fmt.Errorf("in name couldn't be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &InName{
|
||||||
|
Base: &Base{},
|
||||||
|
names: names,
|
||||||
|
adapter: adapter,
|
||||||
|
payload: iNames,
|
||||||
|
}, nil
|
||||||
|
}
|
|
@ -23,7 +23,7 @@ func (u *InType) Match(metadata *C.Metadata) (bool, string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *InType) RuleType() C.RuleType {
|
func (u *InType) RuleType() C.RuleType {
|
||||||
return C.INTYPE
|
return C.InType
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *InType) Adapter() string {
|
func (u *InType) Adapter() string {
|
||||||
|
@ -37,7 +37,7 @@ func (u *InType) Payload() string {
|
||||||
func NewInType(iTypes, adapter string) (*InType, error) {
|
func NewInType(iTypes, adapter string) (*InType, error) {
|
||||||
types := strings.Split(iTypes, "/")
|
types := strings.Split(iTypes, "/")
|
||||||
if len(types) == 0 {
|
if len(types) == 0 {
|
||||||
return nil, fmt.Errorf("in type could be empty")
|
return nil, fmt.Errorf("in type couldn't be empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
tps, err := parseInTypes(types)
|
tps, err := parseInTypes(types)
|
||||||
|
|
49
rules/common/in_user.go
Normal file
49
rules/common/in_user.go
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
C "github.com/Dreamacro/clash/constant"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type InUser struct {
|
||||||
|
*Base
|
||||||
|
users []string
|
||||||
|
adapter string
|
||||||
|
payload string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *InUser) Match(metadata *C.Metadata) (bool, string) {
|
||||||
|
for _, user := range u.users {
|
||||||
|
if metadata.InUser == user {
|
||||||
|
return true, u.adapter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *InUser) RuleType() C.RuleType {
|
||||||
|
return C.InUser
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *InUser) Adapter() string {
|
||||||
|
return u.adapter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *InUser) Payload() string {
|
||||||
|
return u.payload
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewInUser(iUsers, adapter string) (*InUser, error) {
|
||||||
|
users := strings.Split(iUsers, "/")
|
||||||
|
if len(users) == 0 {
|
||||||
|
return nil, fmt.Errorf("in user couldn't be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &InUser{
|
||||||
|
Base: &Base{},
|
||||||
|
users: users,
|
||||||
|
adapter: adapter,
|
||||||
|
payload: iUsers,
|
||||||
|
}, nil
|
||||||
|
}
|
|
@ -47,6 +47,10 @@ func ParseRule(tp, payload, target string, params []string, subRules map[string]
|
||||||
parsed, parseErr = RC.NewUid(payload, target)
|
parsed, parseErr = RC.NewUid(payload, target)
|
||||||
case "IN-TYPE":
|
case "IN-TYPE":
|
||||||
parsed, parseErr = RC.NewInType(payload, target)
|
parsed, parseErr = RC.NewInType(payload, target)
|
||||||
|
case "IN-USER":
|
||||||
|
parsed, parseErr = RC.NewInUser(payload, target)
|
||||||
|
case "IN-NAME":
|
||||||
|
parsed, parseErr = RC.NewInName(payload, target)
|
||||||
case "SUB-RULE":
|
case "SUB-RULE":
|
||||||
parsed, parseErr = logic.NewSubRule(payload, target, subRules, ParseRule)
|
parsed, parseErr = logic.NewSubRule(payload, target, subRules, ParseRule)
|
||||||
case "AND":
|
case "AND":
|
||||||
|
|
Loading…
Reference in a new issue