xray_setting_routing_sync.go 7.0 KB

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