mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-11 17:47:20 +10:00
dns: reject evaluate fakeip servers
This commit is contained in:
@@ -272,6 +272,10 @@ func (r *Router) buildRules(startRules bool) ([]adapter.DNSRule, bool, dnsRuleMo
|
||||
return nil, false, dnsRuleModeFlags{}, err
|
||||
}
|
||||
}
|
||||
err = validateEvaluateFakeIPRules(r.rawRules, r.transport)
|
||||
if err != nil {
|
||||
return nil, false, dnsRuleModeFlags{}, err
|
||||
}
|
||||
newRules := make([]adapter.DNSRule, 0, len(r.rawRules))
|
||||
for i, ruleOptions := range r.rawRules {
|
||||
var dnsRule adapter.DNSRule
|
||||
@@ -1078,6 +1082,27 @@ func validateLegacyDNSModeDisabledRules(rules []option.DNSRule) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateEvaluateFakeIPRules(rules []option.DNSRule, transportManager adapter.DNSTransportManager) error {
|
||||
if transportManager == nil {
|
||||
return nil
|
||||
}
|
||||
for i, rule := range rules {
|
||||
if dnsRuleActionType(rule) != C.RuleActionTypeEvaluate {
|
||||
continue
|
||||
}
|
||||
server := dnsRuleActionServer(rule)
|
||||
if server == "" {
|
||||
continue
|
||||
}
|
||||
transport, loaded := transportManager.Transport(server)
|
||||
if !loaded || transport.Type() != C.DNSTypeFakeIP {
|
||||
continue
|
||||
}
|
||||
return E.New("dns rule[", i, "]: evaluate action cannot use fakeip server: ", server)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateLegacyDNSModeDisabledRuleTree(rule option.DNSRule) (bool, error) {
|
||||
switch rule.Type {
|
||||
case "", C.RuleTypeDefault:
|
||||
@@ -1146,3 +1171,14 @@ func dnsRuleActionType(rule option.DNSRule) string {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func dnsRuleActionServer(rule option.DNSRule) string {
|
||||
switch rule.Type {
|
||||
case "", C.RuleTypeDefault:
|
||||
return rule.DefaultOptions.RouteOptions.Server
|
||||
case C.RuleTypeLogical:
|
||||
return rule.LogicalOptions.RouteOptions.Server
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2022,84 +2022,39 @@ func TestLookupLegacyDNSModeDisabledSkipsFakeIPRule(t *testing.T) {
|
||||
require.Equal(t, []netip.Addr{netip.MustParseAddr("2.2.2.2")}, addresses)
|
||||
}
|
||||
|
||||
func TestLookupLegacyDNSModeDisabledEvaluateSkipFakeIPPreservesResponse(t *testing.T) {
|
||||
func TestExchangeLegacyDNSModeDisabledAllowsRouteFakeIPRule(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
defaultTransport := &fakeDNSTransport{tag: "default", transportType: C.DNSTypeUDP}
|
||||
router := newTestRouter(t, []option.DNSRule{
|
||||
{
|
||||
Type: C.RuleTypeDefault,
|
||||
DefaultOptions: option.DefaultDNSRule{
|
||||
RawDefaultDNSRule: option.RawDefaultDNSRule{
|
||||
Domain: badoption.Listable[string]{"example.com"},
|
||||
},
|
||||
DNSRuleAction: option.DNSRuleAction{
|
||||
Action: C.RuleActionTypeEvaluate,
|
||||
RouteOptions: option.DNSRouteActionOptions{Server: "upstream"},
|
||||
},
|
||||
fakeTransport := &fakeDNSTransport{tag: "fake", transportType: C.DNSTypeFakeIP}
|
||||
router := newTestRouter(t, []option.DNSRule{{
|
||||
Type: C.RuleTypeDefault,
|
||||
DefaultOptions: option.DefaultDNSRule{
|
||||
RawDefaultDNSRule: option.RawDefaultDNSRule{
|
||||
Domain: badoption.Listable[string]{"example.com"},
|
||||
},
|
||||
DNSRuleAction: option.DNSRuleAction{
|
||||
Action: C.RuleActionTypeRoute,
|
||||
RouteOptions: option.DNSRouteActionOptions{Server: "fake"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: C.RuleTypeDefault,
|
||||
DefaultOptions: option.DefaultDNSRule{
|
||||
RawDefaultDNSRule: option.RawDefaultDNSRule{
|
||||
Domain: badoption.Listable[string]{"example.com"},
|
||||
},
|
||||
DNSRuleAction: option.DNSRuleAction{
|
||||
Action: C.RuleActionTypeEvaluate,
|
||||
RouteOptions: option.DNSRouteActionOptions{Server: "fake"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: C.RuleTypeDefault,
|
||||
DefaultOptions: option.DefaultDNSRule{
|
||||
RawDefaultDNSRule: option.RawDefaultDNSRule{
|
||||
MatchResponse: true,
|
||||
ResponseAnswer: badoption.Listable[option.DNSRecordOptions]{mustRecord(t, "example.com. IN A 1.1.1.1")},
|
||||
},
|
||||
DNSRuleAction: option.DNSRuleAction{
|
||||
Action: C.RuleActionTypeRoute,
|
||||
RouteOptions: option.DNSRouteActionOptions{Server: "selected"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, &fakeDNSTransportManager{
|
||||
defaultTransport: defaultTransport,
|
||||
}}, &fakeDNSTransportManager{
|
||||
defaultTransport: &fakeDNSTransport{tag: "default", transportType: C.DNSTypeUDP},
|
||||
transports: map[string]adapter.DNSTransport{
|
||||
"default": defaultTransport,
|
||||
"upstream": &fakeDNSTransport{tag: "upstream", transportType: C.DNSTypeUDP},
|
||||
"fake": &fakeDNSTransport{tag: "fake", transportType: C.DNSTypeFakeIP},
|
||||
"selected": &fakeDNSTransport{tag: "selected", transportType: C.DNSTypeUDP},
|
||||
"default": &fakeDNSTransport{tag: "default", transportType: C.DNSTypeUDP},
|
||||
"fake": fakeTransport,
|
||||
},
|
||||
}, &fakeDNSClient{
|
||||
exchange: func(transport adapter.DNSTransport, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||
switch transport.Tag() {
|
||||
case "upstream":
|
||||
if message.Question[0].Qtype == mDNS.TypeA {
|
||||
return FixedResponse(0, message.Question[0], []netip.Addr{netip.MustParseAddr("1.1.1.1")}, 60), nil
|
||||
}
|
||||
return FixedResponse(0, message.Question[0], nil, 60), nil
|
||||
case "selected":
|
||||
if message.Question[0].Qtype == mDNS.TypeA {
|
||||
return FixedResponse(0, message.Question[0], []netip.Addr{netip.MustParseAddr("2.2.2.2")}, 60), nil
|
||||
}
|
||||
return FixedResponse(0, message.Question[0], nil, 60), nil
|
||||
case "default":
|
||||
if message.Question[0].Qtype == mDNS.TypeA {
|
||||
return FixedResponse(0, message.Question[0], []netip.Addr{netip.MustParseAddr("4.4.4.4")}, 60), nil
|
||||
}
|
||||
return FixedResponse(0, message.Question[0], nil, 60), nil
|
||||
default:
|
||||
return nil, E.New("unexpected transport")
|
||||
}
|
||||
require.Same(t, fakeTransport, transport)
|
||||
return FixedResponse(0, message.Question[0], []netip.Addr{netip.MustParseAddr("198.18.0.1")}, 60), nil
|
||||
},
|
||||
})
|
||||
router.currentRules.Load().legacyDNSMode = false
|
||||
|
||||
addresses, err := router.Lookup(context.Background(), "example.com", adapter.DNSQueryOptions{})
|
||||
response, err := router.Exchange(context.Background(), &mDNS.Msg{
|
||||
Question: []mDNS.Question{fixedQuestion("example.com", mDNS.TypeA)},
|
||||
}, adapter.DNSQueryOptions{})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, []netip.Addr{netip.MustParseAddr("2.2.2.2")}, addresses)
|
||||
require.Equal(t, []netip.Addr{netip.MustParseAddr("198.18.0.1")}, MessageToAddresses(response))
|
||||
}
|
||||
|
||||
func TestInitializeRejectsDNSRuleStrategyWhenLegacyDNSModeIsDisabledByEvaluate(t *testing.T) {
|
||||
@@ -2133,6 +2088,70 @@ func TestInitializeRejectsDNSRuleStrategyWhenLegacyDNSModeIsDisabledByEvaluate(t
|
||||
require.ErrorContains(t, err, "deprecated")
|
||||
}
|
||||
|
||||
func TestInitializeRejectsEvaluateFakeIPServerInDefaultRule(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
router := &Router{
|
||||
ctx: context.Background(),
|
||||
logger: log.NewNOPFactory().NewLogger("dns"),
|
||||
transport: &fakeDNSTransportManager{transports: map[string]adapter.DNSTransport{"fake": &fakeDNSTransport{tag: "fake", transportType: C.DNSTypeFakeIP}}},
|
||||
client: &fakeDNSClient{},
|
||||
rawRules: make([]option.DNSRule, 0, 1),
|
||||
defaultDomainStrategy: C.DomainStrategyAsIS,
|
||||
}
|
||||
router.currentRules.Store(newRulesSnapshot(make([]adapter.DNSRule, 0, 1), false))
|
||||
err := router.Initialize([]option.DNSRule{{
|
||||
Type: C.RuleTypeDefault,
|
||||
DefaultOptions: option.DefaultDNSRule{
|
||||
RawDefaultDNSRule: option.RawDefaultDNSRule{
|
||||
Domain: badoption.Listable[string]{"example.com"},
|
||||
},
|
||||
DNSRuleAction: option.DNSRuleAction{
|
||||
Action: C.RuleActionTypeEvaluate,
|
||||
RouteOptions: option.DNSRouteActionOptions{Server: "fake"},
|
||||
},
|
||||
},
|
||||
}})
|
||||
require.ErrorContains(t, err, "evaluate action cannot use fakeip server")
|
||||
require.ErrorContains(t, err, "fake")
|
||||
}
|
||||
|
||||
func TestInitializeRejectsEvaluateFakeIPServerInLogicalRule(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
router := &Router{
|
||||
ctx: context.Background(),
|
||||
logger: log.NewNOPFactory().NewLogger("dns"),
|
||||
transport: &fakeDNSTransportManager{transports: map[string]adapter.DNSTransport{"fake": &fakeDNSTransport{tag: "fake", transportType: C.DNSTypeFakeIP}}},
|
||||
client: &fakeDNSClient{},
|
||||
rawRules: make([]option.DNSRule, 0, 1),
|
||||
defaultDomainStrategy: C.DomainStrategyAsIS,
|
||||
}
|
||||
router.currentRules.Store(newRulesSnapshot(make([]adapter.DNSRule, 0, 1), false))
|
||||
err := router.Initialize([]option.DNSRule{{
|
||||
Type: C.RuleTypeLogical,
|
||||
LogicalOptions: option.LogicalDNSRule{
|
||||
RawLogicalDNSRule: option.RawLogicalDNSRule{
|
||||
Mode: C.LogicalTypeOr,
|
||||
Rules: []option.DNSRule{{
|
||||
Type: C.RuleTypeDefault,
|
||||
DefaultOptions: option.DefaultDNSRule{
|
||||
RawDefaultDNSRule: option.RawDefaultDNSRule{
|
||||
Domain: badoption.Listable[string]{"example.com"},
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
DNSRuleAction: option.DNSRuleAction{
|
||||
Action: C.RuleActionTypeEvaluate,
|
||||
RouteOptions: option.DNSRouteActionOptions{Server: "fake"},
|
||||
},
|
||||
},
|
||||
}})
|
||||
require.ErrorContains(t, err, "evaluate action cannot use fakeip server")
|
||||
require.ErrorContains(t, err, "fake")
|
||||
}
|
||||
|
||||
func TestInitializeRejectsDNSRuleStrategyWhenLegacyDNSModeIsDisabledByMatchResponse(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user