Implement TCP and ICMP rejects

This commit is contained in:
世界 2024-10-22 21:28:22 +08:00
parent ac0678f6f1
commit 536b60744c
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
6 changed files with 94 additions and 40 deletions

View file

@ -34,6 +34,7 @@ type Router interface {
FakeIPStore() FakeIPStore FakeIPStore() FakeIPStore
ConnectionRouter ConnectionRouter
PreMatch(metadata InboundContext) error
ConnectionRouterEx ConnectionRouterEx
GeoIPReader() *geoip.Reader GeoIPReader() *geoip.Reader

View file

@ -35,6 +35,9 @@ const (
const ( const (
RuleActionRejectMethodDefault = "default" RuleActionRejectMethodDefault = "default"
RuleActionRejectMethodReset = "reset"
RuleActionRejectMethodNetworkUnreachable = "network-unreachable"
RuleActionRejectMethodHostUnreachable = "host-unreachable"
RuleActionRejectMethodPortUnreachable = "port-unreachable" RuleActionRejectMethodPortUnreachable = "port-unreachable"
RuleActionRejectMethodDrop = "drop" RuleActionRejectMethodDrop = "drop"
) )

View file

@ -404,9 +404,15 @@ func (t *TUN) Close() error {
) )
} }
func (t *TUN) PrepareConnection(source M.Socksaddr, destination M.Socksaddr) error { func (t *TUN) PrepareConnection(network string, source M.Socksaddr, destination M.Socksaddr) error {
// TODO: implement rejects return t.router.PreMatch(adapter.InboundContext{
return nil Inbound: t.tag,
InboundType: C.TypeTun,
Network: network,
Source: source,
Destination: destination,
InboundOptions: t.inboundOptions,
})
} }
func (t *TUN) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) { func (t *TUN) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {

View file

@ -136,23 +136,29 @@ type DNSRouteActionOptions struct {
ClientSubnet *AddrPrefix `json:"client_subnet,omitempty"` ClientSubnet *AddrPrefix `json:"client_subnet,omitempty"`
} }
type RejectActionOptions struct { type _RejectActionOptions struct {
Method RejectMethod `json:"method,omitempty"` Method string `json:"method,omitempty"`
} }
type RejectMethod string type RejectActionOptions _RejectActionOptions
func (m *RejectMethod) UnmarshalJSON(bytes []byte) error { func (r *RejectActionOptions) UnmarshalJSON(bytes []byte) error {
err := json.Unmarshal(bytes, (*string)(m)) err := json.Unmarshal(bytes, (*_RejectActionOptions)(r))
if err != nil { if err != nil {
return err return err
} }
switch *m { switch r.Method {
case C.RuleActionRejectMethodDefault, C.RuleActionRejectMethodPortUnreachable, C.RuleActionRejectMethodDrop: case "", C.RuleActionRejectMethodDefault:
return nil r.Method = C.RuleActionRejectMethodDefault
case C.RuleActionRejectMethodReset,
C.RuleActionRejectMethodNetworkUnreachable,
C.RuleActionRejectMethodHostUnreachable,
C.RuleActionRejectMethodPortUnreachable,
C.RuleActionRejectMethodDrop:
default: default:
return E.New("unknown reject method: " + *m) return E.New("unknown reject method: " + r.Method)
} }
return nil
} }
type RouteActionSniff struct { type RouteActionSniff struct {

View file

@ -21,7 +21,6 @@ import (
"github.com/sagernet/sing-box/route/rule" "github.com/sagernet/sing-box/route/rule"
"github.com/sagernet/sing-dns" "github.com/sagernet/sing-dns"
"github.com/sagernet/sing-mux" "github.com/sagernet/sing-mux"
"github.com/sagernet/sing-tun"
"github.com/sagernet/sing-vmess" "github.com/sagernet/sing-vmess"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf" "github.com/sagernet/sing/common/buf"
@ -89,7 +88,7 @@ func (r *Router) routeConnection(ctx context.Context, conn net.Conn, metadata ad
if deadline.NeedAdditionalReadDeadline(conn) { if deadline.NeedAdditionalReadDeadline(conn) {
conn = deadline.NewConn(conn) conn = deadline.NewConn(conn)
} }
selectedRule, _, buffers, err := r.matchRule(ctx, &metadata, conn, nil, -1) selectedRule, _, buffers, err := r.matchRule(ctx, &metadata, false, conn, nil, -1)
if err != nil { if err != nil {
return err return err
} }
@ -108,16 +107,7 @@ func (r *Router) routeConnection(ctx context.Context, conn net.Conn, metadata ad
selectReturn = true selectReturn = true
case *rule.RuleActionReject: case *rule.RuleActionReject:
buf.ReleaseMulti(buffers) buf.ReleaseMulti(buffers)
var rejectErr error N.CloseOnHandshakeFailure(conn, onClose, action.Error())
switch action.Method {
case C.RuleActionRejectMethodDefault:
rejectErr = os.ErrClosed
case C.RuleActionRejectMethodPortUnreachable:
rejectErr = syscall.ECONNREFUSED
case C.RuleActionRejectMethodDrop:
rejectErr = tun.ErrDrop
}
N.CloseOnHandshakeFailure(conn, onClose, rejectErr)
return nil return nil
} }
} }
@ -236,7 +226,7 @@ func (r *Router) routePacketConnection(ctx context.Context, conn N.PacketConn, m
conn = deadline.NewPacketConn(bufio.NewNetPacketConn(conn)) conn = deadline.NewPacketConn(bufio.NewNetPacketConn(conn))
}*/ }*/
selectedRule, _, buffers, err := r.matchRule(ctx, &metadata, nil, conn, -1) selectedRule, _, buffers, err := r.matchRule(ctx, &metadata, false, nil, conn, -1)
if err != nil { if err != nil {
return err return err
} }
@ -306,8 +296,23 @@ func (r *Router) routePacketConnection(ctx context.Context, conn N.PacketConn, m
return nil return nil
} }
func (r *Router) PreMatch(metadata adapter.InboundContext) error {
selectedRule, _, _, err := r.matchRule(r.ctx, &metadata, true, nil, nil, -1)
if err != nil {
return err
}
if selectedRule == nil {
return nil
}
rejectAction, isReject := selectedRule.Action().(*rule.RuleActionReject)
if !isReject {
return nil
}
return rejectAction.Error()
}
func (r *Router) matchRule( func (r *Router) matchRule(
ctx context.Context, metadata *adapter.InboundContext, ctx context.Context, metadata *adapter.InboundContext, preMatch bool,
inputConn net.Conn, inputPacketConn N.PacketConn, ruleIndex int, inputConn net.Conn, inputPacketConn N.PacketConn, ruleIndex int,
) (selectedRule adapter.Rule, selectedRuleIndex int, buffers []*buf.Buffer, fatalErr error) { ) (selectedRule adapter.Rule, selectedRuleIndex int, buffers []*buf.Buffer, fatalErr error) {
if r.processSearcher != nil && metadata.ProcessInfo == nil { if r.processSearcher != nil && metadata.ProcessInfo == nil {
@ -370,7 +375,7 @@ func (r *Router) matchRule(
//nolint:staticcheck //nolint:staticcheck
if metadata.InboundOptions != common.DefaultValue[option.InboundOptions]() { if metadata.InboundOptions != common.DefaultValue[option.InboundOptions]() {
if metadata.InboundOptions.SniffEnabled { if !preMatch && metadata.InboundOptions.SniffEnabled {
newBuffers, newErr := r.actionSniff(ctx, metadata, &rule.RuleActionSniff{ newBuffers, newErr := r.actionSniff(ctx, metadata, &rule.RuleActionSniff{
OverrideDestination: metadata.InboundOptions.SniffOverrideDestination, OverrideDestination: metadata.InboundOptions.SniffOverrideDestination,
Timeout: time.Duration(metadata.InboundOptions.SniffTimeout), Timeout: time.Duration(metadata.InboundOptions.SniffTimeout),
@ -415,15 +420,28 @@ match:
if !matched { if !matched {
break break
} }
if !preMatch {
r.logger.DebugContext(ctx, "match[", currentRuleIndex, "] ", currentRule, " => ", currentRule.Action()) r.logger.DebugContext(ctx, "match[", currentRuleIndex, "] ", currentRule, " => ", currentRule.Action())
} else {
switch currentRule.Action().Type() {
case C.RuleActionTypeReject, C.RuleActionTypeResolve:
r.logger.DebugContext(ctx, "pre-match[", currentRuleIndex, "] ", currentRule, " => ", currentRule.Action())
}
}
switch action := currentRule.Action().(type) { switch action := currentRule.Action().(type) {
case *rule.RuleActionSniff: case *rule.RuleActionSniff:
if !preMatch {
newBuffers, newErr := r.actionSniff(ctx, metadata, action, inputConn, inputPacketConn) newBuffers, newErr := r.actionSniff(ctx, metadata, action, inputConn, inputPacketConn)
if newErr != nil { if newErr != nil {
fatalErr = newErr fatalErr = newErr
return return
} }
buffers = append(buffers, newBuffers...) buffers = append(buffers, newBuffers...)
} else {
selectedRule = currentRule
selectedRuleIndex = currentRuleIndex
break match
}
case *rule.RuleActionResolve: case *rule.RuleActionResolve:
fatalErr = r.actionResolve(ctx, metadata, action) fatalErr = r.actionResolve(ctx, metadata, action)
if fatalErr != nil { if fatalErr != nil {
@ -436,7 +454,7 @@ match:
} }
ruleIndex = currentRuleIndex ruleIndex = currentRuleIndex
} }
if metadata.Destination.Addr.IsUnspecified() { if !preMatch && metadata.Destination.Addr.IsUnspecified() {
newBuffers, newErr := r.actionSniff(ctx, metadata, &rule.RuleActionSniff{}, inputConn, inputPacketConn) newBuffers, newErr := r.actionSniff(ctx, metadata, &rule.RuleActionSniff{}, inputConn, inputPacketConn)
if newErr != nil { if newErr != nil {
fatalErr = newErr fatalErr = newErr

View file

@ -2,7 +2,9 @@ package rule
import ( import (
"net/netip" "net/netip"
"os"
"strings" "strings"
"syscall"
"time" "time"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
@ -10,6 +12,7 @@ import (
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-dns" "github.com/sagernet/sing-dns"
"github.com/sagernet/sing-tun"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format" F "github.com/sagernet/sing/common/format"
) )
@ -22,10 +25,10 @@ func NewRuleAction(action option.RuleAction) (adapter.RuleAction, error) {
UDPDisableDomainUnmapping: action.RouteOptions.UDPDisableDomainUnmapping, UDPDisableDomainUnmapping: action.RouteOptions.UDPDisableDomainUnmapping,
}, nil }, nil
case C.RuleActionTypeReturn: case C.RuleActionTypeReturn:
return &RuleActionReject{}, nil return &RuleActionReturn{}, nil
case C.RuleActionTypeReject: case C.RuleActionTypeReject:
return &RuleActionReject{ return &RuleActionReject{
Method: string(action.RejectOptions.Method), Method: action.RejectOptions.Method,
}, nil }, nil
case C.RuleActionTypeHijackDNS: case C.RuleActionTypeHijackDNS:
return &RuleActionHijackDNS{}, nil return &RuleActionHijackDNS{}, nil
@ -58,7 +61,7 @@ func NewDNSRuleAction(action option.DNSRuleAction) adapter.RuleAction {
return &RuleActionReturn{} return &RuleActionReturn{}
case C.RuleActionTypeReject: case C.RuleActionTypeReject:
return &RuleActionReject{ return &RuleActionReject{
Method: string(action.RejectOptions.Method), Method: action.RejectOptions.Method,
} }
default: default:
panic(F.ToString("unknown rule action: ", action.Action)) panic(F.ToString("unknown rule action: ", action.Action))
@ -118,6 +121,23 @@ func (r *RuleActionReject) String() string {
return F.ToString("reject(", r.Method, ")") return F.ToString("reject(", r.Method, ")")
} }
func (r *RuleActionReject) Error() error {
switch r.Method {
case C.RuleActionRejectMethodReset:
return os.ErrClosed
case C.RuleActionRejectMethodNetworkUnreachable:
return syscall.ENETUNREACH
case C.RuleActionRejectMethodHostUnreachable:
return syscall.EHOSTUNREACH
case C.RuleActionRejectMethodDefault, C.RuleActionRejectMethodPortUnreachable:
return syscall.ECONNREFUSED
case C.RuleActionRejectMethodDrop:
return tun.ErrDrop
default:
panic(F.ToString("unknown reject method: ", r.Method))
}
}
type RuleActionHijackDNS struct{} type RuleActionHijackDNS struct{}
func (r *RuleActionHijackDNS) Type() string { func (r *RuleActionHijackDNS) Type() string {