Implement TCP and ICMP rejects

This commit is contained in:
世界 2024-10-22 21:28:22 +08:00
parent 430fec1d49
commit 24db6e4e31
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
ConnectionRouter
PreMatch(metadata InboundContext) error
ConnectionRouterEx
GeoIPReader() *geoip.Reader

View file

@ -34,7 +34,10 @@ const (
)
const (
RuleActionRejectMethodDefault = "default"
RuleActionRejectMethodPortUnreachable = "port-unreachable"
RuleActionRejectMethodDrop = "drop"
RuleActionRejectMethodDefault = "default"
RuleActionRejectMethodReset = "reset"
RuleActionRejectMethodNetworkUnreachable = "network-unreachable"
RuleActionRejectMethodHostUnreachable = "host-unreachable"
RuleActionRejectMethodPortUnreachable = "port-unreachable"
RuleActionRejectMethodDrop = "drop"
)

View file

@ -404,9 +404,15 @@ func (t *TUN) Close() error {
)
}
func (t *TUN) PrepareConnection(source M.Socksaddr, destination M.Socksaddr) error {
// TODO: implement rejects
return nil
func (t *TUN) PrepareConnection(network string, source M.Socksaddr, destination M.Socksaddr) error {
return t.router.PreMatch(adapter.InboundContext{
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) {

View file

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

View file

@ -21,7 +21,6 @@ import (
"github.com/sagernet/sing-box/route/rule"
"github.com/sagernet/sing-dns"
"github.com/sagernet/sing-mux"
"github.com/sagernet/sing-tun"
"github.com/sagernet/sing-vmess"
"github.com/sagernet/sing/common"
"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) {
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 {
return err
}
@ -108,16 +107,7 @@ func (r *Router) routeConnection(ctx context.Context, conn net.Conn, metadata ad
selectReturn = true
case *rule.RuleActionReject:
buf.ReleaseMulti(buffers)
var rejectErr 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)
N.CloseOnHandshakeFailure(conn, onClose, action.Error())
return nil
}
}
@ -236,7 +226,7 @@ func (r *Router) routePacketConnection(ctx context.Context, conn N.PacketConn, m
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 {
return err
}
@ -306,8 +296,23 @@ func (r *Router) routePacketConnection(ctx context.Context, conn N.PacketConn, m
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(
ctx context.Context, metadata *adapter.InboundContext,
ctx context.Context, metadata *adapter.InboundContext, preMatch bool,
inputConn net.Conn, inputPacketConn N.PacketConn, ruleIndex int,
) (selectedRule adapter.Rule, selectedRuleIndex int, buffers []*buf.Buffer, fatalErr error) {
if r.processSearcher != nil && metadata.ProcessInfo == nil {
@ -370,7 +375,7 @@ func (r *Router) matchRule(
//nolint:staticcheck
if metadata.InboundOptions != common.DefaultValue[option.InboundOptions]() {
if metadata.InboundOptions.SniffEnabled {
if !preMatch && metadata.InboundOptions.SniffEnabled {
newBuffers, newErr := r.actionSniff(ctx, metadata, &rule.RuleActionSniff{
OverrideDestination: metadata.InboundOptions.SniffOverrideDestination,
Timeout: time.Duration(metadata.InboundOptions.SniffTimeout),
@ -415,15 +420,28 @@ match:
if !matched {
break
}
r.logger.DebugContext(ctx, "match[", currentRuleIndex, "] ", currentRule, " => ", currentRule.Action())
if !preMatch {
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) {
case *rule.RuleActionSniff:
newBuffers, newErr := r.actionSniff(ctx, metadata, action, inputConn, inputPacketConn)
if newErr != nil {
fatalErr = newErr
return
if !preMatch {
newBuffers, newErr := r.actionSniff(ctx, metadata, action, inputConn, inputPacketConn)
if newErr != nil {
fatalErr = newErr
return
}
buffers = append(buffers, newBuffers...)
} else {
selectedRule = currentRule
selectedRuleIndex = currentRuleIndex
break match
}
buffers = append(buffers, newBuffers...)
case *rule.RuleActionResolve:
fatalErr = r.actionResolve(ctx, metadata, action)
if fatalErr != nil {
@ -436,7 +454,7 @@ match:
}
ruleIndex = currentRuleIndex
}
if metadata.Destination.Addr.IsUnspecified() {
if !preMatch && metadata.Destination.Addr.IsUnspecified() {
newBuffers, newErr := r.actionSniff(ctx, metadata, &rule.RuleActionSniff{}, inputConn, inputPacketConn)
if newErr != nil {
fatalErr = newErr

View file

@ -2,7 +2,9 @@ package rule
import (
"net/netip"
"os"
"strings"
"syscall"
"time"
"github.com/sagernet/sing-box/adapter"
@ -10,6 +12,7 @@ import (
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-dns"
"github.com/sagernet/sing-tun"
E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format"
)
@ -22,10 +25,10 @@ func NewRuleAction(action option.RuleAction) (adapter.RuleAction, error) {
UDPDisableDomainUnmapping: action.RouteOptions.UDPDisableDomainUnmapping,
}, nil
case C.RuleActionTypeReturn:
return &RuleActionReject{}, nil
return &RuleActionReturn{}, nil
case C.RuleActionTypeReject:
return &RuleActionReject{
Method: string(action.RejectOptions.Method),
Method: action.RejectOptions.Method,
}, nil
case C.RuleActionTypeHijackDNS:
return &RuleActionHijackDNS{}, nil
@ -58,7 +61,7 @@ func NewDNSRuleAction(action option.DNSRuleAction) adapter.RuleAction {
return &RuleActionReturn{}
case C.RuleActionTypeReject:
return &RuleActionReject{
Method: string(action.RejectOptions.Method),
Method: action.RejectOptions.Method,
}
default:
panic(F.ToString("unknown rule action: ", action.Action))
@ -118,6 +121,23 @@ func (r *RuleActionReject) String() string {
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{}
func (r *RuleActionHijackDNS) Type() string {