xray_setting_routing_sync.go 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. package service
  2. import (
  3. "encoding/json"
  4. "strings"
  5. )
  6. var routingMatcherKeys = []string{
  7. "domain", "domains", "ip", "port", "sourcePort", "localPort", "network",
  8. "source", "sourceIP", "localIP", "user", "vlessRoute", "protocol", "attrs", "process",
  9. }
  10. func readInboundTags(raw any) []string {
  11. switch tags := raw.(type) {
  12. case []string:
  13. return append([]string(nil), tags...)
  14. case string:
  15. if tags == "" {
  16. return nil
  17. }
  18. return []string{tags}
  19. case []any:
  20. out := make([]string, 0, len(tags))
  21. for _, item := range tags {
  22. if s, ok := item.(string); ok && s != "" {
  23. out = append(out, s)
  24. }
  25. }
  26. return out
  27. default:
  28. return nil
  29. }
  30. }
  31. func writeInboundTags(rule map[string]any, tags []string) {
  32. if len(tags) == 0 {
  33. delete(rule, "inboundTag")
  34. return
  35. }
  36. rule["inboundTag"] = tags
  37. }
  38. func ruleHasNonInboundMatchers(rule map[string]any) bool {
  39. for _, key := range routingMatcherKeys {
  40. if hasRoutingMatcherValue(rule[key]) {
  41. return true
  42. }
  43. }
  44. return false
  45. }
  46. func hasRoutingMatcherValue(raw any) bool {
  47. switch v := raw.(type) {
  48. case nil:
  49. return false
  50. case string:
  51. return v != ""
  52. case float64, int, int64, bool:
  53. return true
  54. case []string:
  55. return len(v) > 0
  56. case []any:
  57. return len(v) > 0
  58. case map[string]any:
  59. return len(v) > 0
  60. default:
  61. return true
  62. }
  63. }
  64. func replaceInboundTagInRules(rules []map[string]any, oldTag, newTag string) bool {
  65. changed := false
  66. for _, rule := range rules {
  67. if replaceInboundTagInRule(rule, oldTag, newTag) {
  68. changed = true
  69. }
  70. }
  71. return changed
  72. }
  73. func replaceInboundTagInRule(rule map[string]any, oldTag, newTag string) bool {
  74. tags := readInboundTags(rule["inboundTag"])
  75. if len(tags) == 0 {
  76. return false
  77. }
  78. updated := false
  79. for i, tag := range tags {
  80. if tag == oldTag {
  81. tags[i] = newTag
  82. updated = true
  83. }
  84. }
  85. if updated {
  86. writeInboundTags(rule, tags)
  87. }
  88. return updated
  89. }
  90. func removeInboundTagFromRules(rules []map[string]any, deletedTag string) ([]map[string]any, bool) {
  91. if deletedTag == "" {
  92. return rules, false
  93. }
  94. changed := false
  95. out := make([]map[string]any, 0, len(rules))
  96. for _, rule := range rules {
  97. tags := readInboundTags(rule["inboundTag"])
  98. if len(tags) == 0 {
  99. out = append(out, rule)
  100. continue
  101. }
  102. nextTags := make([]string, 0, len(tags))
  103. hadDeleted := false
  104. for _, tag := range tags {
  105. if tag == deletedTag {
  106. hadDeleted = true
  107. continue
  108. }
  109. nextTags = append(nextTags, tag)
  110. }
  111. if !hadDeleted {
  112. out = append(out, rule)
  113. continue
  114. }
  115. changed = true
  116. if len(nextTags) == 0 && !ruleHasNonInboundMatchers(rule) {
  117. continue
  118. }
  119. if len(nextTags) == 0 {
  120. delete(rule, "inboundTag")
  121. } else {
  122. writeInboundTags(rule, nextTags)
  123. }
  124. out = append(out, rule)
  125. }
  126. return out, changed
  127. }
  128. func replaceInboundTagInOutbounds(outbounds []any, oldTag, newTag string) bool {
  129. changed := false
  130. for _, outIface := range outbounds {
  131. out, ok := outIface.(map[string]any)
  132. if !ok {
  133. continue
  134. }
  135. proto, _ := out["protocol"].(string)
  136. if !strings.EqualFold(proto, "loopback") {
  137. continue
  138. }
  139. settings, ok := out["settings"].(map[string]any)
  140. if !ok {
  141. continue
  142. }
  143. tag, _ := settings["inboundTag"].(string)
  144. if tag != oldTag {
  145. continue
  146. }
  147. settings["inboundTag"] = newTag
  148. changed = true
  149. }
  150. return changed
  151. }
  152. func removeInboundTagFromOutbounds(outbounds []any, deletedTag string) bool {
  153. changed := false
  154. for _, outIface := range outbounds {
  155. out, ok := outIface.(map[string]any)
  156. if !ok {
  157. continue
  158. }
  159. proto, _ := out["protocol"].(string)
  160. if !strings.EqualFold(proto, "loopback") {
  161. continue
  162. }
  163. settings, ok := out["settings"].(map[string]any)
  164. if !ok {
  165. continue
  166. }
  167. tag, _ := settings["inboundTag"].(string)
  168. if tag != deletedTag {
  169. continue
  170. }
  171. delete(settings, "inboundTag")
  172. changed = true
  173. }
  174. return changed
  175. }
  176. func mutateXrayTemplateRouting(raw string, mutate func(cfg map[string]any) bool) (string, bool, error) {
  177. raw = UnwrapXrayTemplateConfig(raw)
  178. var cfg map[string]any
  179. if err := json.Unmarshal([]byte(raw), &cfg); err != nil {
  180. return raw, false, err
  181. }
  182. if !mutate(cfg) {
  183. return raw, false, nil
  184. }
  185. out, err := json.MarshalIndent(cfg, "", " ")
  186. if err != nil {
  187. return raw, false, err
  188. }
  189. return string(out), true, nil
  190. }
  191. func routingRulesFromCfg(cfg map[string]any) []map[string]any {
  192. routing, _ := cfg["routing"].(map[string]any)
  193. if routing == nil {
  194. return nil
  195. }
  196. rawRules, ok := routing["rules"].([]any)
  197. if !ok {
  198. return nil
  199. }
  200. rules := make([]map[string]any, 0, len(rawRules))
  201. for _, item := range rawRules {
  202. rule, ok := item.(map[string]any)
  203. if !ok {
  204. continue
  205. }
  206. rules = append(rules, rule)
  207. }
  208. return rules
  209. }
  210. func setRoutingRulesInCfg(cfg map[string]any, rules []map[string]any) {
  211. routing, _ := cfg["routing"].(map[string]any)
  212. if routing == nil {
  213. routing = map[string]any{}
  214. cfg["routing"] = routing
  215. }
  216. items := make([]any, len(rules))
  217. for i, rule := range rules {
  218. items[i] = rule
  219. }
  220. routing["rules"] = items
  221. }
  222. func outboundsFromCfg(cfg map[string]any) []any {
  223. outbounds, _ := cfg["outbounds"].([]any)
  224. return outbounds
  225. }
  226. // PropagateInboundTagRename rewrites routing rules and loopback outbound
  227. // references when a panel inbound tag changes.
  228. func (s *XraySettingService) PropagateInboundTagRename(oldTag, newTag string) (bool, error) {
  229. if oldTag == "" || newTag == "" || oldTag == newTag {
  230. return false, nil
  231. }
  232. template, err := s.GetXrayConfigTemplate()
  233. if err != nil {
  234. return false, err
  235. }
  236. updated, changed, err := mutateXrayTemplateRouting(template, func(cfg map[string]any) bool {
  237. mutated := false
  238. rules := routingRulesFromCfg(cfg)
  239. if len(rules) > 0 {
  240. if replaceInboundTagInRules(rules, oldTag, newTag) {
  241. setRoutingRulesInCfg(cfg, rules)
  242. mutated = true
  243. }
  244. }
  245. outbounds := outboundsFromCfg(cfg)
  246. if len(outbounds) > 0 && replaceInboundTagInOutbounds(outbounds, oldTag, newTag) {
  247. cfg["outbounds"] = outbounds
  248. mutated = true
  249. }
  250. return mutated
  251. })
  252. if err != nil || !changed {
  253. return false, err
  254. }
  255. if err := s.SaveXraySetting(updated); err != nil {
  256. return false, err
  257. }
  258. return true, nil
  259. }
  260. // RemoveInboundTagReferences drops a deleted inbound tag from routing rules.
  261. // Rules that only matched that inbound are removed; rules with additional
  262. // matchers keep the rule and only lose the inboundTag entry.
  263. func (s *XraySettingService) RemoveInboundTagReferences(deletedTag string) (bool, error) {
  264. if deletedTag == "" {
  265. return false, nil
  266. }
  267. template, err := s.GetXrayConfigTemplate()
  268. if err != nil {
  269. return false, err
  270. }
  271. updated, changed, err := mutateXrayTemplateRouting(template, func(cfg map[string]any) bool {
  272. mutated := false
  273. rules := routingRulesFromCfg(cfg)
  274. if len(rules) > 0 {
  275. nextRules, rulesChanged := removeInboundTagFromRules(rules, deletedTag)
  276. if rulesChanged {
  277. setRoutingRulesInCfg(cfg, nextRules)
  278. mutated = true
  279. }
  280. }
  281. outbounds := outboundsFromCfg(cfg)
  282. if len(outbounds) > 0 && removeInboundTagFromOutbounds(outbounds, deletedTag) {
  283. cfg["outbounds"] = outbounds
  284. mutated = true
  285. }
  286. return mutated
  287. })
  288. if err != nil || !changed {
  289. return false, err
  290. }
  291. if err := s.SaveXraySetting(updated); err != nil {
  292. return false, err
  293. }
  294. return true, nil
  295. }