xdns.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. package maskcompat
  2. import (
  3. "slices"
  4. "strings"
  5. )
  6. // legacyXdnsEDNS0 is the EDNS0 payload the pre-26.9.30 xdns always negotiated;
  7. // the object shape makes it opt-in and caps every answer at 512 bytes without it.
  8. const legacyXdnsEDNS0 = 1232
  9. var legacyXdnsRecordTypes = map[string]int{"": 16, "txt": 16, "a": 1, "aaaa": 28}
  10. // UpgradeLegacyXdns rewrites xdns masks into the names/addrs lists xray-core 26.10.10
  11. // (#7090) parses: it ignores 26.9.30's name/type+settings keys and fails the string lists.
  12. func UpgradeLegacyXdns(finalmask any) bool {
  13. fm, _ := finalmask.(map[string]any)
  14. masks, _ := fm["udp"].([]any)
  15. changed := false
  16. for _, entry := range masks {
  17. mask, _ := entry.(map[string]any)
  18. if maskType, _ := mask["type"].(string); !strings.EqualFold(maskType, "xdns") {
  19. continue
  20. }
  21. if settings, ok := mask["settings"].(map[string]any); ok && upgradeLegacyXdnsSettings(settings) {
  22. changed = true
  23. }
  24. }
  25. return changed
  26. }
  27. // upgradeLegacyXdnsSettings maps a bare name to TXT, the type legacy clients queried by
  28. // default, and drops entries the old core refused instead of keeping them to fail again.
  29. func upgradeLegacyXdnsSettings(settings map[string]any) bool {
  30. rawDomains, _ := settings["domains"].([]any)
  31. rawResolvers, _ := settings["resolvers"].([]any)
  32. if !slices.ContainsFunc(rawDomains, isLegacyXdnsDomain) && !slices.ContainsFunc(rawResolvers, isLegacyXdnsResolver) {
  33. return false
  34. }
  35. domains := make([]any, 0, len(rawDomains))
  36. listed := map[string]bool{}
  37. addDomain := func(domain map[string]any) {
  38. names, _ := domain["names"].([]any)
  39. for _, name := range names {
  40. if s, ok := name.(string); ok {
  41. listed[strings.ToLower(s)] = true
  42. }
  43. }
  44. domains = append(domains, domain)
  45. }
  46. addLegacyDomain := func(domain map[string]any) {
  47. if name, _ := domain["names"].([]any)[0].(string); !listed[strings.ToLower(name)] {
  48. addDomain(domain)
  49. }
  50. }
  51. for _, entry := range rawDomains {
  52. switch value := entry.(type) {
  53. case map[string]any:
  54. addDomain(upgradeXdnsDomainObject(value))
  55. case string:
  56. if domain, ok := legacyXdnsDomain(value); ok {
  57. addLegacyDomain(domain)
  58. }
  59. }
  60. }
  61. resolvers := make([]any, 0, len(rawResolvers))
  62. for _, entry := range rawResolvers {
  63. switch value := entry.(type) {
  64. case string:
  65. head, addr, found := strings.Cut(value, "+udp://")
  66. addr = strings.TrimSpace(addr)
  67. domain, valid := legacyXdnsDomain(head)
  68. if !found || addr == "" || !valid {
  69. continue
  70. }
  71. addLegacyDomain(domain)
  72. resolvers = append(resolvers, map[string]any{"addrs": []any{"udp://" + addr}})
  73. case map[string]any:
  74. if !isLegacyXdnsResolver(value) {
  75. resolvers = append(resolvers, value)
  76. } else if addr, ok := legacyXdnsResolverAddr(value); ok {
  77. resolvers = append(resolvers, map[string]any{"addrs": []any{addr}})
  78. }
  79. default:
  80. resolvers = append(resolvers, entry)
  81. }
  82. }
  83. settings["domains"] = domains
  84. if len(resolvers) > 0 {
  85. settings["resolvers"] = resolvers
  86. } else {
  87. delete(settings, "resolvers")
  88. }
  89. return true
  90. }
  91. // upgradeXdnsDomainObject folds a 26.9.30 domain's single "name" into the "names" list.
  92. func upgradeXdnsDomainObject(domain map[string]any) map[string]any {
  93. raw, legacy := domain["name"]
  94. if !legacy {
  95. return domain
  96. }
  97. delete(domain, "name")
  98. name, _ := raw.(string)
  99. name = strings.TrimSpace(name)
  100. names, _ := domain["names"].([]any)
  101. if name != "" && !slices.Contains(names, any(name)) {
  102. names = append([]any{name}, names...)
  103. }
  104. if names == nil {
  105. names = []any{}
  106. }
  107. domain["names"] = names
  108. return domain
  109. }
  110. // legacyXdnsResolverAddr turns a 26.9.30 {type, settings:{addr}} resolver into the URL
  111. // the addrs list takes; that loader refused every type but udp and tcp.
  112. func legacyXdnsResolverAddr(resolver map[string]any) (string, bool) {
  113. kind, _ := resolver["type"].(string)
  114. kind = strings.ToLower(strings.TrimSpace(kind))
  115. settings, _ := resolver["settings"].(map[string]any)
  116. addr, _ := settings["addr"].(string)
  117. addr = strings.TrimSpace(addr)
  118. if (kind != "udp" && kind != "tcp") || addr == "" {
  119. return "", false
  120. }
  121. return kind + "://" + addr, true
  122. }
  123. // legacyXdnsDomain parses the "name[:txt|a|aaaa]" spec both legacy lists used.
  124. func legacyXdnsDomain(spec string) (map[string]any, bool) {
  125. name, method := strings.TrimSpace(spec), ""
  126. if i := strings.LastIndex(name, ":"); i >= 0 {
  127. name, method = name[:i], strings.ToLower(name[i+1:])
  128. }
  129. name = strings.Trim(name, ".")
  130. recordType, known := legacyXdnsRecordTypes[method]
  131. if name == "" || !known {
  132. return nil, false
  133. }
  134. return map[string]any{"names": []any{name}, "types": []any{recordType}, "edns0": legacyXdnsEDNS0}, true
  135. }
  136. func isLegacyXdnsDomain(value any) bool {
  137. switch domain := value.(type) {
  138. case string:
  139. return true
  140. case map[string]any:
  141. _, legacy := domain["name"]
  142. return legacy
  143. }
  144. return false
  145. }
  146. func isLegacyXdnsResolver(value any) bool {
  147. switch resolver := value.(type) {
  148. case string:
  149. return true
  150. case map[string]any:
  151. _, current := resolver["addrs"]
  152. return !current
  153. }
  154. return false
  155. }