Skip to content

Commit

Permalink
Fix DNS servers with same tag wrongly merged
Browse files Browse the repository at this point in the history
  • Loading branch information
Vigilans committed May 7, 2024
1 parent 8191faa commit 26ea22a
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 14 deletions.
104 changes: 103 additions & 1 deletion infra/conf/merge/merge_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func TestMergeTag(t *testing.T) {
"outboundTag": "out-2"
}]
}
}
}
`
expected := `
{
Expand Down Expand Up @@ -194,6 +194,108 @@ func TestMergeTagDeep(t *testing.T) {
assertResult(t, m, expected)
}

func TestNoMergeDnsServers(t *testing.T) {
json1 := `
{
"dns": {
"queryStrategy": "UseIPv4",
"fallbackStrategy": "disabled-if-any-match",
"domainMatcher": "mph",
"servers": [
{
"address": "aaa.bbb.ccc.ddd",
"port": 53,
"domains": [
"geosite:cn"
],
"tag": "dns-domestic"
},
{
"address": "114.114.114.114",
"port": 53,
"domains": [
"geosite:cn"
],
"tag": "dns-domestic"
},
{
"address": "https://1.1.1.1/dns-query",
"tag": "dns-international"
}
]
},
"routing": {
"domainStrategy": "IPIfNonMatch",
"domainMatcher": "mph",
"rules": [
{
"type": "field",
"inboundTag": "dns-domestic",
"outboundTag": "direct"
},
{
"type": "field",
"inboundTag": "dns-international",
"outboundTag": "proxy"
}
]
}
}
`
expected := `
{
"dns": {
"queryStrategy": "UseIPv4",
"fallbackStrategy": "disabled-if-any-match",
"domainMatcher": "mph",
"servers": [
{
"address": "aaa.bbb.ccc.ddd",
"port": 53,
"domains": [
"geosite:cn"
],
"tag": "dns-domestic"
},
{
"address": "114.114.114.114",
"port": 53,
"domains": [
"geosite:cn"
],
"tag": "dns-domestic"
},
{
"address": "https://1.1.1.1/dns-query",
"tag": "dns-international"
}
]
},
"routing": {
"domainStrategy": "IPIfNonMatch",
"domainMatcher": "mph",
"rules": [
{
"type": "field",
"inboundTag": "dns-domestic",
"outboundTag": "direct"
},
{
"type": "field",
"inboundTag": "dns-international",
"outboundTag": "proxy"
}
]
}
}
`
m, err := merge.JSONs([][]byte{[]byte(json1)})
if err != nil {
t.Error(err)
}
assertResult(t, m, expected)
}

func assertResult(t *testing.T, value []byte, expected string) {
v := make(map[string]interface{})
err := serial.DecodeJSON(bytes.NewReader(value), &v)
Expand Down
14 changes: 8 additions & 6 deletions infra/conf/merge/rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@

package merge

import "fmt"

const (
priorityKey string = "_priority"
tagKey string = "_tag"
)

// ApplyRules applies merge rules according to _tag, _priority fields, and remove them
func ApplyRules(m map[string]interface{}) error {
err := sortMergeSlices(m)
err := sortMergeSlices(m, "")
if err != nil {
return err
}
Expand All @@ -20,22 +22,22 @@ func ApplyRules(m map[string]interface{}) error {
}

// sortMergeSlices enumerates all slices in a map, to sort by priority and merge by tag
func sortMergeSlices(target map[string]interface{}) error {
func sortMergeSlices(target map[string]interface{}, path string) error {
for key, value := range target {
if slice, ok := value.([]interface{}); ok {
sortByPriority(slice)
s, err := mergeSameTag(slice)
s, err := mergeSameTag(slice, fmt.Sprintf("%s.%s", path, key))
if err != nil {
return err
}
target[key] = s
for _, item := range s {
for i, item := range s {
if m, ok := item.(map[string]interface{}); ok {
sortMergeSlices(m)
sortMergeSlices(m, fmt.Sprintf("%s.%s.%d", path, key, i))
}
}
} else if field, ok := value.(map[string]interface{}); ok {
sortMergeSlices(field)
sortMergeSlices(field, fmt.Sprintf("%s.%s", path, key))
}
}
return nil
Expand Down
25 changes: 18 additions & 7 deletions infra/conf/merge/tag.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,16 @@

package merge

func getTag(v map[string]interface{}) string {
if field, ok := v["tag"]; ok {
if t, ok := field.(string); ok {
return t
import (
"strings"
)

func getTag(v map[string]interface{}, tagKeyOnly bool) string {
if !tagKeyOnly {
if field, ok := v["tag"]; ok {
if t, ok := field.(string); ok {
return t
}
}
}
if field, ok := v[tagKey]; ok {
Expand All @@ -18,16 +24,21 @@ func getTag(v map[string]interface{}) string {
return ""
}

func mergeSameTag(s []interface{}) ([]interface{}, error) {
func mergeSameTag(s []interface{}, path string) ([]interface{}, error) {
// from: [a,"",b,"",a,"",b,""]
// to: [a,"",b,"",merged,"",merged,""]
merged := &struct{}{}
tagKeyOnly := false
// See https://github.com/v2fly/v2ray-core/issues/2981
if strings.HasPrefix(path, ".dns.servers") {
tagKeyOnly = true
}
for i, item1 := range s {
map1, ok := item1.(map[string]interface{})
if !ok {
continue
}
tag1 := getTag(map1)
tag1 := getTag(map1, tagKeyOnly)
if tag1 == "" {
continue
}
Expand All @@ -36,7 +47,7 @@ func mergeSameTag(s []interface{}) ([]interface{}, error) {
if !ok {
continue
}
tag2 := getTag(map2)
tag2 := getTag(map2, tagKeyOnly)
if tag1 == tag2 {
s[j] = merged
err := mergeMaps(map1, map2)
Expand Down

0 comments on commit 26ea22a

Please sign in to comment.