xdns_test.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. package maskcompat
  2. import (
  3. "encoding/json"
  4. "slices"
  5. "testing"
  6. "github.com/xtls/xray-core/infra/conf"
  7. "github.com/xtls/xray-core/transport/internet/finalmask/xdns"
  8. )
  9. // coreXdnsView runs an xdns mask's settings through conf.XDNS, the loader the core
  10. // calls at startup, and returns the domain names and resolvers the core would use.
  11. func coreXdnsView(t *testing.T, settings any) (domains, resolvers []string, err error) {
  12. t.Helper()
  13. raw, err := json.Marshal(settings)
  14. if err != nil {
  15. t.Fatalf("marshal xdns settings: %v", err)
  16. }
  17. var mask conf.XDNS
  18. if err := json.Unmarshal(raw, &mask); err != nil {
  19. return nil, nil, err
  20. }
  21. built, err := mask.Build()
  22. if err != nil {
  23. return nil, nil, err
  24. }
  25. config := built.(*xdns.Config)
  26. for _, domain := range config.Domains {
  27. domains = append(domains, domain.Name)
  28. }
  29. for _, resolver := range config.Resolvers {
  30. resolvers = append(resolvers, resolver.Type+"://"+resolver.Addr)
  31. }
  32. return domains, resolvers, nil
  33. }
  34. func TestUpgradeLegacyXdns(t *testing.T) {
  35. tests := []struct {
  36. name string
  37. mask string
  38. want string
  39. wantDomains []string
  40. wantResolvers []string
  41. }{
  42. {
  43. name: "bare server domain becomes TXT with the legacy EDNS0 size",
  44. mask: `{"type":"xdns","settings":{"domains":["t.example.com"]}}`,
  45. want: `{"domains":[{"edns0":1232,"names":["t.example.com"],"types":[16]}]}`,
  46. wantDomains: []string{"t.example.com"},
  47. },
  48. {
  49. name: "method suffixes map to their record types",
  50. mask: `{"type":"xdns","settings":{"domains":["a.example.com:a","q.example.com:AAAA","t.example.com:txt"]}}`,
  51. want: `{"domains":[{"edns0":1232,"names":["a.example.com"],"types":[1]},{"edns0":1232,"names":["q.example.com"],"types":[28]},{"edns0":1232,"names":["t.example.com"],"types":[16]}]}`,
  52. wantDomains: []string{"a.example.com", "q.example.com", "t.example.com"},
  53. },
  54. {
  55. name: "client resolver splits into its domain and a udp resolver",
  56. mask: `{"type":"XDNS","settings":{"resolvers":["t.example.com:a+udp://8.8.8.8:53"]}}`,
  57. want: `{"domains":[{"edns0":1232,"names":["t.example.com"],"types":[1]}],"resolvers":[{"addrs":["udp://8.8.8.8:53"]}]}`,
  58. wantDomains: []string{"t.example.com"},
  59. wantResolvers: []string{"udp://8.8.8.8:53"},
  60. },
  61. {
  62. name: "a resolver for an already listed domain adds no duplicate",
  63. mask: `{"type":"xdns","settings":{"domains":["t.example.com"],"resolvers":["T.example.com+udp://1.1.1.1:53"]}}`,
  64. want: `{"domains":[{"edns0":1232,"names":["t.example.com"],"types":[16]}],"resolvers":[{"addrs":["udp://1.1.1.1:53"]}]}`,
  65. wantDomains: []string{"t.example.com"},
  66. wantResolvers: []string{"udp://1.1.1.1:53"},
  67. },
  68. {
  69. name: "string entries the old core refused are dropped",
  70. mask: `{"type":"xdns","settings":{"domains":["t.example.com","m.example.com:mx"],"resolvers":["1.1.1.1:53"]}}`,
  71. want: `{"domains":[{"edns0":1232,"names":["t.example.com"],"types":[16]}]}`,
  72. wantDomains: []string{"t.example.com"},
  73. },
  74. {
  75. name: "26.9.30 domain and resolver objects move to names and addrs",
  76. mask: `{"type":"xdns","settings":{"domains":[{"name":"t.example.com","types":[16,28],"edns0":1232,"lenLimit":200}],"resolvers":[{"type":"tcp","settings":{"addr":"8.8.8.8:53"}},{"type":"udp","settings":{"addr":"1.1.1.1:5353"}}],"extraPoll":1}}`,
  77. want: `{"domains":[{"edns0":1232,"lenLimit":200,"names":["t.example.com"],"types":[16,28]}],"extraPoll":1,"resolvers":[{"addrs":["tcp://8.8.8.8:53"]},{"addrs":["udp://1.1.1.1:5353"]}]}`,
  78. wantDomains: []string{"t.example.com"},
  79. wantResolvers: []string{"tcp://8.8.8.8:53", "udp://1.1.1.1:5353"},
  80. },
  81. {
  82. name: "26.9.30 resolvers the old loader refused are dropped",
  83. mask: `{"type":"xdns","settings":{"domains":[{"name":"t.example.com"}],"resolvers":[{"type":"doh","settings":{"addr":"dns.example.com"}},{"type":"udp","settings":{}},{"type":"udp","settings":{"addr":"9.9.9.9:53"}}]}}`,
  84. want: `{"domains":[{"names":["t.example.com"]}],"resolvers":[{"addrs":["udp://9.9.9.9:53"]}]}`,
  85. wantDomains: []string{"t.example.com"},
  86. wantResolvers: []string{"udp://9.9.9.9:53"},
  87. },
  88. }
  89. for _, tc := range tests {
  90. t.Run(tc.name, func(t *testing.T) {
  91. var mask map[string]any
  92. if err := json.Unmarshal([]byte(tc.mask), &mask); err != nil {
  93. t.Fatalf("unmarshal mask: %v", err)
  94. }
  95. domains, resolvers, err := coreXdnsView(t, mask["settings"])
  96. if err == nil && slices.Equal(domains, tc.wantDomains) && slices.Equal(resolvers, tc.wantResolvers) {
  97. t.Fatal("the core already serves the legacy shape as intended; the upgrade is no longer needed")
  98. }
  99. finalmask := map[string]any{"udp": []any{mask}}
  100. if !UpgradeLegacyXdns(finalmask) {
  101. t.Fatal("UpgradeLegacyXdns reported no change for a legacy mask")
  102. }
  103. got, err := json.Marshal(mask["settings"])
  104. if err != nil {
  105. t.Fatalf("marshal upgraded settings: %v", err)
  106. }
  107. if string(got) != tc.want {
  108. t.Fatalf("upgraded settings\n got: %s\nwant: %s", got, tc.want)
  109. }
  110. domains, resolvers, err = coreXdnsView(t, mask["settings"])
  111. if err != nil {
  112. t.Fatalf("the core refuses the upgraded settings: %v", err)
  113. }
  114. if !slices.Equal(domains, tc.wantDomains) || !slices.Equal(resolvers, tc.wantResolvers) {
  115. t.Fatalf("the core serves domains %v resolvers %v, want %v %v", domains, resolvers, tc.wantDomains, tc.wantResolvers)
  116. }
  117. })
  118. }
  119. }
  120. func TestUpgradeLegacyXdnsLeavesCurrentShapeAlone(t *testing.T) {
  121. const current = `{"udp":[{"type":"xdns","settings":{"domains":[{"names":["t.example.com","u.example.com"],"types":[16,28],"edns0":1232}],"resolvers":[{"addrs":["tcp://8.8.8.8:53","1.1.1.1"]}],"extraPoll":2}},{"type":"salamander","settings":{"password":"x"}}]}`
  122. var finalmask map[string]any
  123. if err := json.Unmarshal([]byte(current), &finalmask); err != nil {
  124. t.Fatalf("unmarshal finalmask: %v", err)
  125. }
  126. if UpgradeLegacyXdns(finalmask) {
  127. t.Fatal("UpgradeLegacyXdns rewrote a mask that is already in the names/addrs shape")
  128. }
  129. var want map[string]any
  130. _ = json.Unmarshal([]byte(current), &want)
  131. got, _ := json.Marshal(finalmask)
  132. wantJSON, _ := json.Marshal(want)
  133. if string(got) != string(wantJSON) {
  134. t.Fatalf("finalmask changed\n got: %s\nwant: %s", got, wantJSON)
  135. }
  136. }