1
0

xdns.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. package maskcompat
  2. import "strings"
  3. // legacyXdnsEDNS0 is the EDNS0 payload the pre-26.9.30 xdns always negotiated;
  4. // the object shape makes it opt-in and caps every answer at 512 bytes without it.
  5. const legacyXdnsEDNS0 = 1232
  6. var legacyXdnsRecordTypes = map[string]int{"": 16, "txt": 16, "a": 1, "aaaa": 28}
  7. // UpgradeLegacyXdns rewrites xdns masks from the string lists xray-core 26.9.30
  8. // (#6718) no longer parses into its object lists; one legacy mask fails the whole config.
  9. func UpgradeLegacyXdns(finalmask any) bool {
  10. fm, _ := finalmask.(map[string]any)
  11. masks, _ := fm["udp"].([]any)
  12. changed := false
  13. for _, entry := range masks {
  14. mask, _ := entry.(map[string]any)
  15. if maskType, _ := mask["type"].(string); !strings.EqualFold(maskType, "xdns") {
  16. continue
  17. }
  18. if settings, ok := mask["settings"].(map[string]any); ok && upgradeLegacyXdnsSettings(settings) {
  19. changed = true
  20. }
  21. }
  22. return changed
  23. }
  24. // upgradeLegacyXdnsSettings maps a bare name to TXT, the type legacy clients queried by
  25. // default, and drops entries the old core refused instead of keeping them to fail again.
  26. func upgradeLegacyXdnsSettings(settings map[string]any) bool {
  27. rawDomains, _ := settings["domains"].([]any)
  28. rawResolvers, _ := settings["resolvers"].([]any)
  29. if !hasLegacyXdnsEntry(rawDomains) && !hasLegacyXdnsEntry(rawResolvers) {
  30. return false
  31. }
  32. domains := make([]any, 0, len(rawDomains)+len(rawResolvers))
  33. listed := map[string]bool{}
  34. addDomain := func(domain map[string]any) {
  35. key, _ := domain["name"].(string)
  36. key = strings.ToLower(key)
  37. if key != "" && listed[key] {
  38. return
  39. }
  40. listed[key] = true
  41. domains = append(domains, domain)
  42. }
  43. for _, entry := range rawDomains {
  44. switch value := entry.(type) {
  45. case map[string]any:
  46. addDomain(value)
  47. case string:
  48. if domain, ok := legacyXdnsDomain(value); ok {
  49. addDomain(domain)
  50. }
  51. }
  52. }
  53. resolvers := make([]any, 0, len(rawResolvers))
  54. for _, entry := range rawResolvers {
  55. spec, ok := entry.(string)
  56. if !ok {
  57. resolvers = append(resolvers, entry)
  58. continue
  59. }
  60. head, addr, found := strings.Cut(spec, "+udp://")
  61. addr = strings.TrimSpace(addr)
  62. domain, valid := legacyXdnsDomain(head)
  63. if !found || addr == "" || !valid {
  64. continue
  65. }
  66. addDomain(domain)
  67. resolvers = append(resolvers, map[string]any{
  68. "type": "udp",
  69. "settings": map[string]any{"addr": addr},
  70. })
  71. }
  72. settings["domains"] = domains
  73. if len(resolvers) > 0 {
  74. settings["resolvers"] = resolvers
  75. } else {
  76. delete(settings, "resolvers")
  77. }
  78. return true
  79. }
  80. // legacyXdnsDomain parses the "name[:txt|a|aaaa]" spec both legacy lists used.
  81. func legacyXdnsDomain(spec string) (map[string]any, bool) {
  82. name, method := strings.TrimSpace(spec), ""
  83. if i := strings.LastIndex(name, ":"); i >= 0 {
  84. name, method = name[:i], strings.ToLower(name[i+1:])
  85. }
  86. name = strings.Trim(name, ".")
  87. recordType, known := legacyXdnsRecordTypes[method]
  88. if name == "" || !known {
  89. return nil, false
  90. }
  91. return map[string]any{"name": name, "types": []any{recordType}, "edns0": legacyXdnsEDNS0}, true
  92. }
  93. func hasLegacyXdnsEntry(values []any) bool {
  94. for _, value := range values {
  95. if _, ok := value.(string); ok {
  96. return true
  97. }
  98. }
  99. return false
  100. }