mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-14 04:38:28 +10:00
Legacy DNS address-filter mode still accepts destination-side IP predicates with a deprecation warning, but the recent evaluate/ match_response refactor started evaluating those predicates during pre-response Match(). That broke rules whose transport selection must be deferred until MatchAddressLimit() can inspect the upstream reply. Restore the old defer behavior by reintroducing an internal IgnoreDestinationIPCIDRMatch flag on InboundContext and using it only for legacy pre-response DNS matching. Default and logical DNS rules now carry the legacy mode bit, set the ignore flag on metadata copies while performing pre-response Match(), and explicitly clear it again for match_response and MatchAddressLimit() so response-phase matching still checks the returned addresses. Add regression coverage for direct legacy destination-IP rules, rule_set-backed CIDR rules, logical wrappers, and the legacy Lookup router path, including fallback after a rejected response. This keeps legacy configs working without changing new-mode evaluate semantics. Tests: go test ./route/rule ./dns Tests: make
186 lines
4.7 KiB
Go
186 lines
4.7 KiB
Go
package adapter
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
"net/netip"
|
|
"strings"
|
|
"time"
|
|
|
|
C "github.com/sagernet/sing-box/constant"
|
|
"github.com/sagernet/sing-box/log"
|
|
"github.com/sagernet/sing-box/option"
|
|
"github.com/sagernet/sing/common"
|
|
M "github.com/sagernet/sing/common/metadata"
|
|
|
|
"github.com/miekg/dns"
|
|
)
|
|
|
|
type Inbound interface {
|
|
Lifecycle
|
|
Type() string
|
|
Tag() string
|
|
}
|
|
|
|
type TCPInjectableInbound interface {
|
|
Inbound
|
|
ConnectionHandlerEx
|
|
}
|
|
|
|
type UDPInjectableInbound interface {
|
|
Inbound
|
|
PacketConnectionHandlerEx
|
|
}
|
|
|
|
type InboundRegistry interface {
|
|
option.InboundOptionsRegistry
|
|
Create(ctx context.Context, router Router, logger log.ContextLogger, tag string, inboundType string, options any) (Inbound, error)
|
|
}
|
|
|
|
type InboundManager interface {
|
|
Lifecycle
|
|
Inbounds() []Inbound
|
|
Get(tag string) (Inbound, bool)
|
|
Remove(tag string) error
|
|
Create(ctx context.Context, router Router, logger log.ContextLogger, tag string, inboundType string, options any) error
|
|
}
|
|
|
|
type InboundContext struct {
|
|
Inbound string
|
|
InboundType string
|
|
IPVersion uint8
|
|
Network string
|
|
Source M.Socksaddr
|
|
Destination M.Socksaddr
|
|
User string
|
|
Outbound string
|
|
|
|
// sniffer
|
|
|
|
Protocol string
|
|
Domain string
|
|
Client string
|
|
SniffContext any
|
|
SnifferNames []string
|
|
SniffError error
|
|
|
|
// cache
|
|
|
|
// Deprecated: implement in rule action
|
|
InboundDetour string
|
|
LastInbound string
|
|
OriginDestination M.Socksaddr
|
|
RouteOriginalDestination M.Socksaddr
|
|
UDPDisableDomainUnmapping bool
|
|
UDPConnect bool
|
|
UDPTimeout time.Duration
|
|
TLSFragment bool
|
|
TLSFragmentFallbackDelay time.Duration
|
|
TLSRecordFragment bool
|
|
|
|
NetworkStrategy *C.NetworkStrategy
|
|
NetworkType []C.InterfaceType
|
|
FallbackNetworkType []C.InterfaceType
|
|
FallbackDelay time.Duration
|
|
|
|
DestinationAddresses []netip.Addr
|
|
DNSResponse *dns.Msg
|
|
DestinationAddressMatchFromResponse bool
|
|
SourceGeoIPCode string
|
|
GeoIPCode string
|
|
ProcessInfo *ConnectionOwner
|
|
SourceMACAddress net.HardwareAddr
|
|
SourceHostname string
|
|
QueryType uint16
|
|
FakeIP bool
|
|
|
|
// rule cache
|
|
|
|
IPCIDRMatchSource bool
|
|
IPCIDRAcceptEmpty bool
|
|
|
|
SourceAddressMatch bool
|
|
SourcePortMatch bool
|
|
DestinationAddressMatch bool
|
|
DestinationPortMatch bool
|
|
DidMatch bool
|
|
IgnoreDestinationIPCIDRMatch bool
|
|
}
|
|
|
|
func (c *InboundContext) ResetRuleCache() {
|
|
c.IPCIDRMatchSource = false
|
|
c.IPCIDRAcceptEmpty = false
|
|
c.ResetRuleMatchCache()
|
|
}
|
|
|
|
func (c *InboundContext) ResetRuleMatchCache() {
|
|
c.SourceAddressMatch = false
|
|
c.SourcePortMatch = false
|
|
c.DestinationAddressMatch = false
|
|
c.DestinationPortMatch = false
|
|
c.DidMatch = false
|
|
}
|
|
|
|
func (c *InboundContext) DestinationAddressesForMatch() []netip.Addr {
|
|
if c.DestinationAddressMatchFromResponse {
|
|
return DNSResponseAddresses(c.DNSResponse)
|
|
}
|
|
return c.DestinationAddresses
|
|
}
|
|
|
|
func (c *InboundContext) DNSResponseAddressesForMatch() []netip.Addr {
|
|
return DNSResponseAddresses(c.DNSResponse)
|
|
}
|
|
|
|
func DNSResponseAddresses(response *dns.Msg) []netip.Addr {
|
|
if response == nil || response.Rcode != dns.RcodeSuccess {
|
|
return nil
|
|
}
|
|
addresses := make([]netip.Addr, 0, len(response.Answer))
|
|
for _, rawRecord := range response.Answer {
|
|
switch record := rawRecord.(type) {
|
|
case *dns.A:
|
|
addresses = append(addresses, M.AddrFromIP(record.A))
|
|
case *dns.AAAA:
|
|
addresses = append(addresses, M.AddrFromIP(record.AAAA))
|
|
case *dns.HTTPS:
|
|
for _, value := range record.SVCB.Value {
|
|
if value.Key() == dns.SVCB_IPV4HINT || value.Key() == dns.SVCB_IPV6HINT {
|
|
addresses = append(addresses, common.Map(strings.Split(value.String(), ","), M.ParseAddr)...)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return addresses
|
|
}
|
|
|
|
type inboundContextKey struct{}
|
|
|
|
func WithContext(ctx context.Context, inboundContext *InboundContext) context.Context {
|
|
return context.WithValue(ctx, (*inboundContextKey)(nil), inboundContext)
|
|
}
|
|
|
|
func ContextFrom(ctx context.Context) *InboundContext {
|
|
metadata := ctx.Value((*inboundContextKey)(nil))
|
|
if metadata == nil {
|
|
return nil
|
|
}
|
|
return metadata.(*InboundContext)
|
|
}
|
|
|
|
func ExtendContext(ctx context.Context) (context.Context, *InboundContext) {
|
|
var newMetadata InboundContext
|
|
if metadata := ContextFrom(ctx); metadata != nil {
|
|
newMetadata = *metadata
|
|
}
|
|
return WithContext(ctx, &newMetadata), &newMetadata
|
|
}
|
|
|
|
func OverrideContext(ctx context.Context) context.Context {
|
|
if metadata := ContextFrom(ctx); metadata != nil {
|
|
newMetadata := *metadata
|
|
return WithContext(ctx, &newMetadata)
|
|
}
|
|
return ctx
|
|
}
|